1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-07-27 18:02:17 +03:00

- Make it possible to transfer Strings containing null values via ESP-NOW and FloodingMesh.

- Add uint8ArrayToMultiString and bufferedUint8ArrayToMultiString TypeConversionFunctions to facilitate transfer of Strings containing null values.

- Add HKDF to CryptoInterface.

- Add ChaCha20 + Poly1305 AEAD to CryptoInterface.

- Add customizable nonce generator to CryptoInterface.

- Add ability to automatically encrypt/decrypt ESP-NOW messages via AEAD (ChaCha20 + Poly1305), independent from encrypted ESP-NOW connections.

- Greatly improve performance of incrementSessionKey, espnowGetMessageID, espnowSetMessageID and all non-template TypeConversionFunctions. The average performance increase is roughly a factor 5. Fun fact: Printing a MAC to a HEX String is now over twice as fast when using TypeConversionFunctions compared to using standard functionality like sprintf.

- Add uint64ToUint8Array and uint8ArrayToUint64 TypeConversionFunctions.

- Make it possible to use String values as ESP-NOW and FloodingMesh key seeds, instead of just requiring plain key arrays.

- Add customizable responseTransmittedHook to sendEspnowResponses.

- Add _responsesToSendMutex to make the new responseTransmittedHook safe to use.

- Remove verboseModePrinting from sendPeerRequestConfirmations method to reduce performance variations.

- Fix faulty messageID generation in FloodingMesh.

- Make assert checks more complete and easier to understand in the setMetadataDelimiter method of FloodingMesh.

- Rename EspnowEncryptionKey to EspnowEncryptedConnectionKey since there are now multiple encryption keys.

- Rename acceptsUnencryptedRequests to acceptsUnverifiedRequests, unencryptedMessageID to unsynchronizedMessageID, receivedEncryptedMessage to receivedEncryptedTransmission, since there are now multiple modes of encryption.

- Rename resultArrayLength to outputLength in CryptoInterface and remove its value restrictions in order to match the BearSSL functionality.

- Improve performance of FloodingMesh::encryptedBroadcast.

- Rename FloodingMesh methods maxUnencryptedMessageSize/maxEncryptedMessageSize to maxUnencryptedMessageLength/maxEncryptedMessageLength, so that String length naming is consistent within the library.

- Update examples to illustrate the new features.

- Improve comments.
This commit is contained in:
Anders
2019-12-04 02:30:16 +01:00
parent 2fef67dcb0
commit 962a23d253
20 changed files with 1202 additions and 318 deletions

View File

@ -19,10 +19,11 @@ const char exampleWiFiPassword[] PROGMEM = "ChangeThisWiFiPassword_TODO"; // The
// 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.
uint8_t espnowEncryptionKey[16] = {0x33, 0x44, 0x33, 0x44, 0x33, 0x44, 0x33, 0x44, // This is the key for encrypting transmissions.
0x33, 0x44, 0x33, 0x44, 0x33, 0x44, 0x32, 0x11
};
uint8_t espnowEncryptionKok[16] = {0x22, 0x44, 0x33, 0x44, 0x33, 0x44, 0x33, 0x44, // This is the key for encrypting the encryption key.
// Note that it is also possible to use Strings as key seeds instead of arrays.
uint8_t espnowEncryptedConnectionKey[16] = {0x33, 0x44, 0x33, 0x44, 0x33, 0x44, 0x33, 0x44, // This is the key for encrypting transmissions of encrypted connections.
0x33, 0x44, 0x33, 0x44, 0x33, 0x44, 0x32, 0x11
};
uint8_t espnowEncryptionKok[16] = {0x22, 0x44, 0x33, 0x44, 0x33, 0x44, 0x33, 0x44, // This is the key for encrypting the encrypted connection key.
0x33, 0x44, 0x33, 0x44, 0x33, 0x44, 0x32, 0x33
};
uint8_t espnowHashKey[16] = {0xEF, 0x44, 0x33, 0x0C, 0x33, 0x44, 0xFE, 0x44, // This is the secret key used for HMAC during encrypted connection requests.
@ -40,7 +41,7 @@ void networkFilter(int numberOfNetworks, MeshBackendBase &meshInstance);
bool broadcastFilter(String &firstTransmission, EspnowMeshBackend &meshInstance);
/* Create the mesh node object */
EspnowMeshBackend espnowNode = EspnowMeshBackend(manageRequest, manageResponse, networkFilter, broadcastFilter, FPSTR(exampleWiFiPassword), espnowEncryptionKey, espnowHashKey, FPSTR(exampleMeshName), uint64ToString(ESP.getChipId()), true);
EspnowMeshBackend espnowNode = EspnowMeshBackend(manageRequest, manageResponse, networkFilter, broadcastFilter, FPSTR(exampleWiFiPassword), espnowEncryptedConnectionKey, espnowHashKey, FPSTR(exampleMeshName), uint64ToString(ESP.getChipId()), true);
/**
Callback for when other nodes send you a request
@ -57,8 +58,8 @@ String manageRequest(const String &request, MeshBackendBase &meshInstance) {
// To get the actual class of the polymorphic meshInstance, do as follows (meshBackendCast replaces dynamic_cast since RTTI is disabled)
if (EspnowMeshBackend *espnowInstance = meshBackendCast<EspnowMeshBackend *>(&meshInstance)) {
String messageEncrypted = espnowInstance->receivedEncryptedMessage() ? ", Encrypted" : ", Unencrypted";
Serial.print("ESP-NOW (" + espnowInstance->getSenderMac() + messageEncrypted + "): ");
String transmissionEncrypted = espnowInstance->receivedEncryptedTransmission() ? ", Encrypted transmission" : ", Unencrypted transmission";
Serial.print("ESP-NOW (" + espnowInstance->getSenderMac() + transmissionEncrypted + "): ");
} else if (TcpIpMeshBackend *tcpIpInstance = meshBackendCast<TcpIpMeshBackend *>(&meshInstance)) {
(void)tcpIpInstance; // This is useful to remove a "unused parameter" compiler warning. Does nothing else.
Serial.print("TCP/IP: ");
@ -69,6 +70,7 @@ String manageRequest(const String &request, MeshBackendBase &meshInstance) {
/* Print out received message */
// Only show first 100 characters because printing a large String takes a lot of time, which is a bad thing for a callback function.
// If you need to print the whole String it is better to store it and print it in the loop() later.
// Note that request.substring will not work as expected if the String contains null values as data.
Serial.print("Request received: ");
Serial.println(request.substring(0, 100));
@ -88,8 +90,8 @@ transmission_status_t manageResponse(const String &response, MeshBackendBase &me
// To get the actual class of the polymorphic meshInstance, do as follows (meshBackendCast replaces dynamic_cast since RTTI is disabled)
if (EspnowMeshBackend *espnowInstance = meshBackendCast<EspnowMeshBackend *>(&meshInstance)) {
String messageEncrypted = espnowInstance->receivedEncryptedMessage() ? ", Encrypted" : ", Unencrypted";
Serial.print("ESP-NOW (" + espnowInstance->getSenderMac() + messageEncrypted + "): ");
String transmissionEncrypted = espnowInstance->receivedEncryptedTransmission() ? ", Encrypted transmission" : ", Unencrypted transmission";
Serial.print("ESP-NOW (" + espnowInstance->getSenderMac() + transmissionEncrypted + "): ");
} else if (TcpIpMeshBackend *tcpIpInstance = meshBackendCast<TcpIpMeshBackend *>(&meshInstance)) {
Serial.print("TCP/IP: ");
@ -106,6 +108,7 @@ transmission_status_t manageResponse(const String &response, MeshBackendBase &me
/* Print out received message */
// Only show first 100 characters because printing a large String takes a lot of time, which is a bad thing for a callback function.
// If you need to print the whole String it is better to store it and print it in the loop() later.
// Note that response.substring will not work as expected if the String contains null values as data.
Serial.print(F("Response received: "));
Serial.println(response.substring(0, 100));
@ -169,7 +172,9 @@ bool broadcastFilter(String &firstTransmission, EspnowMeshBackend &meshInstance)
return false; // Broadcast is for another mesh network
} else {
// Remove metadata from message and mark as accepted broadcast.
firstTransmission = firstTransmission.substring(metadataEndIndex + 1);
// Note that when you modify firstTransmission it is best to avoid using substring or other String methods that rely on null values for String length determination.
// Otherwise your broadcasts cannot include null values in the message bytes.
firstTransmission.remove(0, metadataEndIndex + 1);
return true;
}
}
@ -193,6 +198,31 @@ bool exampleTransmissionOutcomesUpdateHook(MeshBackendBase &meshInstance) {
return true;
}
/**
Once passed to the setResponseTransmittedHook method of the ESP-NOW backend,
this function will be called after each successful ESP-NOW response transmission, just before the response is removed from the waiting list.
If a particular response is not sent, there will be no function call for it.
Only the hook of the EspnowMeshBackend instance that is getEspnowRequestManager() will be called.
@param response The sent response.
@param recipientMac The MAC address the response was sent to.
@param responseIndex The index of the response in the waiting list.
@param meshInstance The EspnowMeshBackend instance that called the function.
@return True if the response transmission process should continue with the next response in the waiting list.
False if the response transmission process should stop after removing the just sent response from the waiting list.
*/
bool exampleResponseTransmittedHook(const String &response, const uint8_t *recipientMac, uint32_t responseIndex, EspnowMeshBackend &meshInstance) {
// Currently this is exactly the same as the default hook, but you can modify it to alter the behaviour of sendEspnowResponses.
(void)response; // This is useful to remove a "unused parameter" compiler warning. Does nothing else.
(void)recipientMac;
(void)responseIndex;
(void)meshInstance;
return true;
}
void setup() {
// Prevents the flash memory from being worn out, see: https://github.com/esp8266/Arduino/issues/1054 .
// This will however delay node WiFi start-up by about 700 ms. The delay is 900 ms if we otherwise would have stored the WiFi network we want to connect to.
@ -222,10 +252,10 @@ void setup() {
// Note: This changes the Kok for all EspnowMeshBackend instances on this ESP8266.
// Encrypted connections added before the Kok change will retain their old Kok.
// Both Kok and encryption key must match in an encrypted connection pair for encrypted communication to be possible.
// Both Kok and encrypted connection key must match in an encrypted connection pair for encrypted communication to be possible.
// Otherwise the transmissions will never reach the recipient, even though acks are received by the sender.
EspnowMeshBackend::setEspnowEncryptionKok(espnowEncryptionKok);
espnowNode.setEspnowEncryptionKey(espnowEncryptionKey);
espnowNode.setEspnowEncryptedConnectionKey(espnowEncryptedConnectionKey);
// Makes it possible to find the node through scans, and also makes it possible to recover from an encrypted connection where only the other node is encrypted.
// Note that only one AP can be active at a time in total, and this will always be the one which was last activated.
@ -238,6 +268,21 @@ void setup() {
espnowNode.setMessage(String(F("Hello world request #")) + String(requestNumber) + String(F(" from ")) + espnowNode.getMeshName() + espnowNode.getNodeID() + String(F(".")));
espnowNode.setTransmissionOutcomesUpdateHook(exampleTransmissionOutcomesUpdateHook);
espnowNode.setResponseTransmittedHook(exampleResponseTransmittedHook);
// In addition to using encrypted ESP-NOW connections the framework can also send automatically encrypted messages (AEAD) over both encrypted and unencrypted connections.
// Using AEAD will only encrypt the message content, not the transmission metadata.
// The AEAD encryption does not require any pairing, and is thus faster for single messages than establishing a new encrypted connection before transfer.
// AEAD encryption also works with ESP-NOW broadcasts and supports an unlimited number of nodes, which is not true for encrypted connections.
// Encrypted ESP-NOW connections do however come with built in replay attack protection, which is not provided by the framework when using AEAD encryption,
// and allow EspnowProtocolInterpreter::aeadMetadataSize extra message bytes per transmission.
// Transmissions via encrypted connections are also slightly faster than via AEAD once a connection has been established.
//
// Uncomment the lines below to use automatic AEAD encryption/decryption of messages sent/received.
// All nodes this node wishes to communicate with must then also use encrypted messages with the same getEspnowMessageEncryptionKey(), or messages will not be accepted.
// Note that using AEAD encrypted messages will reduce the number of message bytes that can be transmitted.
//espnowNode.setEspnowMessageEncryptionKey("ChangeThisKeySeed_TODO"); // The message encryption key should always be set manually. Otherwise a default key (all zeroes) is used.
//espnowNode.setUseEncryptedMessages(true);
}
int32_t timeOfLastScan = -10000;