Create storage class
This commit is contained in:
parent
a4cab0931f
commit
67169240f9
@ -3,18 +3,22 @@
|
|||||||
#include "server.h"
|
#include "server.h"
|
||||||
#include "servo.h"
|
#include "servo.h"
|
||||||
#include "message.h"
|
#include "message.h"
|
||||||
|
#include "storage.h"
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
|
||||||
class SesameController: public ServerConnectionCallbacks {
|
class SesameController: public ServerConnectionCallbacks {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SesameController(ServerConnection* server, ServoController* servo, AsyncWebServer* local);
|
SesameController(ServerConnection* server, ServoController* servo, AsyncWebServer* local, uint8_t remoteDeviceCount);
|
||||||
|
|
||||||
|
void configure();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
ServerConnection* server;
|
ServerConnection* server;
|
||||||
ServoController* servo;
|
ServoController* servo;
|
||||||
AsyncWebServer* local;
|
AsyncWebServer* local;
|
||||||
|
Storage storage;
|
||||||
|
|
||||||
// The buffer to hold a received message while it is read
|
// The buffer to hold a received message while it is read
|
||||||
uint8_t receivedMessageBuffer[AUTHENTICATED_MESSAGE_SIZE];
|
uint8_t receivedMessageBuffer[AUTHENTICATED_MESSAGE_SIZE];
|
||||||
@ -37,6 +41,5 @@ private:
|
|||||||
|
|
||||||
uint16_t prepareResponseBuffer(SesameEvent event, uint8_t deviceId = 0);
|
uint16_t prepareResponseBuffer(SesameEvent event, uint8_t deviceId = 0);
|
||||||
void sendPreparedLocalResponse(AsyncWebServerRequest *request);
|
void sendPreparedLocalResponse(AsyncWebServerRequest *request);
|
||||||
void sendLocalResponse(AsyncWebServerRequest *request, SesameEvent event, uint8_t deviceId = 0);
|
|
||||||
void sendPreparedServerResponse();
|
void sendPreparedServerResponse();
|
||||||
};
|
};
|
@ -3,11 +3,6 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The size of the message counter in bytes (uint32_t)
|
|
||||||
*/
|
|
||||||
#define MESSAGE_COUNTER_SIZE sizeof(uint32_t)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Configure an NTP server to get the current time
|
* @brief Configure an NTP server to get the current time
|
||||||
*
|
*
|
||||||
@ -54,64 +49,4 @@ void setMessageTimeAllowedOffset(uint32_t offset);
|
|||||||
* @return true The time is within the acceptable offset of the local time
|
* @return true The time is within the acceptable offset of the local time
|
||||||
* @return false The message time is invalid
|
* @return false The message time is invalid
|
||||||
*/
|
*/
|
||||||
bool isMessageTimeAcceptable(uint32_t messageTime);
|
bool isMessageTimeAcceptable(uint32_t messageTime);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Initialize the use of the message counter API
|
|
||||||
*
|
|
||||||
* The message counter is stored in EEPROM, which must be initialized before use.
|
|
||||||
*
|
|
||||||
* @note The ESP32 does not have a true EEPROM,
|
|
||||||
* which is emulated using a section of the flash memory.
|
|
||||||
*/
|
|
||||||
void prepareMessageCounterUsage();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the expected count for the next message.
|
|
||||||
*
|
|
||||||
* The counter is stored in EEPROM to persist across restarts
|
|
||||||
*
|
|
||||||
* @return The next counter to use by the remote
|
|
||||||
*/
|
|
||||||
uint32_t getNextMessageCounter(uint8_t deviceId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Print info about the current message counter to the serial output
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void printMessageCounters();
|
|
||||||
|
|
||||||
bool isDeviceIdValid(uint8_t deviceId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if a received counter is valid
|
|
||||||
*
|
|
||||||
* The counter is valid if it is larger than the previous counter
|
|
||||||
* (larger or equal to the next expected counter).
|
|
||||||
*
|
|
||||||
* @param counter The counter to check
|
|
||||||
* @return true The counter is valid
|
|
||||||
* @return false The counter belongs to an old message
|
|
||||||
*/
|
|
||||||
bool isMessageCounterValid(uint32_t counter, uint8_t deviceId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Mark a counter of a message as used.
|
|
||||||
*
|
|
||||||
* The counter value is stored in EEPROM to persist across restarts.
|
|
||||||
*
|
|
||||||
* All messages with counters lower than the given one will become invalid.
|
|
||||||
*
|
|
||||||
* @param counter The counter used in the last message.
|
|
||||||
*/
|
|
||||||
void didUseMessageCounter(uint32_t counter, uint8_t deviceId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Reset the message counter.
|
|
||||||
*
|
|
||||||
* @warning The counter should never be reset in production environments,
|
|
||||||
* and only together with a new secret key. Otherwise old messages may be
|
|
||||||
* used for replay attacks.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void resetMessageCounters();
|
|
@ -53,6 +53,8 @@ typedef struct {
|
|||||||
|
|
||||||
} Message;
|
} Message;
|
||||||
|
|
||||||
|
constexpr size_t messageCounterSize = sizeof(uint32_t);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief An authenticated message by the mobile device to command unlocking.
|
* @brief An authenticated message by the mobile device to command unlocking.
|
||||||
*
|
*
|
||||||
|
83
include/storage.h
Normal file
83
include/storage.h
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
class Storage {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Storage(uint8_t remoteDeviceCount) : remoteDeviceCount(remoteDeviceCount) { };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the use of the message counter API
|
||||||
|
*
|
||||||
|
* The message counter is stored in EEPROM, which must be initialized before use.
|
||||||
|
*
|
||||||
|
* @note The ESP32 does not have a true EEPROM,
|
||||||
|
* which is emulated using a section of the flash memory.
|
||||||
|
*/
|
||||||
|
void configure();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if a device ID is allowed
|
||||||
|
*
|
||||||
|
* @param deviceId The ID to check
|
||||||
|
* @return true The id is valid
|
||||||
|
* @return false The id is invalid
|
||||||
|
*/
|
||||||
|
bool isDeviceIdValid(uint8_t deviceId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if a received counter is valid
|
||||||
|
*
|
||||||
|
* The counter is valid if it is larger than the previous counter
|
||||||
|
* (larger or equal to the next expected counter).
|
||||||
|
*
|
||||||
|
* @param counter The counter to check
|
||||||
|
* @return true The counter is valid
|
||||||
|
* @return false The counter belongs to an old message
|
||||||
|
*/
|
||||||
|
bool isMessageCounterValid(uint32_t counter, uint8_t deviceId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Mark a counter of a message as used.
|
||||||
|
*
|
||||||
|
* The counter value is stored in EEPROM to persist across restarts.
|
||||||
|
*
|
||||||
|
* All messages with counters lower than the given one will become invalid.
|
||||||
|
*
|
||||||
|
* @param counter The counter used in the last message.
|
||||||
|
*/
|
||||||
|
void didUseMessageCounter(uint32_t counter, uint8_t deviceId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the expected count for the next message.
|
||||||
|
*
|
||||||
|
* The counter is stored in EEPROM to persist across restarts
|
||||||
|
*
|
||||||
|
* @return The next counter to use by the remote
|
||||||
|
*/
|
||||||
|
uint32_t getNextMessageCounter(uint8_t deviceId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Print info about the current message counter to the serial output
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void printMessageCounters();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reset the message counter.
|
||||||
|
*
|
||||||
|
* @warning The counter should never be reset in production environments,
|
||||||
|
* and only together with a new secret key. Otherwise old messages may be
|
||||||
|
* used for replay attacks.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void resetMessageCounters();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
uint8_t remoteDeviceCount;
|
||||||
|
|
||||||
|
void setMessageCounter(uint32_t counter, uint8_t deviceId);
|
||||||
|
};
|
@ -3,12 +3,18 @@
|
|||||||
#include "fresh.h"
|
#include "fresh.h"
|
||||||
#include "crypto.h"
|
#include "crypto.h"
|
||||||
|
|
||||||
SesameController::SesameController(ServerConnection* server, ServoController* servo, AsyncWebServer* local) :
|
SesameController::SesameController(ServerConnection* server, ServoController* servo, AsyncWebServer* local, uint8_t remoteDeviceCount) :
|
||||||
server(server), servo(servo), local(local) {
|
server(server), servo(servo), local(local), storage(remoteDeviceCount) {
|
||||||
|
|
||||||
// Set up response buffer
|
// Set up response buffer
|
||||||
responseStatus = (SesameEvent*) responseBuffer;
|
responseStatus = (SesameEvent*) responseBuffer;
|
||||||
responseMessage = (AuthenticatedMessage*) (responseBuffer + 1);
|
responseMessage = (AuthenticatedMessage*) (responseBuffer + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SesameController::configure() {
|
||||||
|
// Prepare EEPROM for reading and writing
|
||||||
|
storage.configure();
|
||||||
|
|
||||||
// Direct messages and errors over the websocket to the controller
|
// Direct messages and errors over the websocket to the controller
|
||||||
server->setCallbackHandler(this);
|
server->setCallbackHandler(this);
|
||||||
|
|
||||||
@ -17,6 +23,9 @@ server(server), servo(servo), local(local) {
|
|||||||
this->handleLocalMessage(request);
|
this->handleLocalMessage(request);
|
||||||
this->sendPreparedLocalResponse(request);
|
this->sendPreparedLocalResponse(request);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//storage.resetMessageCounters();
|
||||||
|
storage.printMessageCounters();
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Local
|
// MARK: Local
|
||||||
@ -84,17 +93,17 @@ SesameEvent SesameController::verifyAndProcessReceivedMessage(AuthenticatedMessa
|
|||||||
if (!isAuthenticMessage(message, remoteKey, keySize)) {
|
if (!isAuthenticMessage(message, remoteKey, keySize)) {
|
||||||
return SesameEvent::MessageAuthenticationFailed;
|
return SesameEvent::MessageAuthenticationFailed;
|
||||||
}
|
}
|
||||||
if (!isDeviceIdValid(message->message.device)) {
|
if (!storage.isDeviceIdValid(message->message.device)) {
|
||||||
return SesameEvent::MessageDeviceInvalid;
|
return SesameEvent::MessageDeviceInvalid;
|
||||||
}
|
}
|
||||||
if (!isMessageCounterValid(message->message.id, message->message.device)) {
|
if (!storage.isMessageCounterValid(message->message.id, message->message.device)) {
|
||||||
return SesameEvent::MessageCounterInvalid;
|
return SesameEvent::MessageCounterInvalid;
|
||||||
}
|
}
|
||||||
if (!isMessageTimeAcceptable(message->message.time)) {
|
if (!isMessageTimeAcceptable(message->message.time)) {
|
||||||
return SesameEvent::MessageTimeMismatch;
|
return SesameEvent::MessageTimeMismatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
didUseMessageCounter(message->message.id, message->message.device);
|
storage.didUseMessageCounter(message->message.id, message->message.device);
|
||||||
// Move servo
|
// Move servo
|
||||||
servo->pressButton();
|
servo->pressButton();
|
||||||
Serial.printf("[Info] Accepted message %d\n", message->message.id);
|
Serial.printf("[Info] Accepted message %d\n", message->message.id);
|
||||||
@ -119,7 +128,7 @@ uint16_t SesameController::prepareResponseBuffer(SesameEvent event, uint8_t devi
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
responseMessage->message.time = getEpochTime();
|
responseMessage->message.time = getEpochTime();
|
||||||
responseMessage->message.id = getNextMessageCounter(deviceId);
|
responseMessage->message.id = storage.getNextMessageCounter(deviceId);
|
||||||
responseMessage->message.device = deviceId;
|
responseMessage->message.device = deviceId;
|
||||||
if (!authenticateMessage(responseMessage, localKey, keySize)) {
|
if (!authenticateMessage(responseMessage, localKey, keySize)) {
|
||||||
*responseStatus = SesameEvent::InvalidResponseAuthentication;
|
*responseStatus = SesameEvent::InvalidResponseAuthentication;
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <EEPROM.h>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The allowed discrepancy between the time of a received message
|
* @brief The allowed discrepancy between the time of a received message
|
||||||
@ -55,54 +54,4 @@ bool isMessageTimeAcceptable(uint32_t t) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void prepareMessageCounterUsage() {
|
|
||||||
EEPROM.begin(MESSAGE_COUNTER_SIZE * remoteDeviceCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t getNextMessageCounter(uint8_t deviceId) {
|
|
||||||
int offset = deviceId * MESSAGE_COUNTER_SIZE;
|
|
||||||
uint32_t counter = (uint32_t) EEPROM.read(offset + 0) << 24;
|
|
||||||
counter += (uint32_t) EEPROM.read(offset + 1) << 16;
|
|
||||||
counter += (uint32_t) EEPROM.read(offset + 2) << 8;
|
|
||||||
counter += (uint32_t) EEPROM.read(offset + 3);
|
|
||||||
return counter;
|
|
||||||
}
|
|
||||||
|
|
||||||
void printMessageCounters() {
|
|
||||||
Serial.print("[INFO] Next message numbers:");
|
|
||||||
for (uint8_t i = 0; i < remoteDeviceCount; i += 1) {
|
|
||||||
Serial.printf(" %u", getNextMessageCounter(i));
|
|
||||||
}
|
|
||||||
Serial.println("");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isDeviceIdValid(uint8_t deviceId) {
|
|
||||||
return deviceId < remoteDeviceCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isMessageCounterValid(uint32_t counter, uint8_t deviceId) {
|
|
||||||
return counter >= getNextMessageCounter(deviceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setMessageCounter(uint32_t counter, uint8_t deviceId) {
|
|
||||||
int offset = deviceId * MESSAGE_COUNTER_SIZE;
|
|
||||||
EEPROM.write(offset + 0, (counter >> 24) & 0xFF);
|
|
||||||
EEPROM.write(offset + 1, (counter >> 16) & 0xFF);
|
|
||||||
EEPROM.write(offset + 2, (counter >> 8) & 0xFF);
|
|
||||||
EEPROM.write(offset + 3, counter & 0xFF);
|
|
||||||
EEPROM.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void didUseMessageCounter(uint32_t counter, uint8_t deviceId) {
|
|
||||||
// Store the next counter, so that resetting starts at 0
|
|
||||||
setMessageCounter(counter+1, deviceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
void resetMessageCounters() {
|
|
||||||
for (uint8_t i = 0; i < remoteDeviceCount; i += 1) {
|
|
||||||
setMessageCounter(0, i);
|
|
||||||
}
|
|
||||||
Serial.println("[WARN] Message counters reset");
|
|
||||||
}
|
|
@ -25,7 +25,7 @@ ServoController servo(pwmTimer, servoFrequency, servoPin);
|
|||||||
|
|
||||||
AsyncWebServer local(localPort);
|
AsyncWebServer local(localPort);
|
||||||
|
|
||||||
SesameController controller(&server, &servo, &local);
|
SesameController controller(&server, &servo, &local, remoteDeviceCount);
|
||||||
|
|
||||||
// Forward declare monitoring functions
|
// Forward declare monitoring functions
|
||||||
void ensureWiFiConnection(uint32_t time);
|
void ensureWiFiConnection(uint32_t time);
|
||||||
@ -41,9 +41,7 @@ void setup() {
|
|||||||
servo.configure(lockOpeningDuration, servoPressedState, servoReleasedState);
|
servo.configure(lockOpeningDuration, servoPressedState, servoReleasedState);
|
||||||
Serial.println("[INFO] Servo configured");
|
Serial.println("[INFO] Servo configured");
|
||||||
|
|
||||||
prepareMessageCounterUsage();
|
controller.configure();
|
||||||
//resetMessageCounters();
|
|
||||||
printMessageCounters();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
|
53
src/storage.cpp
Normal file
53
src/storage.cpp
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#include "storage.h"
|
||||||
|
#include "message.h"
|
||||||
|
#include <EEPROM.h>
|
||||||
|
|
||||||
|
void Storage::configure() {
|
||||||
|
EEPROM.begin(messageCounterSize * remoteDeviceCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Storage::isDeviceIdValid(uint8_t deviceId) {
|
||||||
|
return deviceId < remoteDeviceCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Storage::isMessageCounterValid(uint32_t counter, uint8_t deviceId) {
|
||||||
|
return counter >= getNextMessageCounter(deviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Storage::didUseMessageCounter(uint32_t counter, uint8_t deviceId) {
|
||||||
|
// Store the next counter, so that resetting starts at 0
|
||||||
|
setMessageCounter(counter+1, deviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Storage::setMessageCounter(uint32_t counter, uint8_t deviceId) {
|
||||||
|
int offset = deviceId * messageCounterSize;
|
||||||
|
EEPROM.write(offset + 0, (counter >> 24) & 0xFF);
|
||||||
|
EEPROM.write(offset + 1, (counter >> 16) & 0xFF);
|
||||||
|
EEPROM.write(offset + 2, (counter >> 8) & 0xFF);
|
||||||
|
EEPROM.write(offset + 3, counter & 0xFF);
|
||||||
|
EEPROM.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Storage::getNextMessageCounter(uint8_t deviceId) {
|
||||||
|
int offset = deviceId * messageCounterSize;
|
||||||
|
uint32_t counter = (uint32_t) EEPROM.read(offset + 0) << 24;
|
||||||
|
counter += (uint32_t) EEPROM.read(offset + 1) << 16;
|
||||||
|
counter += (uint32_t) EEPROM.read(offset + 2) << 8;
|
||||||
|
counter += (uint32_t) EEPROM.read(offset + 3);
|
||||||
|
return counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Storage::printMessageCounters() {
|
||||||
|
Serial.print("[INFO] Next message numbers:");
|
||||||
|
for (uint8_t i = 0; i < remoteDeviceCount; i += 1) {
|
||||||
|
Serial.printf(" %u", getNextMessageCounter(i));
|
||||||
|
}
|
||||||
|
Serial.println("");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Storage::resetMessageCounters() {
|
||||||
|
for (uint8_t i = 0; i < remoteDeviceCount; i += 1) {
|
||||||
|
setMessageCounter(0, i);
|
||||||
|
}
|
||||||
|
Serial.println("[WARN] Message counters reset");
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user