Initial commit

This commit is contained in:
Lech 2017-12-17 12:31:13 +01:00 committed by Lech Perczak
commit af6dcf2629
24 changed files with 762 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
*.config
*.creator
*.creator.*
*.files
*.includes

52
ArduinoGpio.cpp Normal file
View File

@ -0,0 +1,52 @@
#include "ArduinoGpio.h"
#include <Arduino.h>
ArduinoGpio::ArduinoGpio(int pin, bool initialValue, IGpio::Mode initialMode):
pin(pin)
{
/* There is no protection to prevent from creation of multiple instances
* of single pin, as Arduino does not support C++ exceptions.
* Only one parent object may own instance of a pin.
*/
write(initialValue);
setMode((initialMode));
}
ArduinoGpio::~ArduinoGpio()
{
setMode(Mode::Input);
write(false);
}
bool ArduinoGpio::read()
{
return (digitalRead(pin) != 0);
}
void ArduinoGpio::write(bool newValue)
{
digitalWrite(pin, newValue ? HIGH : LOW);
}
IGpio::Mode ArduinoGpio::getMode()
{
return mode;
}
void ArduinoGpio::setMode(IGpio::Mode newMode)
{
mode = newMode;
pinMode(pin, toAdruinoMode(newMode));
}
int ArduinoGpio::toAdruinoMode(IGpio::Mode mode)
{
switch(mode) {
case Mode::Input:
return INPUT;
case Mode::InputPullup:
return INPUT_PULLUP;
case Mode::Output:
return OUTPUT;
}
}

22
ArduinoGpio.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef ARDUINOGPIO_H
#define ARDUINOGPIO_H
#include "IGpio.h"
class ArduinoGpio : public IGpio
{
public:
ArduinoGpio(int pin, bool initialValue, Mode initialMode);
virtual ~ArduinoGpio();
virtual bool read();
virtual void write(bool newValue);
virtual Mode getMode();
virtual void setMode(Mode newMode);
private:
int pin;
Mode mode;
int toAdruinoMode(Mode mode);
};
#endif // ARDUINOGPIO_H

63
ArduinoSerialLogger.cpp Normal file
View File

@ -0,0 +1,63 @@
#include "ArduinoSerialLogger.h"
#include <stdarg.h>
ArduinoSerialLogger::ArduinoSerialLogger(Serial_ &hardwareSerial):
hardwareSerial(hardwareSerial)
{
hardwareSerial.begin(DEBUG_CONSOLE_BAUD);
}
ArduinoSerialLogger::~ArduinoSerialLogger()
{
hardwareSerial.end();
}
void ArduinoSerialLogger::debug(const char *format, ...) const
{
va_list args;
va_start(args, format);
formatLog("DBG/", format, args);
va_end(args);
}
void ArduinoSerialLogger::info(const char *format, ...) const
{
va_list args;
va_start(args, format);
formatLog("INF/", format, args);
va_end(args);
}
void ArduinoSerialLogger::warning(const char *format, ...) const
{
va_list args;
va_start(args, format);
formatLog("WRN/", format, args);
va_end(args);
}
void ArduinoSerialLogger::error(const char *format, ...) const
{
va_list args;
va_start(args, format);
formatLog("ERR/", format, args);
va_end(args);
}
void ArduinoSerialLogger::flush() const
{
hardwareSerial.flush();
}
void ArduinoSerialLogger::formatLog(const char *level, const char *format, va_list args) const
{
char newFormat[128];
/* This is the least crappy solution I came up with at 23:40 after a beer.
* sstream is not available on this platform.
* CAN YOU SPOT THE BUG? */
snprintf(newFormat, sizeof(newFormat), "%s%s\n", level, format);
char buffer[256];
vsnprintf(buffer, sizeof(buffer), newFormat, args);
hardwareSerial.print(buffer);
}

31
ArduinoSerialLogger.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef ARDUINOSERIALLOGGER_H
#define ARDUINOSERIALLOGGER_H
#include "ILogger.h"
#include <Arduino.h>
#define Serial_ HardwareSerial
class ArduinoSerialLogger : public ILogger
{
public:
explicit ArduinoSerialLogger(Serial_& hardwareSerial);
virtual ~ArduinoSerialLogger();
virtual void debug(const char *format, ...) const;
virtual void info(const char *format, ...) const;
virtual void warning(const char* format, ...) const;
virtual void error(const char* format, ...) const;
virtual void flush() const;
private:
enum {
DEBUG_CONSOLE_BAUD = 9600
};
void formatLog(const char* level, const char* format, va_list args) const;
Serial_& hardwareSerial;
};
#endif // ARDUINOSERIALLOGGER_H

29
DoorLock.cpp Normal file
View File

@ -0,0 +1,29 @@
#include "DoorLock.h"
#include <Arduino.h>
DoorLock::DoorLock(IGpio &gpio, DoorLock::ActiveState activeState, DoorLock::TMilliseconds openTime):
gpio(gpio),
activeState(activeState),
openTime(openTime)
{
deactivate();
gpio.setMode(IGpio::Mode::Output);
}
void DoorLock::open()
{
activate();
delay(openTime);
deactivate();
}
void DoorLock::activate()
{
gpio.write(activeState == ActiveState::High);
}
void DoorLock::deactivate()
{
gpio.write(activeState != ActiveState::High);
}

27
DoorLock.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef DOORLOCK_H
#define DOORLOCK_H
#include "IGpio.h"
class DoorLock
{
public:
enum class ActiveState {
Low,
High,
};
typedef int TMilliseconds;
DoorLock(IGpio& gpio, ActiveState activeState, TMilliseconds openTime);
void open();
private:
void activate();
void deactivate();
IGpio& gpio;
ActiveState activeState;
TMilliseconds openTime;
};
#endif // DOORLOCK_H

61
DoorLockController.cpp Normal file
View File

@ -0,0 +1,61 @@
#include "DoorLockController.h"
DoorLockController::DoorLockController() :
logger(Serial),
greenLedGpio(LED_GREEN_PIN, false, IGpio::Mode::Output),
redLedGpio(LED_RED_PIN, false, IGpio::Mode::Output),
doorLockGpio(DOOR_PIN, false, IGpio::Mode::Output),
statusLed(redLedGpio, greenLedGpio),
doorLock(doorLockGpio, DoorLock::ActiveState::Low, DOOR_OPEN_TIME_MS),
nfcAuthenticator(NFC_SLAVE_SELECT_PIN, NFC_RESET_PIN, logger),
oneWireAuthenticator(ONEWIRE_PIN, logger),
authenticators{&nfcAuthenticator, &oneWireAuthenticator},
unauthorizedAccess(false),
heartbeatCounter(0)
{
logger.info("Ready. Waiting for keys.");
}
void DoorLockController::heartbeat()
{
heartbeatCounter = (heartbeatCounter == BLINK_INTERVAL_CYCLES - 1) ? 0 : heartbeatCounter+1;
if(heartbeatCounter == 0)
statusLed.setState(unauthorizedAccess ? DualColorLed::State::Red : DualColorLed::State::Green);
}
void DoorLockController::checkForKeys()
{
for(IAuthenticator* authenticator : authenticators) {
Key key = authenticator->getKey();
if(!key.isValid())
continue;
if(keyDatabase.contains(key))
{
statusLed.setState(DualColorLed::State::Green);
logger.info("Access granted.");
unauthorizedAccess = false;
doorLock.open();
continue;
}
else
{
statusLed.setState(DualColorLed::State::Red);
logger.error("ACCESS DENIED!");
unauthorizedAccess = true;
delay(DOOR_OPEN_TIME_MS);
continue;
}
}
}
void DoorLockController::run()
{
for(;;)
{
heartbeat();
checkForKeys();
statusLed.setState(DualColorLed::State::Off);
delay(POLLING_INTERVAL_MS);
}
}

50
DoorLockController.h Normal file
View File

@ -0,0 +1,50 @@
#ifndef CDOORLOCKCONTROLLER_H
#define CDOORLOCKCONTROLLER_H
#include "ArduinoGpio.h"
#include "ArduinoSerialLogger.h"
#include "DoorLock.h"
#include "NfcAuthenticator.h"
#include "OneWireAuthenticator.h"
#include "HardcodedKeyStorage.h"
#include "DualColorLed.h"
class DoorLockController
{
public:
DoorLockController();
void run();
private:
enum {
NFC_SLAVE_SELECT_PIN = 10,
NFC_RESET_PIN = 9,
ONEWIRE_PIN = 8,
DOOR_PIN = 4,
LED_RED_PIN = 3,
LED_GREEN_PIN = 2,
DOOR_OPEN_TIME_MS = 3000,
NUM_AUTHENTICATORS=2,
POLLING_INTERVAL_MS = 250,
BLINK_INTERVAL_CYCLES = 20
};
void heartbeat();
void checkForKeys();
ArduinoSerialLogger logger;
ArduinoGpio greenLedGpio, redLedGpio, doorLockGpio;
DualColorLed statusLed;
DoorLock doorLock;
NfcAuthenticator nfcAuthenticator;
OneWireAuthenticator oneWireAuthenticator;
IAuthenticator* authenticators[NUM_AUTHENTICATORS];
HardcodedKeyStorage keyDatabase;
bool unauthorizedAccess;
short heartbeatCounter;
};
#endif // CDOORLOCKCONTROLLER_H

37
DualColorLed.cpp Normal file
View File

@ -0,0 +1,37 @@
#include "DualColorLed.h"
#include <Arduino.h>
DualColorLed::DualColorLed(IGpio& redGpio, IGpio& greenGpio):
redGpio(redGpio),
greenGpio(greenGpio),
currentState(State::Off)
{
redGpio.write(false);
greenGpio.write(false);
redGpio.setMode(IGpio::Mode::Output);
greenGpio.setMode(IGpio::Mode::Output);
}
void DualColorLed::setState(DualColorLed::State newState)
{
if(currentState == newState)
return;
currentState = newState;
switch(newState) {
case State::Off:
redGpio.write(false);
greenGpio.write(false);
return;
case State::Red:
redGpio.write(true);
greenGpio.write(false);
return;
case State::Green:
redGpio.write(false);
greenGpio.write(true);
return;
}
}

24
DualColorLed.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef DUALCOLORLED_H
#define DUALCOLORLED_H
#include "IGpio.h"
class DualColorLed
{
public:
enum class State {
Off,
Red,
Green
};
DualColorLed(IGpio& redGpio, IGpio& greenGpio);
void setState(State newState);
private:
IGpio& redGpio;
IGpio& greenGpio;
State currentState;
};
#endif // DUALCOLORLED_H

36
HardcodedKeyStorage.cpp Normal file
View File

@ -0,0 +1,36 @@
#include "HardcodedKeyStorage.h"
HardcodedKeyStorage::HardcodedKeyStorage():
keyTable( {
MifareClassicKey((const uint8_t[]) { 0x01, 0x02, 0x03, 0x04 }), // SAMPLE. DO NOT USE!
})
{}
HardcodedKeyStorage::~HardcodedKeyStorage() {}
bool HardcodedKeyStorage::contains(const Key& inputKey)
{
if(!inputKey.isValid())
return false;
for(const Key& keyFromTable: keyTable)
{
if(inputKey == keyFromTable)
return true;
}
return false;
}
bool HardcodedKeyStorage::insert(const Key &existingKey, const Key &newKey)
{
(void) existingKey;
(void) newKey;
return false;
}
bool HardcodedKeyStorage::remove(const Key &key)
{
(void) key;
return false;
}

24
HardcodedKeyStorage.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef HARDCODEDKEYSTORAGE_H
#define HARDCODEDKEYSTORAGE_H
#include "IKeyStorage.h"
class HardcodedKeyStorage: public IKeyStorage
{
public:
HardcodedKeyStorage();
virtual ~HardcodedKeyStorage();
virtual bool contains(const Key &inputKey);
virtual bool insert(const Key &existingKey, const Key &newKey);
virtual bool remove(const Key &key);
private:
enum {
NUMBER_OF_ENTRIES = 1,
};
Key keyTable[NUMBER_OF_ENTRIES];
};
#endif // HARDCODEDKEYSTORAGE_H

14
IAuthenticator.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef IAUTHENTICATOR_H
#define IAUTHENTICATOR_H
#include "Key.h"
class IAuthenticator
{
public:
virtual ~IAuthenticator() {};
virtual Key getKey() = 0;
};
#endif // IAUTHENTICATOR_H

20
IGpio.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef IGPIO_H
#define IGPIO_H
class IGpio
{
public:
enum class Mode {
Input,
InputPullup,
Output,
};
virtual ~IGpio() {}
virtual bool read() = 0;
virtual void write(bool value) = 0;
virtual Mode getMode() = 0;
virtual void setMode(Mode mode) = 0;
};
#endif // IGPIO_H

16
IKeyStorage.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef IKEYSTORAGE_H
#define IKEYSTORAGE_H
#include "Key.h"
class IKeyStorage
{
public:
virtual ~IKeyStorage() {}
virtual bool contains(const Key& key) = 0;
virtual bool insert(const Key& existingKey, const Key& newKey) = 0;
virtual bool remove(const Key& key) = 0;
};
#endif // IKEYSTORAGE_H

17
ILogger.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef ILOGGER_H
#define ILOGGER_H
class ILogger
{
public:
virtual ~ILogger() {}
virtual void debug(const char *format, ...) const = 0;
virtual void info(const char *format, ...) const = 0;
virtual void warning(const char* format, ...) const = 0;
virtual void error(const char* format, ...) const = 0;
virtual void flush() const;
};
#endif // ILOGGER_H

48
Key.cpp Normal file
View File

@ -0,0 +1,48 @@
#include "Key.h"
#include <string.h>
Key::Key() :
type(KeyType::Invalid),
size(0),
value()
{
}
Key::Key(KeyType type, size_t size, const void *data):
type(type),
size(size),
value()
{
memcpy(value, data, size);
}
bool Key::isValid() const
{
return type != KeyType::Invalid;
}
bool Key::operator==(const Key &rhs) const
{
if(type == KeyType::Invalid)
return type == rhs.type;
return type == rhs.type && size == rhs.size && isBufferEqual(value, rhs.value, size);
}
bool Key::isBufferEqual(const unsigned char *lhs, const unsigned char *rhs, size_t size) const
{
unsigned char maskOfDifferences = 0;
/** This comparison by design checks whole buffers to harden timing attacks */
for(size_t i = 0; i < size; ++i)
maskOfDifferences |= lhs[i] ^ rhs[i];
return (maskOfDifferences == 0);
}
DallasIButtonKey::DallasIButtonKey(const uint8_t uid[]):
Key(KeyType::iButton, UID_SIZE, uid) {}
MifareClassicKey::MifareClassicKey(const uint8_t nuid[]):
Key(KeyType::MifareClassic, NUID_SIZE, nuid) {}

48
Key.h Normal file
View File

@ -0,0 +1,48 @@
#ifndef KEY_H
#define KEY_H
#include <stddef.h>
#include <stdint.h>
class Key {
public:
enum {
MAX_KEY_SIZE = 8
};
enum class KeyType
{
Invalid = 0,
MifareClassic = 1,
iButton = 2,
};
Key();
bool isValid() const;
bool operator==(const Key& rhs) const;
protected:
Key(KeyType type, size_t size, const void* data);
private:
bool isBufferEqual(const unsigned char *lhs, const unsigned char* rhs, size_t size) const;
KeyType type;
size_t size;
unsigned char value[MAX_KEY_SIZE];
};
class DallasIButtonKey: public Key {
public:
enum {UID_SIZE = 8};
explicit DallasIButtonKey(const uint8_t uid[UID_SIZE]);
};
class MifareClassicKey: public Key {
public:
enum {NUID_SIZE = 4};
explicit MifareClassicKey(const uint8_t nuid[NUID_SIZE]);
};
#endif // KEY_H

54
NfcAuthenticator.cpp Normal file
View File

@ -0,0 +1,54 @@
#include "NfcAuthenticator.h"
#include "ILogger.h"
#include <SPI.h>
NfcAuthenticator::NfcAuthenticator(int nfcSlaveSelectPin, int nfcResetPin, const ILogger &logger):
rfid(nfcSlaveSelectPin, nfcResetPin),
logger(logger)
{
SPI.begin();
rfid.PCD_Init();
}
NfcAuthenticator::~NfcAuthenticator()
{}
Key NfcAuthenticator::getKey()
{
if(!initializeCard())
return Key();
uint8_t *nuid = rfid.uid.uidByte;
logger.debug("Mifare Classic tag detected, NUID: %02hhX %02hhX %02hhX %02hhX", nuid[0], nuid[1], nuid[2], nuid[3]);
releaseCard();
return MifareClassicKey(rfid.uid.uidByte);
}
bool NfcAuthenticator::initializeCard()
{
if (!rfid.PICC_IsNewCardPresent())
return false;
if (!rfid.PICC_ReadCardSerial())
return false;
auto piccType = rfid.PICC_GetType(rfid.uid.sak);
if (piccType != MFRC522::PICC_TYPE_MIFARE_MINI &&
piccType != MFRC522::PICC_TYPE_MIFARE_1K &&
piccType != MFRC522::PICC_TYPE_MIFARE_4K)
{
logger.warning("Invalid Mifare tag type: %s", rfid.PICC_GetTypeName(piccType));
return false;
}
return true;
}
void NfcAuthenticator::releaseCard()
{
rfid.PICC_HaltA();
rfid.PCD_StopCrypto1();
}

25
NfcAuthenticator.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef NFCAUTHENTICATOR_H
#define NFCAUTHENTICATOR_H
#include "IAuthenticator.h"
#include <MFRC522.h>
class ILogger;
class NfcAuthenticator: public IAuthenticator
{
public:
NfcAuthenticator(int nfcSlaveSelectPin, int nfcResetPin, const ILogger& logger);
virtual ~NfcAuthenticator();
virtual Key getKey();
private:
bool initializeCard();
void releaseCard();
MFRC522 rfid;
const ILogger& logger;
};
#endif // NFCAUTHENTICATOR_H

27
OneWireAuthenticator.cpp Normal file
View File

@ -0,0 +1,27 @@
#include "OneWireAuthenticator.h"
#include "ILogger.h"
#include <Arduino.h>
OneWireAuthenticator::OneWireAuthenticator(int interfacePin, const ILogger& logger):
oneWire(interfacePin),
logger(logger)
{
pinMode(interfacePin, INPUT_PULLUP);
}
Key OneWireAuthenticator::getKey()
{
uint8_t key[ONEWIRE_KEY_SIZE];
if (!oneWire.search(key)) {
oneWire.reset_search();
return Key();
}
logger.debug("1-Wire device detected, S/N: %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX",
key[0], key[1], key[2], key[3], key[4], key[5], key[6], key[7]);
oneWire.reset();
return DallasIButtonKey(key);
}

24
OneWireAuthenticator.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef ONEWIREAUTHENTICATOR_H
#define ONEWIREAUTHENTICATOR_H
#include "IAuthenticator.h"
#include <OneWire.h>
class ILogger;
class OneWireAuthenticator: public IAuthenticator
{
public:
OneWireAuthenticator(int interfacePin, const ILogger& logger);
virtual Key getKey();
private:
enum {
ONEWIRE_KEY_SIZE = 8,
};
OneWire oneWire;
const ILogger& logger;
};
#endif // ONEWIREAUTHENTICATOR_H

8
zamek_hswro.ino Normal file
View File

@ -0,0 +1,8 @@
#include "DoorLockController.h"
void setup() {}
void loop()
{
DoorLockController().run();
}