mirror of
https://github.com/esp8266/Arduino.git
synced 2025-04-19 23:22:16 +03:00
- 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.
This commit is contained in:
parent
e64125a53c
commit
2ec2679d6e
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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<String> 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<String> 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;
|
||||
}
|
||||
}
|
||||
|
@ -26,34 +26,29 @@
|
||||
#define __ESPNOWJSONTRANSLATOR_H__
|
||||
|
||||
#include <WString.h>
|
||||
#include <initializer_list>
|
||||
|
||||
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<String> 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<String> 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);
|
||||
/**
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
122
libraries/ESP8266WiFiMesh/src/Serializer.cpp
Normal file
122
libraries/ESP8266WiFiMesh/src/Serializer.cpp
Normal file
@ -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);
|
||||
}
|
||||
}
|
44
libraries/ESP8266WiFiMesh/src/Serializer.h
Normal file
44
libraries/ESP8266WiFiMesh/src/Serializer.h
Normal file
@ -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 <WString.h>
|
||||
|
||||
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
|
@ -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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user