From 2ec2679d6eb55fc35d26a893b0a1e37a9da41170 Mon Sep 17 00:00:00 2001 From: Anders Date: Sun, 10 May 2020 16:42:57 +0200 Subject: [PATCH] - Move all serialization code to separate Serializer namespace and files. - Generalize and improve JSON processing code. - Prevent mesh passwords from containing " characters to avoid messing up the JSON processing. - Improve documentation. --- .../examples/HelloEspnow/HelloEspnow.ino | 2 +- .../examples/HelloMesh/HelloMesh.ino | 2 +- .../examples/HelloTcpIp/HelloTcpIp.ino | 2 +- .../src/EncryptedConnectionData.cpp | 13 +- .../ESP8266WiFiMesh/src/EspnowMeshBackend.cpp | 26 +- .../ESP8266WiFiMesh/src/EspnowMeshBackend.h | 6 +- .../ESP8266WiFiMesh/src/FloodingMesh.cpp | 12 +- .../ESP8266WiFiMesh/src/JsonTranslator.cpp | 343 +++++++++--------- .../ESP8266WiFiMesh/src/JsonTranslator.h | 117 ++++-- .../ESP8266WiFiMesh/src/MeshBackendBase.cpp | 1 + .../ESP8266WiFiMesh/src/MeshBackendBase.h | 2 +- libraries/ESP8266WiFiMesh/src/Serializer.cpp | 122 +++++++ libraries/ESP8266WiFiMesh/src/Serializer.h | 44 +++ .../src/TypeConversionFunctions.h | 4 +- 14 files changed, 464 insertions(+), 232 deletions(-) create mode 100644 libraries/ESP8266WiFiMesh/src/Serializer.cpp create mode 100644 libraries/ESP8266WiFiMesh/src/Serializer.h diff --git a/libraries/ESP8266WiFiMesh/examples/HelloEspnow/HelloEspnow.ino b/libraries/ESP8266WiFiMesh/examples/HelloEspnow/HelloEspnow.ino index fcb608c29..9ec029344 100644 --- a/libraries/ESP8266WiFiMesh/examples/HelloEspnow/HelloEspnow.ino +++ b/libraries/ESP8266WiFiMesh/examples/HelloEspnow/HelloEspnow.ino @@ -19,7 +19,7 @@ namespace TypeCast = MeshTypeConversionFunctions; https://arduino-esp8266.readthedocs.io/en/latest/PROGMEM.html */ constexpr char exampleMeshName[] PROGMEM = "MeshNode_"; // The name of the mesh network. Used as prefix for the node SSID and to find other network nodes in the example networkFilter and broadcastFilter functions below. -constexpr char exampleWiFiPassword[] PROGMEM = "ChangeThisWiFiPassword_TODO"; // The password has to be min 8 and max 64 characters long, otherwise an AP which uses it will not be found during scans. +constexpr char exampleWiFiPassword[] PROGMEM = "ChangeThisWiFiPassword_TODO"; // Note: " is an illegal character. The password has to be min 8 and max 64 characters long, otherwise an AP which uses it will not be found during scans. // A custom encryption key is required when using encrypted ESP-NOW transmissions. There is always a default Kok set, but it can be replaced if desired. // All ESP-NOW keys below must match in an encrypted connection pair for encrypted communication to be possible. diff --git a/libraries/ESP8266WiFiMesh/examples/HelloMesh/HelloMesh.ino b/libraries/ESP8266WiFiMesh/examples/HelloMesh/HelloMesh.ino index fdb129d3a..a6fb6bcd2 100644 --- a/libraries/ESP8266WiFiMesh/examples/HelloMesh/HelloMesh.ino +++ b/libraries/ESP8266WiFiMesh/examples/HelloMesh/HelloMesh.ino @@ -26,7 +26,7 @@ namespace TypeCast = MeshTypeConversionFunctions; https://arduino-esp8266.readthedocs.io/en/latest/PROGMEM.html */ constexpr char exampleMeshName[] PROGMEM = "MeshNode_"; // The name of the mesh network. Used as prefix for the node SSID and to find other network nodes in the example networkFilter and broadcastFilter functions below. -constexpr char exampleWiFiPassword[] PROGMEM = "ChangeThisWiFiPassword_TODO"; // The password has to be min 8 and max 64 characters long, otherwise an AP which uses it will not be found during scans. +constexpr char exampleWiFiPassword[] PROGMEM = "ChangeThisWiFiPassword_TODO"; // Note: " is an illegal character. The password has to be min 8 and max 64 characters long, otherwise an AP which uses it will not be found during scans. // A custom encryption key is required when using encrypted ESP-NOW transmissions. There is always a default Kok set, but it can be replaced if desired. // All ESP-NOW keys below must match in an encrypted connection pair for encrypted communication to be possible. diff --git a/libraries/ESP8266WiFiMesh/examples/HelloTcpIp/HelloTcpIp.ino b/libraries/ESP8266WiFiMesh/examples/HelloTcpIp/HelloTcpIp.ino index 325450da3..5c46b02e7 100644 --- a/libraries/ESP8266WiFiMesh/examples/HelloTcpIp/HelloTcpIp.ino +++ b/libraries/ESP8266WiFiMesh/examples/HelloTcpIp/HelloTcpIp.ino @@ -19,7 +19,7 @@ namespace TypeCast = MeshTypeConversionFunctions; https://arduino-esp8266.readthedocs.io/en/latest/PROGMEM.html */ constexpr char exampleMeshName[] PROGMEM = "MeshNode_"; -constexpr char exampleWiFiPassword[] PROGMEM = "ChangeThisWiFiPassword_TODO"; +constexpr char exampleWiFiPassword[] PROGMEM = "ChangeThisWiFiPassword_TODO"; // Note: " is an illegal character. The password has to be min 8 and max 64 characters long, otherwise an AP which uses it will not be found during scans. unsigned int requestNumber = 0; unsigned int responseNumber = 0; diff --git a/libraries/ESP8266WiFiMesh/src/EncryptedConnectionData.cpp b/libraries/ESP8266WiFiMesh/src/EncryptedConnectionData.cpp index b747b8b3a..8aaca0733 100644 --- a/libraries/ESP8266WiFiMesh/src/EncryptedConnectionData.cpp +++ b/libraries/ESP8266WiFiMesh/src/EncryptedConnectionData.cpp @@ -27,6 +27,7 @@ #include "TypeConversionFunctions.h" #include "JsonTranslator.h" #include "MeshCryptoInterface.h" +#include "Serializer.h" namespace { @@ -157,16 +158,8 @@ bool EncryptedConnectionData::desync() const { return _desync; } String EncryptedConnectionData::serialize() const { - // Returns: {"connectionState":{"duration":"123","password":"abc","ownSK":"1A2","peerSK":"3B4","peerStaMac":"F2","peerApMac":"E3"}} - - return - String(FPSTR(JsonTranslator::jsonConnectionState)) - + (temporary() ? String(FPSTR(JsonTranslator::jsonDuration)) + '\"' + String(temporary()->remainingDuration()) + F("\",") : emptyString) - + FPSTR(JsonTranslator::jsonDesync) + '\"' + String(desync()) + F("\",") - + FPSTR(JsonTranslator::jsonOwnSessionKey) + '\"' + TypeCast::uint64ToString(getOwnSessionKey()) + F("\",") - + FPSTR(JsonTranslator::jsonPeerSessionKey) + '\"' + TypeCast::uint64ToString(getPeerSessionKey()) + F("\",") - + FPSTR(JsonTranslator::jsonPeerStaMac) + '\"' + TypeCast::macToString(_peerStaMac) + F("\",") - + FPSTR(JsonTranslator::jsonPeerApMac) + '\"' + TypeCast::macToString(_peerApMac) + F("\"}}"); + return Serializer:: serializeEncryptedConnection((temporary() ? String(temporary()->remainingDuration()) : emptyString), String(desync()), TypeCast::uint64ToString(getOwnSessionKey()), + TypeCast::uint64ToString(getPeerSessionKey()), TypeCast::macToString(_peerStaMac), TypeCast::macToString(_peerApMac)); } const ExpiringTimeTracker *EncryptedConnectionData::temporary() const diff --git a/libraries/ESP8266WiFiMesh/src/EspnowMeshBackend.cpp b/libraries/ESP8266WiFiMesh/src/EspnowMeshBackend.cpp index 5f0ec6937..2713b389d 100644 --- a/libraries/ESP8266WiFiMesh/src/EspnowMeshBackend.cpp +++ b/libraries/ESP8266WiFiMesh/src/EspnowMeshBackend.cpp @@ -27,6 +27,8 @@ extern "C" { #include "MutexTracker.h" #include "JsonTranslator.h" #include "MeshCryptoInterface.h" +#include "EspnowUtility.h" +#include "Serializer.h" namespace { @@ -1112,7 +1114,7 @@ TransmissionStatusType EspnowMeshBackend::espnowSendToNode(const String &message TransmissionStatusType EspnowMeshBackend::espnowSendToNodeUnsynchronized(const String message, const uint8_t *targetBSSID, const char messageType, const uint64_t messageID, EspnowMeshBackend *espnowInstance) { using namespace EspnowProtocolInterpreter; - + MutexTracker mutexTracker(_espnowSendToNodeMutex); if(!mutexTracker.mutexCaptured()) { @@ -1773,7 +1775,7 @@ EncryptedConnectionStatus EspnowMeshBackend::requestEncryptedConnectionKernel(co requestMessage = encryptionRequestBuilder(requestNonce, existingTimeTracker); else if(_ongoingPeerRequestResult == EncryptedConnectionStatus::SOFT_LIMIT_CONNECTION_ESTABLISHED) // We will only get a soft limit connection. Adjust future actions based on this. - requestMessage = JsonTranslator::createEncryptionRequestHmacMessage(FPSTR(temporaryEncryptionRequestHeader), requestNonce, getEspnowHashKey(), + requestMessage = Serializer::createEncryptionRequestHmacMessage(FPSTR(temporaryEncryptionRequestHeader), requestNonce, getEspnowHashKey(), hashKeyLength, getAutoEncryptionDuration()); else assert(false && String(F("Unknown _ongoingPeerRequestResult during encrypted connection finalization!"))); @@ -1848,7 +1850,7 @@ String EspnowMeshBackend::defaultEncryptionRequestBuilder(const String &requestH { (void)existingTimeTracker; // This removes a "unused parameter" compiler warning. Does nothing else. - return JsonTranslator::createEncryptionRequestHmacMessage(requestHeader, requestNonce, hashKey, hashKeyLength, durationMs); + return Serializer::createEncryptionRequestHmacMessage(requestHeader, requestNonce, hashKey, hashKeyLength, durationMs); } String EspnowMeshBackend::flexibleEncryptionRequestBuilder(const uint32_t minDurationMs, const uint8_t *hashKey, @@ -1860,7 +1862,7 @@ String EspnowMeshBackend::flexibleEncryptionRequestBuilder(const uint32_t minDur uint32_t connectionDuration = minDurationMs >= existingTimeTracker.remainingDuration() ? minDurationMs : existingTimeTracker.remainingDuration(); - return createEncryptionRequestHmacMessage(FPSTR(temporaryEncryptionRequestHeader), requestNonce, hashKey, hashKeyLength, connectionDuration); + return Serializer::createEncryptionRequestHmacMessage(FPSTR(temporaryEncryptionRequestHeader), requestNonce, hashKey, hashKeyLength, connectionDuration); } EncryptedConnectionStatus EspnowMeshBackend::requestEncryptedConnection(const uint8_t *peerMac) @@ -2437,13 +2439,13 @@ void EspnowMeshBackend::sendPeerRequestConfirmations(const ExpiringTimeTracker * if(!existingEncryptedConnection && ((reciprocalPeerRequest && encryptedConnections.size() >= maxEncryptedConnections) || (!reciprocalPeerRequest && reservedEncryptedConnections() >= maxEncryptedConnections))) { - espnowSendToNodeUnsynchronized(JsonTranslator::createEncryptionRequestHmacMessage(FPSTR(maxConnectionsReachedHeader), + espnowSendToNodeUnsynchronized(Serializer::createEncryptionRequestHmacMessage(FPSTR(maxConnectionsReachedHeader), confirmationsIterator->getPeerRequestNonce(), hashKey, hashKeyLength), defaultBSSID, 'C', generateMessageID(nullptr)); // Generates a new message ID to avoid sending encrypted sessionKeys over unencrypted connections. confirmationsIterator = peerRequestConfirmationsToSend.erase(confirmationsIterator); } - else if(espnowSendToNodeUnsynchronized(JsonTranslator::createEncryptionRequestHmacMessage(FPSTR(basicConnectionInfoHeader), + else if(espnowSendToNodeUnsynchronized(Serializer::createEncryptionRequestHmacMessage(FPSTR(basicConnectionInfoHeader), confirmationsIterator->getPeerRequestNonce(), hashKey, hashKeyLength), sendToDefaultBSSID ? defaultBSSID : unencryptedBSSID, 'C', generateMessageID(nullptr)) // Generates a new message ID to avoid sending encrypted sessionKeys over unencrypted connections. == TransmissionStatusType::TRANSMISSION_COMPLETE) @@ -2475,7 +2477,7 @@ void EspnowMeshBackend::sendPeerRequestConfirmations(const ExpiringTimeTracker * if(!existingEncryptedConnection) { // Send "node full" message - espnowSendToNodeUnsynchronized(JsonTranslator::createEncryptionRequestHmacMessage(FPSTR(maxConnectionsReachedHeader), + espnowSendToNodeUnsynchronized(Serializer::createEncryptionRequestHmacMessage(FPSTR(maxConnectionsReachedHeader), confirmationsIterator->getPeerRequestNonce(), hashKey, hashKeyLength), defaultBSSID, 'C', generateMessageID(nullptr)); // Generates a new message ID to avoid sending encrypted sessionKeys over unencrypted connections. } @@ -2503,7 +2505,7 @@ void EspnowMeshBackend::sendPeerRequestConfirmations(const ExpiringTimeTracker * // Send password and keys. // Probably no need to know which connection type to use, that is stored in request node and will be sent over for finalization. - espnowSendToNodeUnsynchronized(JsonTranslator::createEncryptedConnectionInfo(messageHeader, + espnowSendToNodeUnsynchronized(Serializer::createEncryptedConnectionInfo(messageHeader, confirmationsIterator->getPeerRequestNonce(), confirmationsIterator->getAuthenticationPassword(), existingEncryptedConnection->getOwnSessionKey(), existingEncryptedConnection->getPeerSessionKey()), defaultBSSID, 'C', generateMessageID(nullptr)); // Generates a new message ID to avoid sending encrypted sessionKeys over unencrypted connections. @@ -2678,12 +2680,8 @@ void EspnowMeshBackend::resetTransmissionFailRate() } String EspnowMeshBackend::serializeUnencryptedConnection() -{ - using namespace JsonTranslator; - - // Returns: {"connectionState":{"unsyncMsgID":"123"}} - - return String(FPSTR(jsonConnectionState)) + createJsonEndPair(FPSTR(jsonUnsynchronizedMessageID), String(_unsynchronizedMessageID)); +{ + return Serializer::serializeUnencryptedConnection(String(_unsynchronizedMessageID)); } String EspnowMeshBackend::serializeEncryptedConnection(const uint8_t *peerMac) diff --git a/libraries/ESP8266WiFiMesh/src/EspnowMeshBackend.h b/libraries/ESP8266WiFiMesh/src/EspnowMeshBackend.h index 4f8308c2b..0e82e61ff 100644 --- a/libraries/ESP8266WiFiMesh/src/EspnowMeshBackend.h +++ b/libraries/ESP8266WiFiMesh/src/EspnowMeshBackend.h @@ -657,11 +657,11 @@ public: * @param intervalMs The time to wait for an ack after having made an ESP-NOW transmission, in milliseconds. Defaults to 15 ms. */ static void setEspnowRetransmissionInterval(const uint32_t intervalMs); - static uint32_t getEspnowRetransmissionInterval(); + static uint32_t getEspnowRetransmissionInterval(); // The maximum amount of time each of the two stages in an encrypted connection request may take. static void setEncryptionRequestTimeout(const uint32_t timeoutMs); - static uint32_t getEncryptionRequestTimeout(); + static uint32_t getEncryptionRequestTimeout(); void setAutoEncryptionDuration(const uint32_t duration); uint32_t getAutoEncryptionDuration() const; @@ -1217,7 +1217,7 @@ private: * @param peerMac The MAC of the node with which an encrypted connection should be established. * @param encryptionRequestBuilder A function which is responsible for constructing the request message to send. * Called twice when the request is successful. First to build the initial request message and then to build the connection verification message. - * The request message should typically be of the form: JsonTranslator::createEncryptionRequestIntro() + JsonTranslator::createEncryptionRequestEnding(). + * The request message should typically be of the form found in Serializer::createEncryptionRequestHmacMessage. * @return The ultimate status of the requested encrypted connection, as EncryptedConnectionStatus. */ EncryptedConnectionStatus requestEncryptedConnectionKernel(const uint8_t *peerMac, const encryptionRequestBuilderType &encryptionRequestBuilder); diff --git a/libraries/ESP8266WiFiMesh/src/FloodingMesh.cpp b/libraries/ESP8266WiFiMesh/src/FloodingMesh.cpp index 24cb88b1e..b788cf16e 100644 --- a/libraries/ESP8266WiFiMesh/src/FloodingMesh.cpp +++ b/libraries/ESP8266WiFiMesh/src/FloodingMesh.cpp @@ -25,6 +25,7 @@ #include "FloodingMesh.h" #include "TypeConversionFunctions.h" #include "JsonTranslator.h" +#include "Serializer.h" namespace { @@ -153,16 +154,11 @@ void FloodingMesh::performMeshInstanceMaintenance() String FloodingMesh::serializeMeshState() const { - using namespace JsonTranslator; - - // Returns: {"meshState":{"connectionState":{"unsyncMsgID":"123"},"meshMsgCount":"123"}} - String connectionState = getEspnowMeshBackendConst().serializeUnencryptedConnection(); + uint32_t unsyncMsgID = 0; + JsonTranslator::getUnsynchronizedMessageID(connectionState, unsyncMsgID); - return - String(F("{\"meshState\":{")) - + connectionState.substring(1, connectionState.length() - 1) + String(',') - + createJsonEndPair(FPSTR(jsonMeshMessageCount), String(_messageCount)); + return Serializer::serializeMeshState(String(unsyncMsgID), String(_messageCount)); } void FloodingMesh::loadMeshState(const String &serializedMeshState) diff --git a/libraries/ESP8266WiFiMesh/src/JsonTranslator.cpp b/libraries/ESP8266WiFiMesh/src/JsonTranslator.cpp index 9056a9dba..fea0b748d 100644 --- a/libraries/ESP8266WiFiMesh/src/JsonTranslator.cpp +++ b/libraries/ESP8266WiFiMesh/src/JsonTranslator.cpp @@ -25,59 +25,171 @@ #include "JsonTranslator.h" #include "EspnowProtocolInterpreter.h" #include "TypeConversionFunctions.h" -#include "MeshCryptoInterface.h" +#include "MeshCryptoInterface.h" // TODO: Remove? namespace { namespace TypeCast = MeshTypeConversionFunctions; + + bool getMac(const String &jsonString, const String &valueIdentifier, uint8_t *resultArray) + { + String jsonValue; + bool decoded = JsonTranslator::decode(jsonString, valueIdentifier, jsonValue); + + if(jsonValue.length() != 12) + decoded = false; // Mac String is always 12 characters long + + if(decoded) + TypeCast::stringToMac(jsonValue, resultArray); + + return decoded; + } } namespace JsonTranslator { - String createJsonPair(const String &valueIdentifier, const String &value) + int32_t getStartIndex(const String &jsonString, const String &valueIdentifier, const int32_t searchStartIndex) { - return valueIdentifier + '\"' + value + F("\","); + int32_t startIndex = jsonString.indexOf(String('"') + valueIdentifier + F("\":"), searchStartIndex); + if(startIndex < 0) + return startIndex; + + startIndex += valueIdentifier.length() + 3; // Do not include valueIdentifier and associated characters + return startIndex; + } + + int32_t getEndIndex(const String &jsonString, const int32_t searchStartIndex) + { + int32_t endIndex = -1; + + if(jsonString[searchStartIndex] == '"') + { + endIndex = jsonString.indexOf('"', searchStartIndex + 1); + } + else if(jsonString[searchStartIndex] == '{') + { + uint32_t depth = 1; + bool withinString = false; + + for(uint32_t index = searchStartIndex + 1; depth != 0 && index < jsonString.length(); ++index) + { + if(jsonString[index] == '"') + withinString = !withinString; + else if(!withinString) + { + if(jsonString[index] == '{') + ++depth; + else if(jsonString[index] == '}') + --depth; + } + + if(depth == 0) + { + assert(index < 0x80000000); // Must avoid int32_t overflow + endIndex = index; + } + } + } + + return endIndex; + } + + String encode(std::initializer_list identifiersAndValues) + { + assert(identifiersAndValues.size() % 2 == 0); // List must consist of identifer-value pairs. + + String result = String('{'); + + bool isIdentifier = true; + for(String element : identifiersAndValues) + { + bool isObject = !isIdentifier && element[0] == '{'; + if(isObject) + result += element; + else + result += String('"') + element + String('"'); + + if(isIdentifier) + result += ':'; + else + result += ','; + + isIdentifier = !isIdentifier; + } + + result[result.length() - 1] = '}'; + + return result; } - String createJsonEndPair(const String &valueIdentifier, const String &value) + String encodeLiterally(std::initializer_list identifiersAndValues) { - return valueIdentifier + '\"' + value + F("\"}}"); - } - - String createEncryptedConnectionInfo(const String &infoHeader, const String &requestNonce, const String &authenticationPassword, const uint64_t ownSessionKey, const uint64_t peerSessionKey) - { - // Returns: Encrypted connection info:{"arguments":{"nonce":"1F2","password":"abc","ownSK":"3B4","peerSK":"1A2"}} + assert(identifiersAndValues.size() % 2 == 0); // List must consist of identifer-value pairs. + + String result = String('{'); - return - infoHeader + String(F("{\"arguments\":{")) - + createJsonPair(FPSTR(jsonNonce), requestNonce) - + createJsonPair(FPSTR(jsonPassword), authenticationPassword) - + createJsonPair(FPSTR(jsonOwnSessionKey), TypeCast::uint64ToString(peerSessionKey)) // Exchanges session keys since it should be valid for the receiver. - + createJsonEndPair(FPSTR(jsonPeerSessionKey), TypeCast::uint64ToString(ownSessionKey)); - } - - String createEncryptionRequestIntro(const String &requestHeader, const uint32_t duration) - { - return - requestHeader + String(F("{\"arguments\":{")) - + (requestHeader == FPSTR(EspnowProtocolInterpreter::temporaryEncryptionRequestHeader) ? createJsonPair(FPSTR(jsonDuration), String(duration)) : emptyString); - } - - String createEncryptionRequestEnding(const String &requestNonce) - { - return createJsonEndPair(FPSTR(jsonNonce), requestNonce); - } - - String createEncryptionRequestHmacMessage(const String &requestHeader, const String &requestNonce, const uint8_t *hashKey, const uint8_t hashKeyLength, const uint32_t duration) - { - String mainMessage = createEncryptionRequestIntro(requestHeader, duration) + createJsonPair(FPSTR(jsonNonce), requestNonce); - uint8_t staMac[6] {0}; - uint8_t apMac[6] {0}; - String requesterStaApMac = TypeCast::macToString(WiFi.macAddress(staMac)) + TypeCast::macToString(WiFi.softAPmacAddress(apMac)); - String hmac = MeshCryptoInterface::createMeshHmac(requesterStaApMac + mainMessage, hashKey, hashKeyLength); - return mainMessage + createJsonEndPair(FPSTR(jsonHmac), hmac); + bool isIdentifier = true; + for(String element : identifiersAndValues) + { + if(isIdentifier) + result += String('"') + element + String('"') + ':'; + else + result += element + ','; + + isIdentifier = !isIdentifier; + } + + result[result.length() - 1] = '}'; + + return result; } + bool decode(const String &jsonString, const String &valueIdentifier, String &value) + { + int32_t startIndex = getStartIndex(jsonString, valueIdentifier); + if(startIndex < 0) + return false; + + int32_t endIndex = getEndIndex(jsonString, startIndex); + if(endIndex < 0) + return false; + + if(jsonString[startIndex] == '"') + ++startIndex; // Should not include starting " + else if(jsonString[startIndex] == '{') + ++endIndex; // Should include ending } + else + assert(false && F("Illegal JSON starting character!")); + + value = jsonString.substring(startIndex, endIndex); + return true; + } + + bool decode(const String &jsonString, const String &valueIdentifier, uint32_t &value) + { + String jsonValue; + bool decoded = decode(jsonString, valueIdentifier, jsonValue); + + if(decoded) + value = strtoul(jsonValue.c_str(), nullptr, 0); // strtoul stops reading input when an invalid character is discovered. + + return decoded; + } + + bool decodeRadix(const String &jsonString, const String &valueIdentifier, uint64_t &value, const uint8_t radix) + { + String jsonValue; + bool decoded = decode(jsonString, valueIdentifier, jsonValue); + + if(decoded) + value = TypeCast::stringToUint64(jsonValue, radix); + + return decoded; + } + + + + // TODO: Move to encryptedEspnow class? bool verifyEncryptionRequestHmac(const String &encryptionRequestHmacMessage, const uint8_t *requesterStaMac, const uint8_t *requesterApMac, const uint8_t *hashKey, const uint8_t hashKeyLength) { @@ -86,7 +198,7 @@ namespace JsonTranslator String hmac; if(getHmac(encryptionRequestHmacMessage, hmac)) { - int32_t hmacStartIndex = encryptionRequestHmacMessage.indexOf(FPSTR(jsonHmac)); + int32_t hmacStartIndex = encryptionRequestHmacMessage.indexOf(String('"') + FPSTR(jsonHmac) + F("\":")); if(hmacStartIndex < 0) return false; @@ -99,180 +211,79 @@ namespace JsonTranslator return false; } - - int32_t getStartIndex(const String &jsonString, const String &valueIdentifier, const int32_t searchStartIndex) - { - int32_t startIndex = jsonString.indexOf(valueIdentifier, searchStartIndex); - if(startIndex < 0) - return startIndex; - - startIndex += valueIdentifier.length() + 1; // Do not include valueIdentifier and initial quotation mark - return startIndex; - } - - int32_t getEndIndex(const String &jsonString, const int32_t searchStartIndex) - { - int32_t endIndex = jsonString.indexOf(',', searchStartIndex); - if(endIndex < 0) - endIndex = jsonString.indexOf('}', searchStartIndex); - - endIndex -= 1; // End index will be at the character after the closing quotation mark, so need to subtract 1. - - return endIndex; - } bool getConnectionState(const String &jsonString, String &result) { - int32_t startIndex = jsonString.indexOf(FPSTR(jsonConnectionState)); - if(startIndex < 0) - return false; - - int32_t endIndex = jsonString.indexOf('}'); - if(endIndex < 0) - return false; - - result = jsonString.substring(startIndex, endIndex + 1); - return true; + return decode(jsonString, FPSTR(jsonConnectionState), result); } bool getPassword(const String &jsonString, String &result) { - int32_t startIndex = getStartIndex(jsonString, FPSTR(jsonPassword)); - if(startIndex < 0) - return false; - - int32_t endIndex = getEndIndex(jsonString, startIndex); - if(endIndex < 0) - return false; - - result = jsonString.substring(startIndex, endIndex); - return true; + return decode(jsonString, FPSTR(jsonPassword), result); } bool getOwnSessionKey(const String &jsonString, uint64_t &result) { - int32_t startIndex = getStartIndex(jsonString, FPSTR(jsonOwnSessionKey)); - if(startIndex < 0) - return false; - - int32_t endIndex = getEndIndex(jsonString, startIndex); - if(endIndex < 0) - return false; - - result = TypeCast::stringToUint64(jsonString.substring(startIndex, endIndex)); - return true; + return decodeRadix(jsonString, FPSTR(jsonOwnSessionKey), result); } bool getPeerSessionKey(const String &jsonString, uint64_t &result) { - int32_t startIndex = getStartIndex(jsonString, FPSTR(jsonPeerSessionKey)); - if(startIndex < 0) - return false; - - int32_t endIndex = getEndIndex(jsonString, startIndex); - if(endIndex < 0) - return false; - - result = TypeCast::stringToUint64(jsonString.substring(startIndex, endIndex)); - return true; + return decodeRadix(jsonString, FPSTR(jsonPeerSessionKey), result); } bool getPeerStaMac(const String &jsonString, uint8_t *resultArray) { - int32_t startIndex = getStartIndex(jsonString, FPSTR(jsonPeerStaMac)); - if(startIndex < 0) - return false; - - int32_t endIndex = getEndIndex(jsonString, startIndex); - if(endIndex < 0 || endIndex - startIndex != 12) // Mac String is always 12 characters long - return false; - - TypeCast::stringToMac(jsonString.substring(startIndex, endIndex), resultArray); - return true; + return getMac(jsonString, FPSTR(jsonPeerStaMac), resultArray); } bool getPeerApMac(const String &jsonString, uint8_t *resultArray) - { - int32_t startIndex = getStartIndex(jsonString, FPSTR(jsonPeerApMac)); - if(startIndex < 0) - return false; - - int32_t endIndex = getEndIndex(jsonString, startIndex); - if(endIndex < 0 || endIndex - startIndex != 12) // Mac String is always 12 characters long - return false; - - TypeCast::stringToMac(jsonString.substring(startIndex, endIndex), resultArray); - return true; + { + return getMac(jsonString, FPSTR(jsonPeerApMac), resultArray); } bool getDuration(const String &jsonString, uint32_t &result) { - int32_t startIndex = getStartIndex(jsonString, FPSTR(jsonDuration)); - if(startIndex < 0) - return false; - - result = strtoul(jsonString.substring(startIndex).c_str(), nullptr, 0); // strtoul stops reading input when an invalid character is discovered. - return true; + return decode(jsonString, FPSTR(jsonDuration), result); } bool getNonce(const String &jsonString, String &result) { - int32_t startIndex = getStartIndex(jsonString, FPSTR(jsonNonce)); - if(startIndex < 0) - return false; - - int32_t endIndex = getEndIndex(jsonString, startIndex); - if(endIndex < 0) - return false; - - result = jsonString.substring(startIndex, endIndex); - return true; + return decode(jsonString, FPSTR(jsonNonce), result); } bool getHmac(const String &jsonString, String &result) { - int32_t startIndex = getStartIndex(jsonString, FPSTR(jsonHmac)); - if(startIndex < 0) - return false; - - int32_t endIndex = getEndIndex(jsonString, startIndex); - if(endIndex < 0) - return false; - - result = jsonString.substring(startIndex, endIndex); - return true; + return decode(jsonString, FPSTR(jsonHmac), result); } bool getDesync(const String &jsonString, bool &result) { - int32_t startIndex = getStartIndex(jsonString, FPSTR(jsonDesync)); - if(startIndex < 0) - return false; + String jsonValue; + bool decoded = decode(jsonString, FPSTR(jsonDesync), jsonValue); - result = bool(strtoul(jsonString.substring(startIndex).c_str(), nullptr, 0)); // strtoul stops reading input when an invalid character is discovered. - return true; + if(decoded) + result = bool(strtoul(jsonValue.c_str(), nullptr, 0)); // strtoul stops reading input when an invalid character is discovered. + + return decoded; } bool getUnsynchronizedMessageID(const String &jsonString, uint32_t &result) { - int32_t startIndex = getStartIndex(jsonString, FPSTR(jsonUnsynchronizedMessageID)); - if(startIndex < 0) - return false; - - result = strtoul(jsonString.substring(startIndex).c_str(), nullptr, 0); // strtoul stops reading input when an invalid character is discovered. - return true; + return decode(jsonString, FPSTR(jsonUnsynchronizedMessageID), result); } bool getMeshMessageCount(const String &jsonString, uint16_t &result) { - int32_t startIndex = getStartIndex(jsonString, FPSTR(jsonMeshMessageCount)); - if(startIndex < 0) - return false; + uint32_t longResult = 0; + bool decoded = decode(jsonString, FPSTR(jsonMeshMessageCount), longResult); - uint32_t longResult = strtoul(jsonString.substring(startIndex).c_str(), nullptr, 0); // strtoul stops reading input when an invalid character is discovered. - assert(longResult <= 65535); // Must fit within uint16_t - - result = longResult; - return true; + if(longResult > 65535) // Must fit within uint16_t + decoded = false; + + if(decoded) + result = longResult; + + return decoded; } } diff --git a/libraries/ESP8266WiFiMesh/src/JsonTranslator.h b/libraries/ESP8266WiFiMesh/src/JsonTranslator.h index 7576dcb53..c7d022ea4 100644 --- a/libraries/ESP8266WiFiMesh/src/JsonTranslator.h +++ b/libraries/ESP8266WiFiMesh/src/JsonTranslator.h @@ -26,34 +26,29 @@ #define __ESPNOWJSONTRANSLATOR_H__ #include +#include namespace JsonTranslator { - constexpr char jsonConnectionState[] PROGMEM = "{\"connectionState\":{"; - constexpr char jsonPassword[] PROGMEM = "\"password\":"; - constexpr char jsonOwnSessionKey[] PROGMEM = "\"ownSK\":"; - constexpr char jsonPeerSessionKey[] PROGMEM = "\"peerSK\":"; - constexpr char jsonPeerStaMac[] PROGMEM = "\"peerStaMac\":"; - constexpr char jsonPeerApMac[] PROGMEM = "\"peerApMac\":"; - constexpr char jsonDuration[] PROGMEM = "\"duration\":"; - constexpr char jsonNonce[] PROGMEM = "\"nonce\":"; - constexpr char jsonHmac[] PROGMEM = "\"hmac\":"; - constexpr char jsonDesync[] PROGMEM = "\"desync\":"; - constexpr char jsonUnsynchronizedMessageID[] PROGMEM = "\"unsyncMsgID\":"; - constexpr char jsonMeshMessageCount[] PROGMEM = "\"meshMsgCount\":"; - - String createJsonPair(const String &valueIdentifier, const String &value); - String createJsonEndPair(const String &valueIdentifier, const String &value); - - String createEncryptedConnectionInfo(const String &infoHeader, const String &requestNonce, const String &authenticationPassword, const uint64_t ownSessionKey, const uint64_t peerSessionKey); - String createEncryptionRequestIntro(const String &requestHeader, const uint32_t duration = 0); - String createEncryptionRequestEnding(const String &requestNonce); - String createEncryptionRequestHmacMessage(const String &requestHeader, const String &requestNonce, const uint8_t *hashKey, const uint8_t hashKeyLength, const uint32_t duration = 0); - - bool verifyEncryptionRequestHmac(const String &encryptionRequestHmacMessage, const uint8_t *requesterStaMac, const uint8_t *requesterApMac, const uint8_t *hashKey, const uint8_t hashKeyLength); + constexpr char jsonConnectionState[] PROGMEM = "connectionState"; + constexpr char jsonMeshState[] PROGMEM = "meshState"; + constexpr char jsonPassword[] PROGMEM = "password"; + constexpr char jsonOwnSessionKey[] PROGMEM = "ownSK"; + constexpr char jsonPeerSessionKey[] PROGMEM = "peerSK"; + constexpr char jsonPeerStaMac[] PROGMEM = "peerStaMac"; + constexpr char jsonPeerApMac[] PROGMEM = "peerApMac"; + constexpr char jsonDuration[] PROGMEM = "duration"; + constexpr char jsonNonce[] PROGMEM = "nonce"; + constexpr char jsonHmac[] PROGMEM = "hmac"; + constexpr char jsonDesync[] PROGMEM = "desync"; + constexpr char jsonUnsynchronizedMessageID[] PROGMEM = "unsyncMsgID"; + constexpr char jsonMeshMessageCount[] PROGMEM = "meshMsgCount"; + constexpr char jsonArguments[] PROGMEM = "arguments"; + /** * Provides the index within jsonString where the value of valueIdentifier starts. + * Note that including " within a JSON string value will result in errors. * * @param jsonString The String to search within. * @param valueIdentifier The identifier to search for. @@ -64,14 +59,86 @@ namespace JsonTranslator int32_t getStartIndex(const String &jsonString, const String &valueIdentifier, const int32_t searchStartIndex = 0); /** - * Provides the index within jsonString where the next JSON termination character (',' or '}') is found, starting from searchStartIndex. + * Provides the index within jsonString where the JSON object or JSON string value ends, starting the search from searchStartIndex. + * Note that including " within a JSON string value will result in errors. + * + * The character at searchStartIndex must be either " (for a string) or { (for an object), otherwise the search fails. * * @param jsonString The String to search within. - * @param searchStartIndex The index of jsonString where the search will start. + * @param searchStartIndex The index of jsonString where the search will start. The index position should contain either " or {. * - * @return An int32_t containing the index within jsonString where the next JSON termination character is found, or a negative value if no such character was found. + * @return An int32_t containing the index within jsonString where the JSON string/object ends, or a negative value if no such character was found. */ int32_t getEndIndex(const String &jsonString, const int32_t searchStartIndex); + + /* + * Create a JSON String based on the identifiers and values given. + * + * Assumes all values are either strings or JSON objects. A value is interpreted as a JSON object if it starts with { + * Assumes all identifiers are strings. + * + * @param identifiersAndValues Any even number of String arguments. It is assumed that the identifiers and values are given in an alternating manner, as in encode({Identifier1, Value1, Identifier2, Value2, ...}) + */ + String encode(std::initializer_list identifiersAndValues); + + /* + * Create a JSON String based on the identifiers and values given. + * + * Does not make any assumptions regarding value types. " must be added manually around string values. + * Useful for example if your JSON values can contain starting { characters, since the regular encode() will then interpret them as JSON objects. + * Assumes all identifiers are strings. + * + * @param identifiersAndValues Any even number of String arguments. It is assumed that the identifiers and values are given in an alternating manner, as in encodeLiterally({Identifier1, Value1, Identifier2, Value2, ...}) + */ + String encodeLiterally(std::initializer_list identifiersAndValues); + + /* + * Get a value from a JSON String. + * Assumes all values are either JSON strings ( starting with " ) or JSON objects ( starting with { ). + * + * Note that including " within a JSON string value will result in errors. + * Escape characters are not supported at this moment, since we do not want string length modification to occur during ESP-NOW protocol transmissions. + * + * @param jsonString The String to search within. + * @param valueIdentifier The identifier to search for. + * @param value The String variable to put the result in. + * + * @return True if a value was found. False otherwise. The value argument is not modified if false is returned. + */ + bool decode(const String &jsonString, const String &valueIdentifier, String &value); + + /* + * Get a value from a JSON String. + * Assumes all values are stored as strings in standard C-format (i.e. decimal by default). + * + * Note that including " within a JSON string value will result in errors. + * Escape characters are not supported at this moment, since we do not want string length modification to occur during ESP-NOW protocol transmissions. + * + * @param jsonString The String to search within. + * @param valueIdentifier The identifier to search for. + * @param value The uint32_t variable to put the result in. + * + * @return True if a value was found. False otherwise. The value argument is not modified if false is returned. + */ + bool decode(const String &jsonString, const String &valueIdentifier, uint32_t &value); + + /* + * Get a value from a JSON String. + * Assumes all values are stored as strings encoded in the specified radix. Hexadecimal encoding is the default. + * + * Note that including " within a JSON string value will result in errors. + * Escape characters are not supported at this moment, since we do not want string length modification to occur during ESP-NOW protocol transmissions. + * + * @param jsonString The String to search within. + * @param valueIdentifier The identifier to search for. + * @param value The uint64_t variable to put the result in. + * @param radix The base to use when converting the string value to uint64_t. Must be between 2 and 36. + * + * @return True if a value was found. False otherwise. The value argument is not modified if false is returned. + */ + bool decodeRadix(const String &jsonString, const String &valueIdentifier, uint64_t &value, const uint8_t radix = 16); + + bool verifyEncryptionRequestHmac(const String &encryptionRequestHmacMessage, const uint8_t *requesterStaMac, const uint8_t *requesterApMac, const uint8_t *hashKey, const uint8_t hashKeyLength); bool getConnectionState(const String &jsonString, String &result); /** diff --git a/libraries/ESP8266WiFiMesh/src/MeshBackendBase.cpp b/libraries/ESP8266WiFiMesh/src/MeshBackendBase.cpp index c6ef32b6d..ee3fce447 100644 --- a/libraries/ESP8266WiFiMesh/src/MeshBackendBase.cpp +++ b/libraries/ESP8266WiFiMesh/src/MeshBackendBase.cpp @@ -203,6 +203,7 @@ String MeshBackendBase::getNodeID() const {return getSSIDSuffix();} void MeshBackendBase::setMeshPassword(const String &newMeshPassword) { assert(8 <= newMeshPassword.length() && newMeshPassword.length() <= 64); // Limited by the ESP8266 API. + assert(newMeshPassword.indexOf('"') == -1); // " is not allowed in passwords to allow for easier JSON parsing and predictable password length (no need for extra escape characters). _meshPassword = newMeshPassword; diff --git a/libraries/ESP8266WiFiMesh/src/MeshBackendBase.h b/libraries/ESP8266WiFiMesh/src/MeshBackendBase.h index 2d9da7a67..1f39d34ac 100644 --- a/libraries/ESP8266WiFiMesh/src/MeshBackendBase.h +++ b/libraries/ESP8266WiFiMesh/src/MeshBackendBase.h @@ -194,7 +194,7 @@ public: * Will also change the setting for the active AP (via an AP restart) * if this MeshBackendBase instance is the current AP controller. * - * @param newMeshPassword The password to use. + * @param newMeshPassword The password to use. Must be between 8 and 64 characters long. " is an illegal character because of JSON parsing requirements. */ void setMeshPassword(const String &newMeshPassword); String getMeshPassword() const; diff --git a/libraries/ESP8266WiFiMesh/src/Serializer.cpp b/libraries/ESP8266WiFiMesh/src/Serializer.cpp new file mode 100644 index 000000000..198427efd --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/Serializer.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2020 Anders Löfgren + * + * License (MIT license): + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "Serializer.h" +#include "JsonTranslator.h" +#include "TypeConversionFunctions.h" +#include "MeshCryptoInterface.h" + +namespace +{ + namespace TypeCast = MeshTypeConversionFunctions; + + String createJsonEndPair(const String &valueIdentifier, const String &value) + { + const String q = String('"'); + return q + valueIdentifier + q + ':' + q + value + F("\"}}"); + } +} + +namespace Serializer +{ + /* + * NOTE: The internal states may be changed in future updates, so the function signatures here are not guaranteed to be stable. + */ + + String serializeMeshState(const String &unsyncMsgID, const String &meshMsgCount) + { + using namespace JsonTranslator; + + // Returns: {"meshState":{"connectionState":{"unsyncMsgID":"123"},"meshMsgCount":"123"}} + return encode({FPSTR(jsonMeshState), encode({FPSTR(jsonConnectionState), encode({FPSTR(jsonUnsynchronizedMessageID), unsyncMsgID}), FPSTR(jsonMeshMessageCount), meshMsgCount})}); + } + + String serializeUnencryptedConnection(const String &unsyncMsgID) + { + using namespace JsonTranslator; + + // Returns: {"connectionState":{"unsyncMsgID":"123"}} + return encode({FPSTR(jsonConnectionState), encode({FPSTR(jsonUnsynchronizedMessageID), unsyncMsgID})}); + } + + String serializeEncryptedConnection(const String &duration, const String &desync, const String &ownSK, const String &peerSK, const String &peerStaMac, const String &peerApMac) + { + using namespace JsonTranslator; + + if(duration.isEmpty()) + { + // Returns: {"connectionState":{"desync":"0","ownSK":"1A2","peerSK":"3B4","peerStaMac":"F2","peerApMac":"E3"}} + return encode({FPSTR(jsonConnectionState), encode({FPSTR(jsonDesync), desync, FPSTR(jsonOwnSessionKey), ownSK, FPSTR(jsonPeerSessionKey), peerSK, + FPSTR(jsonPeerStaMac), peerStaMac, FPSTR(jsonPeerApMac), peerApMac})}); + } + + // Returns: {"connectionState":{"duration":"123","desync":"0","ownSK":"1A2","peerSK":"3B4","peerStaMac":"F2","peerApMac":"E3"}} + return encode({FPSTR(jsonConnectionState), encode({FPSTR(jsonDuration), duration, FPSTR(jsonDesync), desync, FPSTR(jsonOwnSessionKey), ownSK, FPSTR(jsonPeerSessionKey), peerSK, + FPSTR(jsonPeerStaMac), peerStaMac, FPSTR(jsonPeerApMac), peerApMac})}); + } + + String createEncryptedConnectionInfo(const String &infoHeader, const String &requestNonce, const String &authenticationPassword, const uint64_t ownSessionKey, const uint64_t peerSessionKey) + { + using namespace JsonTranslator; + + const String q = String('"'); + + // Returns: infoHeader{"arguments":{"nonce":"1F2","password":"abc","ownSK":"3B4","peerSK":"1A2"}} + return + infoHeader + + encode({FPSTR(jsonArguments), + encodeLiterally({FPSTR(jsonNonce), q + requestNonce + q, + FPSTR(jsonPassword), q + authenticationPassword + q, + FPSTR(jsonOwnSessionKey), q + TypeCast::uint64ToString(peerSessionKey) + q, // Exchanges session keys since it should be valid for the receiver. + FPSTR(jsonPeerSessionKey), q + TypeCast::uint64ToString(ownSessionKey) + q})}); + } + + String createEncryptionRequestHmacMessage(const String &requestHeader, const String &requestNonce, const uint8_t *hashKey, const uint8_t hashKeyLength, const uint32_t duration) + { + using namespace JsonTranslator; + + String mainMessage = requestHeader; + + if(requestHeader == FPSTR(EspnowProtocolInterpreter::temporaryEncryptionRequestHeader)) + { + mainMessage += encode({FPSTR(jsonArguments), encode({FPSTR(jsonDuration), String(duration), FPSTR(jsonNonce), requestNonce})}); + } + else + { + mainMessage += encode({FPSTR(jsonArguments), encode({FPSTR(jsonNonce), requestNonce})}); + } + + // We need to have an open JSON object so we can add the HMAC later. + mainMessage.remove(mainMessage.length() - 2); + mainMessage += ','; + + uint8_t staMac[6] {0}; + uint8_t apMac[6] {0}; + String requesterStaApMac = TypeCast::macToString(WiFi.macAddress(staMac)) + TypeCast::macToString(WiFi.softAPmacAddress(apMac)); + String hmac = MeshCryptoInterface::createMeshHmac(requesterStaApMac + mainMessage, hashKey, hashKeyLength); + + // Returns: requestHeader{"arguments":{"duration":"123","nonce":"1F2","hmac":"3B4"}} + return mainMessage + createJsonEndPair(FPSTR(jsonHmac), hmac); + } +} diff --git a/libraries/ESP8266WiFiMesh/src/Serializer.h b/libraries/ESP8266WiFiMesh/src/Serializer.h new file mode 100644 index 000000000..d5d32dbaa --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/Serializer.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2020 Anders Löfgren + * + * License (MIT license): + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef __ESP8266MESHSERIALIZER_H__ +#define __ESP8266MESHSERIALIZER_H__ + +#include + +namespace Serializer +{ + /* + * NOTE: The internal states may be changed in future updates, so the function signatures here are not guaranteed to be stable. + */ + + String serializeMeshState(const String &unsyncMsgID, const String &meshMsgCount); + String serializeUnencryptedConnection(const String &unsyncMsgID); + String serializeEncryptedConnection(const String &duration, const String &desync, const String &ownSK, const String &peerSK, const String &peerStaMac, const String &peerApMac); + + String createEncryptedConnectionInfo(const String &infoHeader, const String &requestNonce, const String &authenticationPassword, const uint64_t ownSessionKey, const uint64_t peerSessionKey); + String createEncryptionRequestHmacMessage(const String &requestHeader, const String &requestNonce, const uint8_t *hashKey, const uint8_t hashKeyLength, const uint32_t duration = 0); +} + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/TypeConversionFunctions.h b/libraries/ESP8266WiFiMesh/src/TypeConversionFunctions.h index 4c678a4ec..de52f3e91 100644 --- a/libraries/ESP8266WiFiMesh/src/TypeConversionFunctions.h +++ b/libraries/ESP8266WiFiMesh/src/TypeConversionFunctions.h @@ -42,7 +42,7 @@ namespace MeshTypeConversionFunctions * @param base The radix to convert "number" into. Must be between 2 and 36. * @return A string of "number" encoded in radix "base". */ - String uint64ToString(uint64_t number, const byte base = 16); + String uint64ToString(uint64_t number, const uint8_t base = 16); /** * Note that using base 10 instead of 16 increases conversion time by roughly a factor of 2, due to unfavourable 64-bit arithmetic. @@ -52,7 +52,7 @@ namespace MeshTypeConversionFunctions * @param base The radix of "string". Must be between 2 and 36. * @return A uint64_t of the string, using radix "base" during decoding. */ - uint64_t stringToUint64(const String &string, const byte base = 16); + uint64_t stringToUint64(const String &string, const uint8_t base = 16); /** * Convert the contents of a uint8_t array to a String in HEX format. The resulting String starts from index 0 of the array.