mirror of
https://github.com/esp8266/Arduino.git
synced 2025-04-19 23:22:16 +03:00
- Use the new Crypto, TypeConversion and random() functionality added to the Arduino core, instead of the versions local to the mesh library.
- Rearrange class variables to minimize storage padding. - Add protected getters for EspnowMeshBackend and MeshBackendBase components. - Partially update README.md
This commit is contained in:
parent
595fb23128
commit
f059e57322
@ -1,52 +1,57 @@
|
||||
ESP8266 WiFi Mesh
|
||||
=================
|
||||
# ESP8266 WiFi Mesh
|
||||
|
||||
A library for turning your ESP8266 into a mesh network node.
|
||||
## Contents
|
||||
1. [Overview]
|
||||
* [How does it work?]
|
||||
2. [Tell me more]
|
||||
* [EspnowMeshBackend](#EspnowMeshBackendMore)
|
||||
* [TcpIpMeshBackend](#TcpIpMeshBackendMore)
|
||||
* [FloodingMesh](#FloodingMeshMore)
|
||||
|
||||
The library has been tested and works with Arduino core for ESP8266 version 2.6.0 (with lwIP2). It may work with earlier and later core releases, but this has not been tested during development.
|
||||
|
||||
**Note:** This mesh library has been rewritten for core release 2.6.0. The old method signatures have been retained for compatibility purposes, but will be removed in core release 3.0.0. If you are still using these old method signatures please consider migrating to the new API shown in the `EspnowMeshBackend.h` or `TcpIpMeshBackend.h` source files.
|
||||
## Overview
|
||||
|
||||
Usage
|
||||
-----
|
||||
This is a library for creating a mesh network using the ESP8266.
|
||||
|
||||
The basic operation of a mesh node is as follows:
|
||||
The library has been tested and works with Arduino core for ESP8266 version 2.7.2 (with lwIP2). It may work with earlier and later core releases, but this has not been tested during development.
|
||||
|
||||
The `attemptTransmission` method of the ESP8266WiFiMesh instance is called with a message to send to other nodes in the mesh network. If the node is already connected to an AP, the message is sent only to that AP. Otherwise a WiFi scan is performed. The scan results are sent to the `networkFilter` callback function of the ESP8266WiFiMesh instance which adds the AP:s of interest to the `connectionQueue` vector. The message is then transmitted to the networks in the `connectionQueue`, and the response from each AP is sent to the `responseHandler` callback of the ESP8266WiFiMesh instance. The outcome from each transmission attempt can be found in the `latestTransmissionOutcomes` vector.
|
||||
**Note:** This mesh library has been extensively rewritten for core release 2.7.2. The old method signatures have been retained for compatibility purposes, but will be removed in core release 3.0.0. If you are still using these old method signatures please consider migrating to the new API shown in the `EspnowMeshBackend.h` or `TcpIpMeshBackend.h` source files.
|
||||
|
||||
The node receives messages from other nodes by calling the `acceptRequest` method of the ESP8266WiFiMesh instance. These received messages are passed to the `requestHandler` callback of the mesh instance. For each received message the return value of `requestHandler` is sent to the other node as a response to the message.
|
||||
### How does it work?
|
||||
|
||||
For more details, see the included example. The main functions to modify in the example are `manageRequest` (`requestHandler`), `manageResponse` (`responseHandler`) and `networkFilter`. There is also more information to be found in the source code comments. An example is the ESP8266WiFiMesh constructor comment, which is shown below for reference:
|
||||
```
|
||||
/**
|
||||
* WiFiMesh Constructor method. Creates a WiFi Mesh Node, ready to be initialised.
|
||||
*
|
||||
* @param requestHandler The callback handler for dealing with received requests. Takes a string as an argument which
|
||||
* is the request string received from another node and returns the string to send back.
|
||||
* @param responseHandler The callback handler for dealing with received responses. Takes a string as an argument which
|
||||
* is the response string received from another node. Returns a transmission status code as a transmission_status_t.
|
||||
* @param networkFilter The callback handler for deciding which WiFi networks to connect to.
|
||||
* @param meshPassword The WiFi password for the mesh network.
|
||||
* @param meshName The name of the mesh network. Used as prefix for the node SSID and to find other network nodes in the example network filter function.
|
||||
* @param nodeID The id for this mesh node. Used as suffix for the node SSID. If set to "", the id will default to ESP.getChipId().
|
||||
* @param verboseMode Determines if we should print the events occurring in the library to Serial. Off by default.
|
||||
* @param meshWiFiChannel The WiFi channel used by the mesh network. Valid values are integers from 1 to 13. Defaults to 1.
|
||||
* WARNING: The ESP8266 has only one WiFi channel, and the the station/client mode is always prioritized for channel selection.
|
||||
* This can cause problems if several ESP8266WiFiMesh instances exist on the same ESP8266 and use different WiFi channels.
|
||||
* In such a case, whenever the station of one ESP8266WiFiMesh instance connects to an AP, it will silently force the
|
||||
* WiFi channel of any active AP on the ESP8266 to match that of the station. This will cause disconnects and possibly
|
||||
* make it impossible for other stations to detect the APs whose WiFi channels have changed.
|
||||
* @param serverPort The server port used by the AP of the ESP8266WiFiMesh instance. If multiple APs exist on a single ESP8266, each requires a separate server port.
|
||||
* If two AP:s on the same ESP8266 are using the same server port, they will not be able to have both server instances active at the same time.
|
||||
* This is managed automatically by the activateAP method.
|
||||
*
|
||||
*/
|
||||
ESP8266WiFiMesh(requestHandlerType requestHandler, responseHandlerType responseHandler, networkFilterType networkFilter,
|
||||
const String &meshPassword, const String &meshName = "MeshNode_", const String &nodeID = WIFI_MESH_EMPTY_STRING, bool verboseMode = false,
|
||||
uint8 meshWiFiChannel = 1, int serverPort = 4011);
|
||||
```
|
||||
The ESP8266 WiFi Mesh library is a cake. Metaphorically speaking. At the bottom you have the general ESP8266 Arduino core WiFi functionality. On top of this two mesh backends have been created (`EspnowMeshBackend` and `TcpIpMeshBackend`), a yummy filling that completely covers the bottom. Then at the very top over the backends is the beautiful and delicious frosting: `FloodingMesh`. `FloodingMesh` is an actual mesh network implementation that uses the `EspnowMeshBackend`.
|
||||
|
||||
### Note
|
||||
Eating the cake would typically be a process which involves all the layers, but it is completely possible to use both the EspnowMeshBackend and the TcpIpMeshBackend separately from FloodingMesh, perhaps to construct your own mesh network architecture or just to simplify the usage of TCP/IP or ESP-NOW. If you have made a nice mesh architecture with this library that you would like to share with the rest of the world, feel free to make a PR with it!
|
||||
|
||||
There is plenty of details to the operations of the library, but if you want to get started quickly you really only need to know this: In the example folder of the library there is a file called `HelloMesh.ino`. Upload it to a few ESP8266 and you have a working mesh network. Change the `useLED` variable to true if you have built-in LEDs on your ESP8266s to illustrate how the message is spread through the network. Change the `floodingMesh.broadcast` calls to modify what the mesh nodes are transmitting to each other.
|
||||
|
||||
Finally, three things are important to note:
|
||||
|
||||
1. This library uses the standard Arduino core for ESP8266 WiFi functions. Therefore, other code that also uses these WiFi functions (e.g. `WiFi.mode()`) may cause conflicts with the library, resulting in strange behaviour. See the FAQ for ideas on how to work around this.
|
||||
2. Both the `EspnowMeshBackend` and the `TcpIpMeshBackend` can be used simultaneously on the same node. However, since there is only one WiFi radio on the ESP8266, only one backend at a time will be responsible for the settings of this radio (SSID, WiFi channel etc.). The backend in control is known as the `APController` in the library. Both backends can still send messages, regardless of who is `APController`.
|
||||
3. The `MeshBackendBase`, `EspnowMeshBackend`, `TcpIpMeshBackend` and `FloodingMesh` source files are supposed to be the main front-ends of the library and are all extensively documented. If you wonder how something is working, chances are good that you will find an answer in the documentation of those files.
|
||||
|
||||
## Tell me more
|
||||
|
||||
### <a name="EspnowMeshBackendMore"></a>EspnowMeshBackend
|
||||
|
||||
#### Usage
|
||||
|
||||
The basic operation of the ESP-NOW mesh backend is as follows:
|
||||
|
||||
### <a name="TcpIpMeshBackendMore"></a>TcpIpMeshBackend
|
||||
|
||||
#### Usage
|
||||
|
||||
The basic operation of the TCP/IP mesh backend is as follows:
|
||||
|
||||
The `attemptTransmission` method of the TcpIpMeshBackend instance is called with a message to send to other nodes in the mesh network. If the node is already connected to an AP, the message is sent only to that AP. Otherwise a WiFi scan is performed. The scan results are sent to the `networkFilter` callback function of the TcpIpMeshBackend instance which adds the AP:s of interest to the `connectionQueue` vector. The message is then transmitted to the networks in the `connectionQueue`, and the response from each AP is sent to the `responseHandler` callback of the TcpIpMeshBackend instance. The outcome from each transmission attempt can be found in the `latestTransmissionOutcomes` vector.
|
||||
|
||||
The node receives messages from other TCP/IP nodes by calling the `acceptRequest` method of the TcpIpMeshBackend instance. These received messages are passed to the `requestHandler` callback of the mesh instance. For each received message the return value of `requestHandler` is sent to the other node as a response to the message. Since received ESP-NOW messages are handled via a callback, there is no need to call `acceptRequest` to receive these.
|
||||
|
||||
For more details, see the included HelloTcpIp example. The main functions to modify in the example are `manageRequest` (`requestHandler`), `manageResponse` (`responseHandler`), `networkFilter` and `exampleTransmissionOutcomesUpdateHook`. There is also much more information to be found in the source code comments.
|
||||
|
||||
#### Note
|
||||
|
||||
* This library can use static IP:s for the nodes to speed up connection times. To enable this, use the `setStaticIP` method after calling the `begin` method, as in the included example. When using static IP, the following is good to keep in mind:
|
||||
|
||||
@ -56,30 +61,35 @@ ESP8266WiFiMesh(requestHandlerType requestHandler, responseHandlerType responseH
|
||||
|
||||
Station gateway IP must match the IP for the server on the nodes. This is the default setting for the library.
|
||||
|
||||
Static IP is a global setting (for now), meaning that all ESP8266WiFiMesh instances on the same ESP8266 share the same static IP settings.
|
||||
Static IP is a global setting (for now), meaning that all TcpIpMeshBackend instances on the same ESP8266 share the same static IP settings.
|
||||
|
||||
* When Arduino core for ESP8266 version 2.4.2 or higher is used, there are optimizations available for WiFi scans and static IP use to reduce the time it takes for nodes to connect to each other. These optimizations are enabled by default. To take advantage of the static IP optimizations you also need to use lwIP2. The lwIP version can be changed in the Tools menu of Arduino IDE.
|
||||
* Scanning all WiFi channels (e.g. via the `attemptTransmission` method with the `scanAllWiFiChannels` argument set to true) will slow down scans considerably and make it more likely that existing WiFi connections will break during scans.
|
||||
|
||||
* The WiFi scan optimization mentioned above works by making WiFi scans only search through the same WiFi channel as the ESP8266WiFiMesh instance is using. If you would like to scan all WiFi channels instead, set the `scanAllWiFiChannels` argument of the `attemptTransmission` method to `true`. Note that scanning all WiFi channels will slow down scans considerably and make it more likely that existing WiFi connections will break during scans. Also note that if the ESP8266 has an active AP, that AP will switch WiFi channel to match that of any other AP the ESP8266 connects to (compare next bullet point). This can make it impossible for other nodes to detect the AP if they are scanning the wrong WiFi channel. To remedy this, force the AP back on the original channel by using the `restartAP` method of the current AP controller once the ESP8266 has disconnected from the other AP. This would typically be done like so:
|
||||
* If the ESP8266 has an active AP, that AP will switch WiFi channel to match that of any other AP the TcpIpMeshBackend of the ESP8266 connects to (compare next bullet point). This can make it impossible for other nodes to detect the AP if they are scanning the wrong WiFi channel. To remedy this, force the AP back on the original channel by using the `restartAP` method of the current AP controller once the ESP8266 has disconnected from the other AP. This would typically be done like so:
|
||||
|
||||
```
|
||||
if(ESP8266WiFiMesh *apController = ESP8266WiFiMesh::getAPController()) // Make sure apController is not nullptr
|
||||
if(MeshBackendBase *apController = MeshBackendBase::getAPController()) // Make sure apController is not nullptr
|
||||
apController->restartAP();
|
||||
```
|
||||
|
||||
* It is possible to have several ESP8266WiFiMesh instances running on every ESP8266 (e.g. to communicate with different mesh networks). However, because the ESP8266 has one WiFi radio only one AP per ESP8266 can be active at a time. Also note that if the ESP8266WiFiMesh instances use different WiFi channels, active APs are forced to use the same WiFi channel as active stations, possibly causing AP disconnections.
|
||||
* It is possible to have several TcpIpMeshBackend instances running on every ESP8266 (e.g. to communicate with different mesh networks). However, because the ESP8266 has one WiFi radio only one AP per ESP8266 can be active at a time. Also note that if the TcpIpMeshBackend instances use different WiFi channels, active APs are forced to use the same WiFi channel as active stations, possibly causing AP disconnections.
|
||||
|
||||
* While it is possible to connect to other nodes by only giving their SSID, e.g. `ESP8266WiFiMesh::connectionQueue.push_back(NetworkInfo("NodeSSID"));`, it is recommended that AP WiFi channel and AP BSSID are given as well, to minimize connection delay.
|
||||
* While it is possible to connect to other nodes by only giving their SSID, e.g. `TcpIpMeshBackend::connectionQueue().emplace_back("NodeSSID");`, it is recommended that AP WiFi channel and AP BSSID are given as well, to minimize connection delay.
|
||||
|
||||
* Also, remember to change the default mesh network WiFi password!
|
||||
|
||||
General Information
|
||||
---------------------------
|
||||
|
||||
* This library uses the standard Arduino core for ESP8266 WiFi functions. Therefore, other code that also uses these WiFi functions may cause conflicts with the library, resulting in strange behaviour.
|
||||
#### General Information
|
||||
|
||||
* By default, a maximum of 4 stations can be connected at a time to each AP. This can be changed to a value in the range 0 to 8 via the `setMaxAPStations` method. Once the max number has been reached, any other station that wants to connect will be forced to wait until an already connected station disconnects. The more stations that are connected, the more memory is required.
|
||||
|
||||
* Unlike `WiFi.mode(WIFI_AP)`, the `WiFi.mode(WIFI_AP_STA)` which is used in this library allows nodes to stay connected to an AP they connect to while in STA mode, at the same time as they can receive connections from other stations. Nodes cannot send data to an AP while in STA_AP mode though, that requires STA mode. Switching to STA mode will sometimes disconnect stations connected to the node AP (though they can request a reconnect even while the previous AP node is in STA mode).
|
||||
* Unlike `WiFi.mode(WIFI_AP)`, the `WiFi.mode(WIFI_AP_STA)` which is used in this library allows TCP/IP nodes to stay connected to an AP they connect to while in STA mode, at the same time as they can receive connections from other stations. Nodes cannot send data to an AP while in STA_AP mode though, that requires STA mode. Switching to STA mode will sometimes disconnect stations connected to the node AP (though they can request a reconnect even while the previous AP node is in STA mode).
|
||||
|
||||
* Scanning for networks (e.g. via the `attemptTransmission` method) without the WiFi scan optimizations for core version 2.4.2 mentioned above, causes the WiFi radio to cycle through all WiFi channels which means existing WiFi connections are likely to break or work poorly if done frequently.
|
||||
### <a name="FloodingMeshMore"></a>FloodingMesh
|
||||
|
||||
The FloodingMesh exclusively uses the `EspnowMeshBackend`. The mesh network size is only limited by available MAC addresses, so the maximum is (2^48)/2 = 140 trillion give or take. However, the maximum throughput of the FloodingMesh is around 100 messages per second with 234 bytes per message, so using the maximum number of nodes is not recommended in most cases.
|
||||
|
||||
As the name implies, FloodingMesh is a simple flooding mesh architecture, which means it stores no mesh network routing data in the nodes but only passes new messages on. It therefore has no RAM overhead for network size, which is important for the ESP8266 since available RAM is very limited. The downside is that there is a lot of network traffic for each sent message, so especially for dense networks a lot of interference will be created. Based on tests, a mesh with 30 nodes close together will work well (1-2 dropped messages of 1000). A mesh with around 160 nodes close together will not work at all (though this would probably be solved by spreading out the nodes more, so the interference is reduced).
|
||||
|
||||
#### Usage
|
||||
|
||||
Change the `useLED` variable to true if you have built-in LEDs on your ESP8266s to illustrate how the message is spread through the network. Change the `floodingMesh.broadcast` calls to modify what the mesh nodes are transmitting to each other.
|
@ -138,9 +138,9 @@ void networkFilter(int numberOfNetworks, MeshBackendBase &meshInstance) {
|
||||
|
||||
if (targetNodeID < TypeCast::stringToUint64(meshInstance.getNodeID())) {
|
||||
if (EspnowMeshBackend *espnowInstance = TypeCast::meshBackendCast<EspnowMeshBackend *>(&meshInstance)) {
|
||||
espnowInstance->connectionQueue().push_back(networkIndex);
|
||||
espnowInstance->connectionQueue().emplace_back(networkIndex);
|
||||
} else if (TcpIpMeshBackend *tcpIpInstance = TypeCast::meshBackendCast<TcpIpMeshBackend *>(&meshInstance)) {
|
||||
tcpIpInstance->connectionQueue().push_back(networkIndex);
|
||||
tcpIpInstance->connectionQueue().emplace_back(networkIndex);
|
||||
} else {
|
||||
Serial.println(F("Invalid mesh backend!"));
|
||||
}
|
||||
|
@ -117,9 +117,9 @@ void networkFilter(int numberOfNetworks, MeshBackendBase &meshInstance) {
|
||||
|
||||
if (targetNodeID < TypeCast::stringToUint64(meshInstance.getNodeID())) {
|
||||
if (EspnowMeshBackend *espnowInstance = TypeCast::meshBackendCast<EspnowMeshBackend *>(&meshInstance)) {
|
||||
espnowInstance->connectionQueue().push_back(networkIndex);
|
||||
espnowInstance->connectionQueue().emplace_back(networkIndex);
|
||||
} else if (TcpIpMeshBackend *tcpIpInstance = TypeCast::meshBackendCast<TcpIpMeshBackend *>(&meshInstance)) {
|
||||
tcpIpInstance->connectionQueue().push_back(networkIndex);
|
||||
tcpIpInstance->connectionQueue().emplace_back(networkIndex);
|
||||
} else {
|
||||
Serial.println(F("Invalid mesh backend!"));
|
||||
}
|
||||
|
@ -1,563 +0,0 @@
|
||||
/*
|
||||
* BearSSL Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
|
||||
* Rest of this file Copyright (C) 2019 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 "CryptoInterface.h"
|
||||
#include "TypeConversionFunctions.h"
|
||||
|
||||
#include <bearssl/bearssl.h>
|
||||
|
||||
namespace TypeCast = MeshTypeConversionFunctions;
|
||||
|
||||
namespace
|
||||
{
|
||||
size_t _ctMinDataLength = 0;
|
||||
size_t _ctMaxDataLength = 1024;
|
||||
|
||||
br_hkdf_context _storedHkdfContext;
|
||||
bool _hkdfContextStored = false;
|
||||
|
||||
uint8_t *defaultNonceGenerator(uint8_t *nonceArray, const size_t nonceLength)
|
||||
{
|
||||
/**
|
||||
* The ESP32 Technical Reference Manual v4.1 chapter 24 has the following to say about random number generation (no information found for ESP8266):
|
||||
*
|
||||
* "When used correctly, every 32-bit value the system reads from the RNG_DATA_REG register of the random number generator is a true random number.
|
||||
* These true random numbers are generated based on the noise in the Wi-Fi/BT RF system.
|
||||
* When Wi-Fi and BT are disabled, the random number generator will give out pseudo-random numbers.
|
||||
*
|
||||
* When Wi-Fi or BT is enabled, the random number generator is fed two bits of entropy every APB clock cycle (normally 80 MHz).
|
||||
* Thus, for the maximum amount of entropy, it is advisable to read the random register at a maximum rate of 5 MHz.
|
||||
* A data sample of 2 GB, read from the random number generator with Wi-Fi enabled and the random register read at 5 MHz,
|
||||
* has been tested using the Dieharder Random Number Testsuite (version 3.31.1).
|
||||
* The sample passed all tests."
|
||||
*
|
||||
* Since ESP32 is the sequal to ESP8266 it is unlikely that the ESP8266 is able to generate random numbers more quickly than 5 MHz when run at a 80 MHz frequency.
|
||||
* A maximum random number frequency of 0.5 MHz is used here to leave some margin for possibly inferior components in the ESP8266.
|
||||
* It should be noted that the ESP8266 has no Bluetooth functionality, so turning the WiFi off is likely to cause RANDOM_REG32 to use pseudo-random numbers.
|
||||
*
|
||||
* It is possible that yield() must be called on the ESP8266 to properly feed the hardware random number generator new bits, since there is only one processor core available.
|
||||
* However, no feeding requirements are mentioned in the ESP32 documentation, and using yield() could possibly cause extended delays during nonce generation.
|
||||
* Thus only delayMicroseconds() is used below.
|
||||
*/
|
||||
|
||||
constexpr uint8_t cooldownMicros = 2;
|
||||
static uint32_t lastCalledMicros = micros() - cooldownMicros;
|
||||
|
||||
uint32_t randomNumber = 0;
|
||||
|
||||
for(size_t byteIndex = 0; byteIndex < nonceLength; ++byteIndex)
|
||||
{
|
||||
if(byteIndex % 4 == 0)
|
||||
{
|
||||
// Old random number has been used up (random number could be exactly 0, so we can't check for that)
|
||||
|
||||
uint32_t timeSinceLastCall = micros() - lastCalledMicros;
|
||||
if(timeSinceLastCall < cooldownMicros)
|
||||
delayMicroseconds(cooldownMicros - timeSinceLastCall);
|
||||
|
||||
randomNumber = RANDOM_REG32;
|
||||
lastCalledMicros = micros();
|
||||
}
|
||||
|
||||
nonceArray[byteIndex] = randomNumber;
|
||||
randomNumber >>= 8;
|
||||
}
|
||||
|
||||
return nonceArray;
|
||||
}
|
||||
|
||||
CryptoInterface::nonceGeneratorType _nonceGenerator = defaultNonceGenerator;
|
||||
|
||||
void *createBearsslHmac(const br_hash_class *hashType, const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength)
|
||||
{
|
||||
// Comments mainly from https://www.bearssl.org/apidoc/bearssl__hmac_8h.html
|
||||
|
||||
// HMAC is initialized with a key and an underlying hash function; it then fills a "key context". That context contains the processed key.
|
||||
// With the key context, a HMAC context can be initialized to process the input bytes and obtain the MAC output. The key context is not modified during that process, and can be reused.
|
||||
|
||||
br_hmac_key_context keyContext; // Holds general HMAC info
|
||||
br_hmac_context hmacContext; // Holds general HMAC info + specific info for the current operation
|
||||
|
||||
// HMAC key context initialisation.
|
||||
// Initialise the key context with the provided hash key, using the hash function identified by hashType. This supports arbitrary key lengths.
|
||||
br_hmac_key_init(&keyContext, hashType, hashKey, hashKeyLength);
|
||||
|
||||
// Initialise a HMAC context with a key context. The key context is unmodified.
|
||||
// Relevant data from the key context is immediately copied; the key context can thus be independently reused, modified or released without impacting this HMAC computation.
|
||||
// An explicit output length can be specified; the actual output length will be the minimum of that value and the natural HMAC output length.
|
||||
// If outputLength is 0, then the natural HMAC output length is selected. The "natural output length" is the output length of the underlying hash function.
|
||||
br_hmac_init(&hmacContext, &keyContext, outputLength);
|
||||
|
||||
// Provide the HMAC context with the data to create a HMAC from.
|
||||
// The provided dataLength bytes are injected as extra input in the HMAC computation incarnated by the hmacContext.
|
||||
// It is acceptable that dataLength is zero, in which case data is ignored (and may be NULL) and this function does nothing.
|
||||
br_hmac_update(&hmacContext, data, dataLength);
|
||||
|
||||
// Compute the HMAC output.
|
||||
// The destination buffer MUST be large enough to accommodate the result; its length is at most the "natural length" of HMAC (i.e. the output length of the underlying hash function).
|
||||
// The context is NOT modified; further bytes may be processed. Thus, "partial HMAC" values can be efficiently obtained.
|
||||
// Optionally the constant-time version br_hmac_outCT() can be used. More info here: https://www.bearssl.org/constanttime.html .
|
||||
br_hmac_out(&hmacContext, resultArray); // returns size_t outputLength
|
||||
|
||||
return resultArray;
|
||||
}
|
||||
|
||||
String createBearsslHmac(const br_hash_class *hashType, const uint8_t hashTypeNaturalLength, const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
|
||||
{
|
||||
assert(1 <= hmacLength && hmacLength <= hashTypeNaturalLength);
|
||||
|
||||
uint8_t hmac[hmacLength];
|
||||
createBearsslHmac(hashType, message.c_str(), message.length(), hashKey, hashKeyLength, hmac, hmacLength);
|
||||
return TypeCast::uint8ArrayToHexString(hmac, hmacLength);
|
||||
}
|
||||
|
||||
void *createBearsslHmacCT(const br_hash_class *hashType, const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength)
|
||||
{
|
||||
assert(_ctMinDataLength <= dataLength && dataLength <= _ctMaxDataLength);
|
||||
|
||||
// Comments mainly from https://www.bearssl.org/apidoc/bearssl__hmac_8h.html
|
||||
|
||||
// HMAC is initialized with a key and an underlying hash function; it then fills a "key context". That context contains the processed key.
|
||||
// With the key context, a HMAC context can be initialized to process the input bytes and obtain the MAC output. The key context is not modified during that process, and can be reused.
|
||||
|
||||
br_hmac_key_context keyContext; // Holds general HMAC info
|
||||
br_hmac_context hmacContext; // Holds general HMAC info + specific info for the current operation
|
||||
|
||||
// HMAC key context initialisation.
|
||||
// Initialise the key context with the provided hash key, using the hash function identified by hashType. This supports arbitrary key lengths.
|
||||
br_hmac_key_init(&keyContext, hashType, hashKey, hashKeyLength);
|
||||
|
||||
// Initialise a HMAC context with a key context. The key context is unmodified.
|
||||
// Relevant data from the key context is immediately copied; the key context can thus be independently reused, modified or released without impacting this HMAC computation.
|
||||
// An explicit output length can be specified; the actual output length will be the minimum of that value and the natural HMAC output length.
|
||||
// If outputLength is 0, then the natural HMAC output length is selected. The "natural output length" is the output length of the underlying hash function.
|
||||
br_hmac_init(&hmacContext, &keyContext, outputLength);
|
||||
|
||||
// Provide the HMAC context with the data to create a HMAC from.
|
||||
// The provided dataLength bytes are injected as extra input in the HMAC computation incarnated by the hmacContext.
|
||||
// It is acceptable that dataLength is zero, in which case data is ignored (and may be NULL) and this function does nothing.
|
||||
// No need for br_hmac_update when using constant-time version it seems. If it is used, the data provided to br_hmac_outCT will just be appended.
|
||||
// br_hmac_update(&hmacContext, data, dataLength);
|
||||
|
||||
// Compute the HMAC output. Assumes message is minimum _ctMinDataLength bytes and maximum _ctMaxDataLength bytes.
|
||||
// As long as this is true, the correct HMAC output is calculated in constant-time. More constant-time info here: https://www.bearssl.org/constanttime.html
|
||||
// Some extra input bytes are processed, then the output is computed.
|
||||
// The extra input consists in the dataLength bytes pointed to by data. The dataLength parameter must lie between _ctMinDataLength and _ctMaxDataLength (inclusive);
|
||||
// _ctMaxDataLength bytes are actually read from data (indicating each data byte can be read multiple times, if dataLength < _ctMaxDataLength).
|
||||
// Computing time (and memory access pattern) will not depend upon the data byte contents or the value of dataLength.
|
||||
// The output is written in the resultArray buffer, that MUST be large enough to receive it.
|
||||
// The difference _ctMaxDataLength - _ctMinDataLength MUST be less than 2^30 (i.e. about one gigabyte).
|
||||
// This function computes the output properly only if the underlying hash function uses MD padding (i.e. MD5, SHA-1, SHA-224, SHA-256, SHA-384 or SHA-512).
|
||||
// The provided context is NOT modified.
|
||||
br_hmac_outCT(&hmacContext, data, dataLength, _ctMinDataLength, _ctMaxDataLength, resultArray); // returns size_t outputLength
|
||||
|
||||
return resultArray;
|
||||
}
|
||||
|
||||
String createBearsslHmacCT(const br_hash_class *hashType, const uint8_t hashTypeNaturalLength, const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
|
||||
{
|
||||
assert(1 <= hmacLength && hmacLength <= hashTypeNaturalLength);
|
||||
|
||||
uint8_t hmac[hmacLength];
|
||||
createBearsslHmacCT(hashType, message.c_str(), message.length(), hashKey, hashKeyLength, hmac, hmacLength);
|
||||
return TypeCast::uint8ArrayToHexString(hmac, hmacLength);
|
||||
}
|
||||
|
||||
|
||||
// Helper function to avoid deprecated warnings.
|
||||
void *md5HashHelper(const void *data, const size_t dataLength, void *resultArray)
|
||||
{
|
||||
br_md5_context context;
|
||||
br_md5_init(&context);
|
||||
br_md5_update(&context, data, dataLength);
|
||||
br_md5_out(&context, resultArray);
|
||||
return resultArray;
|
||||
}
|
||||
|
||||
// Helper function to avoid deprecated warnings.
|
||||
void *sha1HashHelper(const void *data, const size_t dataLength, void *resultArray)
|
||||
{
|
||||
br_sha1_context context;
|
||||
br_sha1_init(&context);
|
||||
br_sha1_update(&context, data, dataLength);
|
||||
br_sha1_out(&context, resultArray);
|
||||
return resultArray;
|
||||
}
|
||||
}
|
||||
|
||||
namespace CryptoInterface
|
||||
{
|
||||
void setCtMinDataLength(const size_t ctMinDataLength)
|
||||
{
|
||||
assert(getCtMaxDataLength() - ctMinDataLength <= CT_MAX_DIFF);
|
||||
_ctMinDataLength = ctMinDataLength;
|
||||
}
|
||||
size_t getCtMinDataLength() {return _ctMinDataLength;}
|
||||
|
||||
void setCtMaxDataLength(const size_t ctMaxDataLength)
|
||||
{
|
||||
assert(ctMaxDataLength - getCtMinDataLength() <= CT_MAX_DIFF);
|
||||
_ctMaxDataLength = ctMaxDataLength;
|
||||
}
|
||||
size_t getCtMaxDataLength() {return _ctMaxDataLength;}
|
||||
|
||||
void setNonceGenerator(nonceGeneratorType nonceGenerator) { _nonceGenerator = nonceGenerator; }
|
||||
nonceGeneratorType getNonceGenerator() { return _nonceGenerator; }
|
||||
|
||||
|
||||
// #################### MD5 ####################
|
||||
|
||||
// resultArray must have size MD5_NATURAL_LENGTH or greater
|
||||
void *md5Hash(const void *data, const size_t dataLength, void *resultArray)
|
||||
{
|
||||
return md5HashHelper(data, dataLength, resultArray);
|
||||
}
|
||||
|
||||
String md5Hash(const String &message)
|
||||
{
|
||||
uint8_t hash[MD5_NATURAL_LENGTH];
|
||||
md5HashHelper(message.c_str(), message.length(), hash);
|
||||
return TypeCast::uint8ArrayToHexString(hash, MD5_NATURAL_LENGTH);
|
||||
}
|
||||
|
||||
void *md5Hmac(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength)
|
||||
{
|
||||
return createBearsslHmac(&br_md5_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, outputLength);
|
||||
}
|
||||
|
||||
String md5Hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
|
||||
{
|
||||
return createBearsslHmac(&br_md5_vtable, MD5_NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
|
||||
}
|
||||
|
||||
void *md5HmacCT(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength)
|
||||
{
|
||||
return createBearsslHmacCT(&br_md5_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, outputLength);
|
||||
}
|
||||
|
||||
String md5HmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
|
||||
{
|
||||
return createBearsslHmacCT(&br_md5_vtable, MD5_NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
|
||||
}
|
||||
|
||||
|
||||
// #################### SHA-1 ####################
|
||||
|
||||
// resultArray must have size SHA1_NATURAL_LENGTH or greater
|
||||
void *sha1Hash(const void *data, const size_t dataLength, void *resultArray)
|
||||
{
|
||||
return sha1HashHelper(data, dataLength, resultArray);
|
||||
}
|
||||
|
||||
String sha1Hash(const String &message)
|
||||
{
|
||||
uint8_t hash[SHA1_NATURAL_LENGTH];
|
||||
sha1HashHelper(message.c_str(), message.length(), hash);
|
||||
return TypeCast::uint8ArrayToHexString(hash, SHA1_NATURAL_LENGTH);
|
||||
}
|
||||
|
||||
void *sha1Hmac(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength)
|
||||
{
|
||||
return createBearsslHmac(&br_sha1_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, outputLength);
|
||||
}
|
||||
|
||||
String sha1Hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
|
||||
{
|
||||
return createBearsslHmac(&br_sha1_vtable, SHA1_NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
|
||||
}
|
||||
|
||||
void *sha1HmacCT(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength)
|
||||
{
|
||||
return createBearsslHmacCT(&br_sha1_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, outputLength);
|
||||
}
|
||||
|
||||
String sha1HmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
|
||||
{
|
||||
return createBearsslHmacCT(&br_sha1_vtable, SHA1_NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
|
||||
}
|
||||
|
||||
|
||||
// #################### SHA-224 ####################
|
||||
|
||||
// resultArray must have size SHA224_NATURAL_LENGTH or greater
|
||||
void *sha224Hash(const void *data, const size_t dataLength, void *resultArray)
|
||||
{
|
||||
br_sha224_context context;
|
||||
br_sha224_init(&context);
|
||||
br_sha224_update(&context, data, dataLength);
|
||||
br_sha224_out(&context, resultArray);
|
||||
return resultArray;
|
||||
}
|
||||
|
||||
String sha224Hash(const String &message)
|
||||
{
|
||||
uint8_t hash[SHA224_NATURAL_LENGTH];
|
||||
sha224Hash(message.c_str(), message.length(), hash);
|
||||
return TypeCast::uint8ArrayToHexString(hash, SHA224_NATURAL_LENGTH);
|
||||
}
|
||||
|
||||
void *sha224Hmac(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength)
|
||||
{
|
||||
return createBearsslHmac(&br_sha224_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, outputLength);
|
||||
}
|
||||
|
||||
String sha224Hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
|
||||
{
|
||||
return createBearsslHmac(&br_sha224_vtable, SHA224_NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
|
||||
}
|
||||
|
||||
void *sha224HmacCT(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength)
|
||||
{
|
||||
return createBearsslHmacCT(&br_sha224_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, outputLength);
|
||||
}
|
||||
|
||||
String sha224HmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
|
||||
{
|
||||
return createBearsslHmacCT(&br_sha224_vtable, SHA224_NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
|
||||
}
|
||||
|
||||
|
||||
// #################### SHA-256 ####################
|
||||
|
||||
// resultArray must have size SHA256_NATURAL_LENGTH or greater
|
||||
void *sha256Hash(const void *data, const size_t dataLength, void *resultArray)
|
||||
{
|
||||
br_sha256_context context;
|
||||
br_sha256_init(&context);
|
||||
br_sha256_update(&context, data, dataLength);
|
||||
br_sha256_out(&context, resultArray);
|
||||
return resultArray;
|
||||
}
|
||||
|
||||
String sha256Hash(const String &message)
|
||||
{
|
||||
uint8_t hash[SHA256_NATURAL_LENGTH];
|
||||
sha256Hash(message.c_str(), message.length(), hash);
|
||||
return TypeCast::uint8ArrayToHexString(hash, SHA256_NATURAL_LENGTH);
|
||||
}
|
||||
|
||||
void *sha256Hmac(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength)
|
||||
{
|
||||
return createBearsslHmac(&br_sha256_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, outputLength);
|
||||
}
|
||||
|
||||
String sha256Hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
|
||||
{
|
||||
return createBearsslHmac(&br_sha256_vtable, SHA256_NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
|
||||
}
|
||||
|
||||
void *sha256HmacCT(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength)
|
||||
{
|
||||
return createBearsslHmacCT(&br_sha256_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, outputLength);
|
||||
}
|
||||
|
||||
String sha256HmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
|
||||
{
|
||||
return createBearsslHmacCT(&br_sha256_vtable, SHA256_NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
|
||||
}
|
||||
|
||||
|
||||
// #################### SHA-384 ####################
|
||||
|
||||
// resultArray must have size SHA384_NATURAL_LENGTH or greater
|
||||
void *sha384Hash(const void *data, const size_t dataLength, void *resultArray)
|
||||
{
|
||||
br_sha384_context context;
|
||||
br_sha384_init(&context);
|
||||
br_sha384_update(&context, data, dataLength);
|
||||
br_sha384_out(&context, resultArray);
|
||||
return resultArray;
|
||||
}
|
||||
|
||||
String sha384Hash(const String &message)
|
||||
{
|
||||
uint8_t hash[SHA384_NATURAL_LENGTH];
|
||||
sha384Hash(message.c_str(), message.length(), hash);
|
||||
return TypeCast::uint8ArrayToHexString(hash, SHA384_NATURAL_LENGTH);
|
||||
}
|
||||
|
||||
void *sha384Hmac(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength)
|
||||
{
|
||||
return createBearsslHmac(&br_sha384_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, outputLength);
|
||||
}
|
||||
|
||||
String sha384Hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
|
||||
{
|
||||
return createBearsslHmac(&br_sha384_vtable, SHA384_NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
|
||||
}
|
||||
|
||||
void *sha384HmacCT(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength)
|
||||
{
|
||||
return createBearsslHmacCT(&br_sha384_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, outputLength);
|
||||
}
|
||||
|
||||
String sha384HmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
|
||||
{
|
||||
return createBearsslHmacCT(&br_sha384_vtable, SHA384_NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
|
||||
}
|
||||
|
||||
|
||||
// #################### SHA-512 ####################
|
||||
|
||||
// resultArray must have size SHA512_NATURAL_LENGTH or greater
|
||||
void *sha512Hash(const void *data, const size_t dataLength, void *resultArray)
|
||||
{
|
||||
br_sha512_context context;
|
||||
br_sha512_init(&context);
|
||||
br_sha512_update(&context, data, dataLength);
|
||||
br_sha512_out(&context, resultArray);
|
||||
return resultArray;
|
||||
}
|
||||
|
||||
String sha512Hash(const String &message)
|
||||
{
|
||||
uint8_t hash[SHA512_NATURAL_LENGTH];
|
||||
sha512Hash(message.c_str(), message.length(), hash);
|
||||
return TypeCast::uint8ArrayToHexString(hash, SHA512_NATURAL_LENGTH);
|
||||
}
|
||||
|
||||
void *sha512Hmac(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength)
|
||||
{
|
||||
return createBearsslHmac(&br_sha512_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, outputLength);
|
||||
}
|
||||
|
||||
String sha512Hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
|
||||
{
|
||||
return createBearsslHmac(&br_sha512_vtable, SHA512_NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
|
||||
}
|
||||
|
||||
void *sha512HmacCT(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength)
|
||||
{
|
||||
return createBearsslHmacCT(&br_sha512_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, outputLength);
|
||||
}
|
||||
|
||||
String sha512HmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
|
||||
{
|
||||
return createBearsslHmacCT(&br_sha512_vtable, SHA512_NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
|
||||
}
|
||||
|
||||
|
||||
// #################### MD5+SHA-1 ####################
|
||||
|
||||
// resultArray must have size MD5SHA1_NATURAL_LENGTH or greater
|
||||
void *md5sha1Hash(const void *data, const size_t dataLength, void *resultArray)
|
||||
{
|
||||
br_md5sha1_context context;
|
||||
br_md5sha1_init(&context);
|
||||
br_md5sha1_update(&context, data, dataLength);
|
||||
br_md5sha1_out(&context, resultArray);
|
||||
return resultArray;
|
||||
}
|
||||
|
||||
String md5sha1Hash(const String &message)
|
||||
{
|
||||
uint8_t hash[MD5SHA1_NATURAL_LENGTH];
|
||||
md5sha1Hash(message.c_str(), message.length(), hash);
|
||||
return TypeCast::uint8ArrayToHexString(hash, MD5SHA1_NATURAL_LENGTH);
|
||||
}
|
||||
|
||||
|
||||
// #################### HKDF ####################
|
||||
|
||||
void hkdfInit(const void *keyMaterial, const size_t keyMaterialLength, const void *salt, const size_t saltLength)
|
||||
{
|
||||
// Comments mainly from https://www.bearssl.org/apidoc/bearssl__kdf_8h.html
|
||||
|
||||
br_hkdf_context context;
|
||||
|
||||
// Initialize an HKDF context, with a hash function, and the salt. This starts the HKDF-Extract process.
|
||||
br_hkdf_init(&context, &br_sha256_vtable, salt, saltLength);
|
||||
|
||||
// Inject more input bytes. This function may be called repeatedly if the input data is provided by chunks, after br_hkdf_init() but before br_hkdf_flip().
|
||||
br_hkdf_inject(&context, keyMaterial, keyMaterialLength);
|
||||
|
||||
// End the HKDF-Extract process, and start the HKDF-Expand process.
|
||||
br_hkdf_flip(&context);
|
||||
|
||||
_storedHkdfContext = context;
|
||||
_hkdfContextStored = true;
|
||||
}
|
||||
|
||||
size_t hkdfProduce(void *resultArray, const size_t outputLength, const void *info, const size_t infoLength)
|
||||
{
|
||||
// Comments mainly from https://www.bearssl.org/apidoc/bearssl__kdf_8h.html
|
||||
|
||||
if(!_hkdfContextStored) // hkdfInit has not yet been executed
|
||||
return 0;
|
||||
|
||||
// HKDF output production (HKDF-Expand).
|
||||
// Produces more output bytes from the current state. This function may be called several times, but only after br_hkdf_flip().
|
||||
// Returned value is the number of actually produced bytes. The total output length is limited to 255 times the output length of the underlying hash function.
|
||||
return br_hkdf_produce(&_storedHkdfContext, info, infoLength, resultArray, outputLength);
|
||||
}
|
||||
|
||||
|
||||
// #################### Authenticated Encryption with Associated Data (AEAD) ####################
|
||||
|
||||
|
||||
// #################### ChaCha20+Poly1305 AEAD ####################
|
||||
|
||||
void chacha20Poly1305Kernel(const int encrypt, void *data, const size_t dataLength, const void *key, const void *keySalt, const size_t keySaltLength,
|
||||
const void *nonce, void *tag, const void *aad, const size_t aadLength)
|
||||
{
|
||||
if(keySalt == nullptr)
|
||||
{
|
||||
br_poly1305_ctmul32_run(key, nonce, data, dataLength, aad, aadLength, tag, br_chacha20_ct_run, encrypt);
|
||||
}
|
||||
else
|
||||
{
|
||||
hkdfInit(key, ENCRYPTION_KEY_LENGTH, keySalt, keySaltLength);
|
||||
uint8_t derivedEncryptionKey[ENCRYPTION_KEY_LENGTH] {0};
|
||||
hkdfProduce(derivedEncryptionKey, ENCRYPTION_KEY_LENGTH);
|
||||
br_poly1305_ctmul32_run(derivedEncryptionKey, nonce, data, dataLength, aad, aadLength, tag, br_chacha20_ct_run, encrypt);
|
||||
}
|
||||
}
|
||||
|
||||
void chacha20Poly1305Encrypt(void *data, const size_t dataLength, const void *key, const void *keySalt, const size_t keySaltLength,
|
||||
void *resultingNonce, void *resultingTag, const void *aad, const size_t aadLength)
|
||||
{
|
||||
uint8_t *nonce = (uint8_t *)resultingNonce;
|
||||
getNonceGenerator()(nonce, 12);
|
||||
|
||||
chacha20Poly1305Kernel(1, data, dataLength, key, keySalt, keySaltLength, nonce, resultingTag, aad, aadLength);
|
||||
}
|
||||
|
||||
bool chacha20Poly1305Decrypt(void *data, const size_t dataLength, const void *key, const void *keySalt, const size_t keySaltLength,
|
||||
const void *encryptionNonce, const void *encryptionTag, const void *aad, const size_t aadLength)
|
||||
{
|
||||
const uint8_t *oldTag = (const uint8_t *)encryptionTag;
|
||||
uint8_t newTag[16] {0};
|
||||
|
||||
chacha20Poly1305Kernel(0, data, dataLength, key, keySalt, keySaltLength, encryptionNonce, newTag, aad, aadLength);
|
||||
|
||||
for(uint32_t i = 0; i < sizeof newTag; ++i)
|
||||
{
|
||||
if(newTag[i] != oldTag[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,803 +0,0 @@
|
||||
/*
|
||||
* BearSSL Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
|
||||
* Rest of this file Copyright (C) 2019 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 __ESP8266ARDUINOCRYPTOINTERFACE_H__
|
||||
#define __ESP8266ARDUINOCRYPTOINTERFACE_H__
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
namespace CryptoInterface
|
||||
{
|
||||
/**
|
||||
* Regarding constant-time (CT) HMAC:
|
||||
*
|
||||
* Basically, constant-time algorithms makes it harder for attackers to learn things about your system based on the execution time of code.
|
||||
* Good intro here: https://www.bearssl.org/constanttime.html
|
||||
*
|
||||
* It should be noted that every HMAC is already partially constant-time. Quoting the link above:
|
||||
* "Hash functions implemented by BearSSL (MD5, SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512) consist in bitwise logical operations and additions on 32-bit or 64-bit words,
|
||||
* naturally yielding constant-time operations. HMAC is naturally as constant-time as the underlying hash function. The size of the MACed data, and the size of the key,
|
||||
* may leak, though; only the contents are protected."
|
||||
*
|
||||
* For messages much smaller than getCtMaxDataLength(), constant-time processing takes substantially longer time to complete than a normal HMAC,
|
||||
* determined by the size of (getCtMaxDataLength() - getCtMinDataLength()).
|
||||
* Constant-time processing also sets limits on the data length.
|
||||
*
|
||||
* Making the fixed data length limits variable will generally defeat the purpose of using constant-time.
|
||||
* Using data that exceeds the fixed data length limits will create the wrong HMAC.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* The nonce generator should take an uint8_t array with a given size in bytes and fill it with the nonce.
|
||||
* The uint8_t array should then be returned by the nonce generator.
|
||||
*/
|
||||
using nonceGeneratorType = std::function<uint8_t *(uint8_t *, const size_t)>;
|
||||
|
||||
constexpr uint8_t MD5_NATURAL_LENGTH = 16;
|
||||
constexpr uint8_t SHA1_NATURAL_LENGTH = 20;
|
||||
constexpr uint8_t SHA224_NATURAL_LENGTH = 28;
|
||||
constexpr uint8_t SHA256_NATURAL_LENGTH = 32;
|
||||
constexpr uint8_t SHA384_NATURAL_LENGTH = 48;
|
||||
constexpr uint8_t SHA512_NATURAL_LENGTH = 64;
|
||||
|
||||
/**
|
||||
* MD5+SHA-1 is the concatenation of MD5 and SHA-1 computed over the same input; in the implementation, the internal data buffer is shared,
|
||||
* thus making it more memory-efficient than separate MD5 and SHA-1. It can be useful in implementing SSL 3.0, TLS 1.0 and TLS 1.1.
|
||||
*/
|
||||
constexpr uint8_t MD5SHA1_NATURAL_LENGTH = 36;
|
||||
|
||||
constexpr uint8_t ENCRYPTION_KEY_LENGTH = 32;
|
||||
|
||||
constexpr uint32_t CT_MAX_DIFF = 1073741823; // 2^30 - 1
|
||||
|
||||
/**
|
||||
* This function allows for fine-tuning of the specifications for the constant time calculations.
|
||||
* It should not be changed once a constant time function has been used at least once.
|
||||
* Otherwise the constant time will not be constant for the used functions.
|
||||
*
|
||||
* The difference getCtMaxDataLength() - getCtMinDataLength() MUST be less than 2^30 (i.e. about one gigabyte).
|
||||
*/
|
||||
void setCtMinDataLength(const size_t ctMinDataLength);
|
||||
/**
|
||||
* 0 by default.
|
||||
*/
|
||||
size_t getCtMinDataLength();
|
||||
|
||||
/**
|
||||
* This function allows for fine-tuning of the specifications for the constant time calculations.
|
||||
* It should not be changed once a constant time function has been used at least once.
|
||||
* Otherwise the constant time will not be constant for the used functions.
|
||||
*
|
||||
* The difference getCtMaxDataLength() - getCtMinDataLength() MUST be less than 2^30 (i.e. about one gigabyte).
|
||||
*/
|
||||
void setCtMaxDataLength(const size_t ctMaxDataLength);
|
||||
/**
|
||||
* 1024 by default.
|
||||
*/
|
||||
size_t getCtMaxDataLength();
|
||||
|
||||
/**
|
||||
* Set the nonce generator used by the CryptoInterface functions.
|
||||
*
|
||||
* @param nonceGenerator The nonce generator to use.
|
||||
*/
|
||||
void setNonceGenerator(nonceGeneratorType nonceGenerator);
|
||||
nonceGeneratorType getNonceGenerator();
|
||||
|
||||
|
||||
// #################### MD5 ####################
|
||||
|
||||
/**
|
||||
* WARNING! The MD5 hash is broken in terms of attacker resistance.
|
||||
* Only use it in those cases where attacker resistance is not important. Prefer SHA-256 or higher otherwise.
|
||||
*
|
||||
* Create a MD5 hash of the data. The result will be MD5_NATURAL_LENGTH bytes long and stored in resultArray.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param data The data array from which to create the hash.
|
||||
* @param dataLength The length of the data array in bytes.
|
||||
* @param resultArray The array wherein to store the resulting hash. MUST be be able to contain MD5_NATURAL_LENGTH bytes or more.
|
||||
*
|
||||
* @return A pointer to resultArray.
|
||||
*/
|
||||
void *md5Hash(const void *data, const size_t dataLength, void *resultArray) __attribute__((deprecated));
|
||||
|
||||
/**
|
||||
* WARNING! The MD5 hash is broken in terms of attacker resistance.
|
||||
* Only use it in those cases where attacker resistance is not important. Prefer SHA-256 or higher otherwise.
|
||||
*
|
||||
* Create a MD5 hash of the data. The result will be MD5_NATURAL_LENGTH bytes long and returned as a String in HEX format.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param message The string from which to create the hash.
|
||||
*
|
||||
* @return A String with the generated hash in HEX format.
|
||||
*/
|
||||
String md5Hash(const String &message) __attribute__((deprecated));
|
||||
|
||||
/**
|
||||
* Create a MD5 HMAC from the data, using the provided hashKey. The result will be up to outputLength bytes long and stored in resultArray.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param data The data array from which to create the HMAC.
|
||||
* @param dataLength The length of the data array in bytes.
|
||||
* @param hashKey The hash key to use when creating the HMAC.
|
||||
* @param hashKeyLength The length of the hash key in bytes.
|
||||
* @param resultArray The array wherein to store the resulting HMAC.
|
||||
* @param outputLength The desired length of the generated HMAC, in bytes. Must fit within resultArray. If outputLength is greater than MD5_NATURAL_LENGTH,
|
||||
* the first (lowest index) MD5_NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
|
||||
* If outputLength is 0, then the natural HMAC output length is selected.
|
||||
*
|
||||
* @return A pointer to resultArray.
|
||||
*/
|
||||
void *md5Hmac(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength);
|
||||
|
||||
/**
|
||||
* Create a MD5 HMAC from the message, using the provided hashKey. The result will be hmacLength bytes long and returned as a String in HEX format.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param message The string from which to create the HMAC.
|
||||
* @param hashKey The hash key to use when creating the HMAC.
|
||||
* @param hashKeyLength The length of the hash key in bytes.
|
||||
* @param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to MD5_NATURAL_LENGTH.
|
||||
*
|
||||
* @return A String with the generated HMAC in HEX format.
|
||||
*/
|
||||
String md5Hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
|
||||
|
||||
/**
|
||||
* Create a MD5 HMAC from the data, using the provided hashKey. The result will be up to outputLength bytes long and stored in resultArray.
|
||||
* Constant-time version.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param data The data array from which to create the HMAC.
|
||||
* @param dataLength The length of the data array in bytes. Valid values are in the range [getCtMinDataLength(), getCtMaxDataLength()].
|
||||
* @param hashKey The hash key to use when creating the HMAC.
|
||||
* @param hashKeyLength The length of the hash key in bytes.
|
||||
* @param resultArray The array wherein to store the resulting HMAC.
|
||||
* @param outputLength The desired length of the generated HMAC, in bytes. Must fit within resultArray. If outputLength is greater than MD5_NATURAL_LENGTH,
|
||||
* the first (lowest index) MD5_NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
|
||||
* If outputLength is 0, then the natural HMAC output length is selected.
|
||||
*
|
||||
* @return A pointer to resultArray.
|
||||
*/
|
||||
void *md5HmacCT(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength);
|
||||
|
||||
/**
|
||||
* Create a MD5 HMAC from the message, using the provided hashKey. The result will be hmacLength bytes long and returned as a String in HEX format.
|
||||
* Constant-time version.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param message The string from which to create the HMAC. Must have a length in the range [getCtMinDataLength(), getCtMaxDataLength()].
|
||||
* @param hashKey The hash key to use when creating the HMAC.
|
||||
* @param hashKeyLength The length of the hash key in bytes.
|
||||
* @param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to MD5_NATURAL_LENGTH.
|
||||
*
|
||||
* @return A String with the generated HMAC in HEX format.
|
||||
*/
|
||||
String md5HmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
|
||||
|
||||
|
||||
// #################### SHA-1 ####################
|
||||
|
||||
/**
|
||||
* WARNING! The SHA-1 hash is broken in terms of attacker resistance.
|
||||
* Only use it in those cases where attacker resistance is not important. Prefer SHA-256 or higher otherwise.
|
||||
*
|
||||
* Create a SHA1 hash of the data. The result will be SHA1_NATURAL_LENGTH bytes long and stored in resultArray.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param data The data array from which to create the hash.
|
||||
* @param dataLength The length of the data array in bytes.
|
||||
* @param resultArray The array wherein to store the resulting hash. MUST be be able to contain SHA1_NATURAL_LENGTH bytes or more.
|
||||
*
|
||||
* @return A pointer to resultArray.
|
||||
*/
|
||||
void *sha1Hash(const void *data, const size_t dataLength, void *resultArray) __attribute__((deprecated));
|
||||
|
||||
/**
|
||||
* WARNING! The SHA-1 hash is broken in terms of attacker resistance.
|
||||
* Only use it in those cases where attacker resistance is not important. Prefer SHA-256 or higher otherwise.
|
||||
*
|
||||
* Create a SHA1 hash of the data. The result will be SHA1_NATURAL_LENGTH bytes long and returned as a String in HEX format.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param message The string from which to create the hash.
|
||||
*
|
||||
* @return A String with the generated hash in HEX format.
|
||||
*/
|
||||
String sha1Hash(const String &message) __attribute__((deprecated));
|
||||
|
||||
/**
|
||||
* Create a SHA1 HMAC from the data, using the provided hashKey. The result will be up to outputLength bytes long and stored in resultArray.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param data The data array from which to create the HMAC.
|
||||
* @param dataLength The length of the data array in bytes.
|
||||
* @param hashKey The hash key to use when creating the HMAC.
|
||||
* @param hashKeyLength The length of the hash key in bytes.
|
||||
* @param resultArray The array wherein to store the resulting HMAC.
|
||||
* @param outputLength The desired length of the generated HMAC, in bytes. Must fit within resultArray. If outputLength is greater than SHA1_NATURAL_LENGTH,
|
||||
* the first (lowest index) SHA1_NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
|
||||
* If outputLength is 0, then the natural HMAC output length is selected.
|
||||
*
|
||||
* @return A pointer to resultArray.
|
||||
*/
|
||||
void *sha1Hmac(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength);
|
||||
|
||||
/**
|
||||
* Create a SHA1 HMAC from the message, using the provided hashKey. The result will be hmacLength bytes long and returned as a String in HEX format.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param message The string from which to create the HMAC.
|
||||
* @param hashKey The hash key to use when creating the HMAC.
|
||||
* @param hashKeyLength The length of the hash key in bytes.
|
||||
* @param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to SHA1_NATURAL_LENGTH.
|
||||
*
|
||||
* @return A String with the generated HMAC in HEX format.
|
||||
*/
|
||||
String sha1Hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
|
||||
|
||||
/**
|
||||
* Create a SHA1 HMAC from the data, using the provided hashKey. The result will be up to outputLength bytes long and stored in resultArray.
|
||||
* Constant-time version.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param data The data array from which to create the HMAC.
|
||||
* @param dataLength The length of the data array in bytes. Valid values are in the range [getCtMinDataLength(), getCtMaxDataLength()].
|
||||
* @param hashKey The hash key to use when creating the HMAC.
|
||||
* @param hashKeyLength The length of the hash key in bytes.
|
||||
* @param resultArray The array wherein to store the resulting HMAC.
|
||||
* @param outputLength The desired length of the generated HMAC, in bytes. Must fit within resultArray. If outputLength is greater than SHA1_NATURAL_LENGTH,
|
||||
* the first (lowest index) SHA1_NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
|
||||
* If outputLength is 0, then the natural HMAC output length is selected.
|
||||
*
|
||||
* @return A pointer to resultArray.
|
||||
*/
|
||||
void *sha1HmacCT(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength);
|
||||
|
||||
/**
|
||||
* Create a SHA1 HMAC from the message, using the provided hashKey. The result will be hmacLength bytes long and returned as a String in HEX format.
|
||||
* Constant-time version.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param message The string from which to create the HMAC. Must have a length in the range [getCtMinDataLength(), getCtMaxDataLength()].
|
||||
* @param hashKey The hash key to use when creating the HMAC.
|
||||
* @param hashKeyLength The length of the hash key in bytes.
|
||||
* @param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to SHA1_NATURAL_LENGTH.
|
||||
*
|
||||
* @return A String with the generated HMAC in HEX format.
|
||||
*/
|
||||
String sha1HmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
|
||||
|
||||
|
||||
// #################### SHA-224 ####################
|
||||
|
||||
/**
|
||||
* Create a SHA224 hash of the data. The result will be SHA224_NATURAL_LENGTH bytes long and stored in resultArray.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param data The data array from which to create the hash.
|
||||
* @param dataLength The length of the data array in bytes.
|
||||
* @param resultArray The array wherein to store the resulting hash. MUST be be able to contain SHA224_NATURAL_LENGTH bytes or more.
|
||||
*
|
||||
* @return A pointer to resultArray.
|
||||
*/
|
||||
void *sha224Hash(const void *data, const size_t dataLength, void *resultArray);
|
||||
|
||||
/**
|
||||
* Create a SHA224 hash of the data. The result will be SHA224_NATURAL_LENGTH bytes long and returned as a String in HEX format.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param message The string from which to create the hash.
|
||||
*
|
||||
* @return A String with the generated hash in HEX format.
|
||||
*/
|
||||
String sha224Hash(const String &message);
|
||||
|
||||
/**
|
||||
* Create a SHA224 HMAC from the data, using the provided hashKey. The result will be up to outputLength bytes long and stored in resultArray.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param data The data array from which to create the HMAC.
|
||||
* @param dataLength The length of the data array in bytes.
|
||||
* @param hashKey The hash key to use when creating the HMAC.
|
||||
* @param hashKeyLength The length of the hash key in bytes.
|
||||
* @param resultArray The array wherein to store the resulting HMAC.
|
||||
* @param outputLength The desired length of the generated HMAC, in bytes. Must fit within resultArray. If outputLength is greater than SHA224_NATURAL_LENGTH,
|
||||
* the first (lowest index) SHA224_NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
|
||||
* If outputLength is 0, then the natural HMAC output length is selected.
|
||||
*
|
||||
* @return A pointer to resultArray.
|
||||
*/
|
||||
void *sha224Hmac(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength);
|
||||
|
||||
/**
|
||||
* Create a SHA224 HMAC from the message, using the provided hashKey. The result will be hmacLength bytes long and returned as a String in HEX format.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param message The string from which to create the HMAC.
|
||||
* @param hashKey The hash key to use when creating the HMAC.
|
||||
* @param hashKeyLength The length of the hash key in bytes.
|
||||
* @param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to SHA224_NATURAL_LENGTH.
|
||||
*
|
||||
* @return A String with the generated HMAC in HEX format.
|
||||
*/
|
||||
String sha224Hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
|
||||
|
||||
/**
|
||||
* Create a SHA224 HMAC from the data, using the provided hashKey. The result will be up to outputLength bytes long and stored in resultArray.
|
||||
* Constant-time version.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param data The data array from which to create the HMAC.
|
||||
* @param dataLength The length of the data array in bytes. Valid values are in the range [getCtMinDataLength(), getCtMaxDataLength()].
|
||||
* @param hashKey The hash key to use when creating the HMAC.
|
||||
* @param hashKeyLength The length of the hash key in bytes.
|
||||
* @param resultArray The array wherein to store the resulting HMAC.
|
||||
* @param outputLength The desired length of the generated HMAC, in bytes. Must fit within resultArray. If outputLength is greater than SHA224_NATURAL_LENGTH,
|
||||
* the first (lowest index) SHA224_NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
|
||||
* If outputLength is 0, then the natural HMAC output length is selected.
|
||||
*
|
||||
* @return A pointer to resultArray.
|
||||
*/
|
||||
void *sha224HmacCT(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength);
|
||||
|
||||
/**
|
||||
* Create a SHA224 HMAC from the message, using the provided hashKey. The result will be hmacLength bytes long and returned as a String in HEX format.
|
||||
* Constant-time version.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param message The string from which to create the HMAC. Must have a length in the range [getCtMinDataLength(), getCtMaxDataLength()].
|
||||
* @param hashKey The hash key to use when creating the HMAC.
|
||||
* @param hashKeyLength The length of the hash key in bytes.
|
||||
* @param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to SHA224_NATURAL_LENGTH.
|
||||
*
|
||||
* @return A String with the generated HMAC in HEX format.
|
||||
*/
|
||||
String sha224HmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
|
||||
|
||||
|
||||
// #################### SHA-256 ####################
|
||||
|
||||
/**
|
||||
* Create a SHA256 hash of the data. The result will be SHA256_NATURAL_LENGTH bytes long and stored in resultArray.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param data The data array from which to create the hash.
|
||||
* @param dataLength The length of the data array in bytes.
|
||||
* @param resultArray The array wherein to store the resulting hash. MUST be be able to contain SHA256_NATURAL_LENGTH bytes or more.
|
||||
*
|
||||
* @return A pointer to resultArray.
|
||||
*/
|
||||
void *sha256Hash(const void *data, const size_t dataLength, void *resultArray);
|
||||
|
||||
/**
|
||||
* Create a SHA256 hash of the data. The result will be SHA256_NATURAL_LENGTH bytes long and returned as a String in HEX format.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param message The string from which to create the hash.
|
||||
*
|
||||
* @return A String with the generated hash in HEX format.
|
||||
*/
|
||||
String sha256Hash(const String &message);
|
||||
|
||||
/**
|
||||
* Create a SHA256 HMAC from the data, using the provided hashKey. The result will be up to outputLength bytes long and stored in resultArray.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param data The data array from which to create the HMAC.
|
||||
* @param dataLength The length of the data array in bytes.
|
||||
* @param hashKey The hash key to use when creating the HMAC.
|
||||
* @param hashKeyLength The length of the hash key in bytes.
|
||||
* @param resultArray The array wherein to store the resulting HMAC.
|
||||
* @param outputLength The desired length of the generated HMAC, in bytes. Must fit within resultArray. If outputLength is greater than SHA256_NATURAL_LENGTH,
|
||||
* the first (lowest index) SHA256_NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
|
||||
* If outputLength is 0, then the natural HMAC output length is selected.
|
||||
*
|
||||
* @return A pointer to resultArray.
|
||||
*/
|
||||
void *sha256Hmac(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength);
|
||||
|
||||
/**
|
||||
* Create a SHA256 HMAC from the message, using the provided hashKey. The result will be hmacLength bytes long and returned as a String in HEX format.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param message The string from which to create the HMAC.
|
||||
* @param hashKey The hash key to use when creating the HMAC.
|
||||
* @param hashKeyLength The length of the hash key in bytes.
|
||||
* @param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to SHA256_NATURAL_LENGTH.
|
||||
*
|
||||
* @return A String with the generated HMAC in HEX format.
|
||||
*/
|
||||
String sha256Hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
|
||||
|
||||
/**
|
||||
* Create a SHA256 HMAC from the data, using the provided hashKey. The result will be up to outputLength bytes long and stored in resultArray.
|
||||
* Constant-time version.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param data The data array from which to create the HMAC.
|
||||
* @param dataLength The length of the data array in bytes. Valid values are in the range [getCtMinDataLength(), getCtMaxDataLength()].
|
||||
* @param hashKey The hash key to use when creating the HMAC.
|
||||
* @param hashKeyLength The length of the hash key in bytes.
|
||||
* @param resultArray The array wherein to store the resulting HMAC.
|
||||
* @param outputLength The desired length of the generated HMAC, in bytes. Must fit within resultArray. If outputLength is greater than SHA256_NATURAL_LENGTH,
|
||||
* the first (lowest index) SHA256_NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
|
||||
* If outputLength is 0, then the natural HMAC output length is selected.
|
||||
*
|
||||
* @return A pointer to resultArray.
|
||||
*/
|
||||
void *sha256HmacCT(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength);
|
||||
|
||||
/**
|
||||
* Create a SHA256 HMAC from the message, using the provided hashKey. The result will be hmacLength bytes long and returned as a String in HEX format.
|
||||
* Constant-time version.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param message The string from which to create the HMAC. Must have a length in the range [getCtMinDataLength(), getCtMaxDataLength()].
|
||||
* @param hashKey The hash key to use when creating the HMAC.
|
||||
* @param hashKeyLength The length of the hash key in bytes.
|
||||
* @param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to SHA256_NATURAL_LENGTH.
|
||||
*
|
||||
* @return A String with the generated HMAC in HEX format.
|
||||
*/
|
||||
String sha256HmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
|
||||
|
||||
|
||||
// #################### SHA-384 ####################
|
||||
|
||||
/**
|
||||
* Create a SHA384 hash of the data. The result will be SHA384_NATURAL_LENGTH bytes long and stored in resultArray.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param data The data array from which to create the hash.
|
||||
* @param dataLength The length of the data array in bytes.
|
||||
* @param resultArray The array wherein to store the resulting hash. MUST be be able to contain SHA384_NATURAL_LENGTH bytes or more.
|
||||
*
|
||||
* @return A pointer to resultArray.
|
||||
*/
|
||||
void *sha384Hash(const void *data, const size_t dataLength, void *resultArray);
|
||||
|
||||
/**
|
||||
* Create a SHA384 hash of the data. The result will be SHA384_NATURAL_LENGTH bytes long and returned as a String in HEX format.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param message The string from which to create the hash.
|
||||
*
|
||||
* @return A String with the generated hash in HEX format.
|
||||
*/
|
||||
String sha384Hash(const String &message);
|
||||
|
||||
/**
|
||||
* Create a SHA384 HMAC from the data, using the provided hashKey. The result will be up to outputLength bytes long and stored in resultArray.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param data The data array from which to create the HMAC.
|
||||
* @param dataLength The length of the data array in bytes.
|
||||
* @param hashKey The hash key to use when creating the HMAC.
|
||||
* @param hashKeyLength The length of the hash key in bytes.
|
||||
* @param resultArray The array wherein to store the resulting HMAC.
|
||||
* @param outputLength The desired length of the generated HMAC, in bytes. Must fit within resultArray. If outputLength is greater than SHA384_NATURAL_LENGTH,
|
||||
* the first (lowest index) SHA384_NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
|
||||
* If outputLength is 0, then the natural HMAC output length is selected.
|
||||
*
|
||||
* @return A pointer to resultArray.
|
||||
*/
|
||||
void *sha384Hmac(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength);
|
||||
|
||||
/**
|
||||
* Create a SHA384 HMAC from the message, using the provided hashKey. The result will be hmacLength bytes long and returned as a String in HEX format.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param message The string from which to create the HMAC.
|
||||
* @param hashKey The hash key to use when creating the HMAC.
|
||||
* @param hashKeyLength The length of the hash key in bytes.
|
||||
* @param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to SHA384_NATURAL_LENGTH.
|
||||
*
|
||||
* @return A String with the generated HMAC in HEX format.
|
||||
*/
|
||||
String sha384Hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
|
||||
|
||||
/**
|
||||
* Create a SHA384 HMAC from the data, using the provided hashKey. The result will be up to outputLength bytes long and stored in resultArray.
|
||||
* Constant-time version.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param data The data array from which to create the HMAC.
|
||||
* @param dataLength The length of the data array in bytes. Valid values are in the range [getCtMinDataLength(), getCtMaxDataLength()].
|
||||
* @param hashKey The hash key to use when creating the HMAC.
|
||||
* @param hashKeyLength The length of the hash key in bytes.
|
||||
* @param resultArray The array wherein to store the resulting HMAC.
|
||||
* @param outputLength The desired length of the generated HMAC, in bytes. Must fit within resultArray. If outputLength is greater than SHA384_NATURAL_LENGTH,
|
||||
* the first (lowest index) SHA384_NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
|
||||
* If outputLength is 0, then the natural HMAC output length is selected.
|
||||
*
|
||||
* @return A pointer to resultArray.
|
||||
*/
|
||||
void *sha384HmacCT(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength);
|
||||
|
||||
/**
|
||||
* Create a SHA384 HMAC from the message, using the provided hashKey. The result will be hmacLength bytes long and returned as a String in HEX format.
|
||||
* Constant-time version.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param message The string from which to create the HMAC. Must have a length in the range [getCtMinDataLength(), getCtMaxDataLength()].
|
||||
* @param hashKey The hash key to use when creating the HMAC.
|
||||
* @param hashKeyLength The length of the hash key in bytes.
|
||||
* @param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to SHA384_NATURAL_LENGTH.
|
||||
*
|
||||
* @return A String with the generated HMAC in HEX format.
|
||||
*/
|
||||
String sha384HmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
|
||||
|
||||
|
||||
// #################### SHA-512 ####################
|
||||
|
||||
/**
|
||||
* Create a SHA512 hash of the data. The result will be SHA512_NATURAL_LENGTH bytes long and stored in resultArray.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param data The data array from which to create the hash.
|
||||
* @param dataLength The length of the data array in bytes.
|
||||
* @param resultArray The array wherein to store the resulting hash. MUST be be able to contain SHA512_NATURAL_LENGTH bytes or more.
|
||||
*
|
||||
* @return A pointer to resultArray.
|
||||
*/
|
||||
void *sha512Hash(const void *data, const size_t dataLength, void *resultArray);
|
||||
|
||||
/**
|
||||
* Create a SHA512 hash of the data. The result will be SHA512_NATURAL_LENGTH bytes long and returned as a String in HEX format.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param message The string from which to create the hash.
|
||||
*
|
||||
* @return A String with the generated hash in HEX format.
|
||||
*/
|
||||
String sha512Hash(const String &message);
|
||||
|
||||
/**
|
||||
* Create a SHA512 HMAC from the data, using the provided hashKey. The result will be up to outputLength bytes long and stored in resultArray.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param data The data array from which to create the HMAC.
|
||||
* @param dataLength The length of the data array in bytes.
|
||||
* @param hashKey The hash key to use when creating the HMAC.
|
||||
* @param hashKeyLength The length of the hash key in bytes.
|
||||
* @param resultArray The array wherein to store the resulting HMAC.
|
||||
* @param outputLength The desired length of the generated HMAC, in bytes. Must fit within resultArray. If outputLength is greater than SHA512_NATURAL_LENGTH,
|
||||
* the first (lowest index) SHA512_NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
|
||||
* If outputLength is 0, then the natural HMAC output length is selected.
|
||||
*
|
||||
* @return A pointer to resultArray.
|
||||
*/
|
||||
void *sha512Hmac(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength);
|
||||
|
||||
/**
|
||||
* Create a SHA512 HMAC from the message, using the provided hashKey. The result will be hmacLength bytes long and returned as a String in HEX format.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param message The string from which to create the HMAC.
|
||||
* @param hashKey The hash key to use when creating the HMAC.
|
||||
* @param hashKeyLength The length of the hash key in bytes.
|
||||
* @param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to SHA512_NATURAL_LENGTH.
|
||||
*
|
||||
* @return A String with the generated HMAC in HEX format.
|
||||
*/
|
||||
String sha512Hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
|
||||
|
||||
/**
|
||||
* Create a SHA512 HMAC from the data, using the provided hashKey. The result will be up to outputLength bytes long and stored in resultArray.
|
||||
* Constant-time version.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param data The data array from which to create the HMAC.
|
||||
* @param dataLength The length of the data array in bytes. Valid values are in the range [getCtMinDataLength(), getCtMaxDataLength()].
|
||||
* @param hashKey The hash key to use when creating the HMAC.
|
||||
* @param hashKeyLength The length of the hash key in bytes.
|
||||
* @param resultArray The array wherein to store the resulting HMAC.
|
||||
* @param outputLength The desired length of the generated HMAC, in bytes. Must fit within resultArray. If outputLength is greater than SHA512_NATURAL_LENGTH,
|
||||
* the first (lowest index) SHA512_NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
|
||||
* If outputLength is 0, then the natural HMAC output length is selected.
|
||||
*
|
||||
* @return A pointer to resultArray.
|
||||
*/
|
||||
void *sha512HmacCT(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength);
|
||||
|
||||
/**
|
||||
* Create a SHA512 HMAC from the message, using the provided hashKey. The result will be hmacLength bytes long and returned as a String in HEX format.
|
||||
* Constant-time version.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* @param message The string from which to create the HMAC. Must have a length in the range [getCtMinDataLength(), getCtMaxDataLength()].
|
||||
* @param hashKey The hash key to use when creating the HMAC.
|
||||
* @param hashKeyLength The length of the hash key in bytes.
|
||||
* @param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to SHA512_NATURAL_LENGTH.
|
||||
*
|
||||
* @return A String with the generated HMAC in HEX format.
|
||||
*/
|
||||
String sha512HmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
|
||||
|
||||
|
||||
// #################### MD5+SHA-1 ####################
|
||||
|
||||
/**
|
||||
* Create a MD5+SHA-1 hash of the data. The result will be MD5SHA1_NATURAL_LENGTH bytes long and stored in resultArray.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* MD5+SHA-1 is the concatenation of MD5 and SHA-1 computed over the same input; in the implementation, the internal data buffer is shared,
|
||||
* thus making it more memory-efficient than separate MD5 and SHA-1. It can be useful in implementing SSL 3.0, TLS 1.0 and TLS 1.1.
|
||||
*
|
||||
* @param data The data array from which to create the hash.
|
||||
* @param dataLength The length of the data array in bytes.
|
||||
* @param resultArray The array wherein to store the resulting hash. MUST be be able to contain MD5SHA1_NATURAL_LENGTH bytes or more.
|
||||
*
|
||||
* @return A pointer to resultArray.
|
||||
*/
|
||||
void *md5sha1Hash(const void *data, const size_t dataLength, void *resultArray);
|
||||
|
||||
/**
|
||||
* Create a MD5+SHA-1 hash of the data. The result will be MD5SHA1_NATURAL_LENGTH bytes long and returned as a String in HEX format.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* MD5+SHA-1 is the concatenation of MD5 and SHA-1 computed over the same input; in the implementation, the internal data buffer is shared,
|
||||
* thus making it more memory-efficient than separate MD5 and SHA-1. It can be useful in implementing SSL 3.0, TLS 1.0 and TLS 1.1.
|
||||
*
|
||||
* @param message The string from which to create the hash.
|
||||
*
|
||||
* @return A String with the generated hash in HEX format.
|
||||
*/
|
||||
String md5sha1Hash(const String &message);
|
||||
|
||||
|
||||
// #################### HKDF ####################
|
||||
|
||||
/**
|
||||
* KDFs (key derivation functions) are functions that takes a variable length input, and provide a variable length output, meant to be used to derive subkeys from a master key.
|
||||
* HKDF is a KDF defined by RFC 5869. It is based on HMAC. The provided implementation uses SHA-256 as the underlying hash function.
|
||||
*
|
||||
* This function initializes the HKDF implementation with the input data to use for HKDF processing.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* Must be called at least once before hkdfProduce() can be used.
|
||||
*
|
||||
* @param keyMaterial An array containing the key material to use when deriving subkeys. Typically this would be the master key.
|
||||
* @param keyMaterialLength The length of keyMaterial in bytes.
|
||||
* @param salt An array containing the salt to use when ingesting key material. Salt is non-secret and can be empty.
|
||||
* Its role is normally to bind the input to a conventional identifier that qualify it within the used protocol or application.
|
||||
* @param saltLength The length of the salt array, in bytes.
|
||||
*/
|
||||
void hkdfInit(const void *keyMaterial, const size_t keyMaterialLength, const void *salt = nullptr, const size_t saltLength = 0);
|
||||
|
||||
/**
|
||||
* Produce more output bytes from the current HKDF state. This function may be called several times to obtain the full output by chunks.
|
||||
* The total output size is limited to 255 * SHA256_NATURAL_LENGTH bytes per unique hkdfInit() call.
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* Should only be used when hkdfInit() has been called at least once.
|
||||
*
|
||||
* @param resultArray The array wherein to store the resulting HKDF.
|
||||
* @param outputLength The requested number of bytes to fill with HKDF output in resultArray.
|
||||
* @param info NOTE: For correct HKDF processing, the same "info" string must be provided for every call until there's a new unique hkdfInit().
|
||||
* An array containing the information string to use when producing output. Info is non-secret and can be empty.
|
||||
* Its role is normally to bind the output to a conventional identifier that qualify it within the used protocol or application.
|
||||
* @param infoLength The length of the info array, in bytes.
|
||||
*
|
||||
* @return The number of HKDF bytes actually produced.
|
||||
*/
|
||||
size_t hkdfProduce(void *resultArray, const size_t outputLength, const void *info = nullptr, size_t infoLength = 0);
|
||||
|
||||
|
||||
// #################### Authenticated Encryption with Associated Data (AEAD) ####################
|
||||
|
||||
/**
|
||||
* From https://www.bearssl.org/apidoc/bearssl__aead_8h.html
|
||||
*
|
||||
* An AEAD algorithm processes messages and provides confidentiality (encryption) and checked integrity (MAC). It uses the following parameters:
|
||||
*
|
||||
* - A symmetric key. Exact size depends on the AEAD algorithm.
|
||||
* - A nonce (IV). Size depends on the AEAD algorithm; for most algorithms, it is crucial for security that any given nonce value is never used twice for the same key and distinct messages.
|
||||
* - Data to encrypt and protect.
|
||||
* - Additional authenticated data, which is covered by the MAC but otherwise left untouched (i.e. not encrypted).
|
||||
*
|
||||
* The AEAD algorithm encrypts the data, and produces an authentication tag.
|
||||
* It is assumed that the encrypted data, the tag, the additional authenticated data and the nonce are sent to the receiver;
|
||||
* the additional data and the nonce may be implicit (e.g. using elements of the underlying transport protocol, such as record sequence numbers).
|
||||
* The receiver will recompute the tag value and compare it with the one received;
|
||||
* if they match, then the data is correct, and can be decrypted and used;
|
||||
* otherwise, at least one of the elements was altered in transit, normally leading to wholesale rejection of the complete message.
|
||||
*/
|
||||
|
||||
|
||||
// #################### ChaCha20+Poly1305 AEAD ####################
|
||||
|
||||
/**
|
||||
* Encrypt the data array using the ChaCha20 stream cipher and use Poly1305 for message authentication.
|
||||
* The function generates in place an equal-length ChaCha20 encrypted version of the data array.
|
||||
* More information about this encryption standard can be found here: https://tools.ietf.org/html/rfc7539 , https://tools.ietf.org/html/rfc8439
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* Encryption of small messages (up to a few hundred data bytes) takes around 0.5-1 ms with the default nonceGenerator, half of this without keySalt.
|
||||
*
|
||||
* The output values of chacha20Poly1305Encrypt should be passed as input values to chacha20Poly1305Decrypt.
|
||||
*
|
||||
* Note that a 12 byte nonce is generated via getNonceGenerator() every time chacha20Poly1305Encrypt is called.
|
||||
* If the same key and nonce combination is used more than once for distinct messages, the encryption will be broken, so keep the following in mind:
|
||||
*
|
||||
* By default the nonce is generated via the hardware random number generator of the ESP8266.
|
||||
* The entropy of this source may not be sufficient to avoid nonce collisions, so to further reduce the risk of encryption failure
|
||||
* it is recommended that a keySalt is always provided when using the default nonceGenerator. Using a keySalt will create a
|
||||
* pseudorandom subkey from the original key via HKDF, and use that for the encryption/decryption.
|
||||
* The same key + keySalt will always generate the same subkey.
|
||||
*
|
||||
* An alternative to using a keySalt is to change the nonceGenerator so that it does not rely on random numbers.
|
||||
* One way to do this would be to use a counter that guarantees the same key + nonce combination is never used.
|
||||
* This may not be easily achievable in all scenarios, however.
|
||||
*
|
||||
* @param data An array containing the data to encrypt. The encrypted data is generated in place, so when the function returns the data array will contain the encrypted data.
|
||||
* @param dataLength The length of the data array in bytes.
|
||||
* @param key The secret encryption key to use. Must be 32 bytes (ENCRYPTION_KEY_LENGTH) long.
|
||||
* @param keySalt The salt to use when generating a subkey from key. Note that the same salt must be used during decryption as during encryption. Set to nullptr to prevent subkey generation.
|
||||
* @param keySaltLength The length of keySalt in bytes.
|
||||
* @param resultingNonce The array that will store the nonce generated during encryption. Must be able to contain at least 12 bytes. The nonce is not secret and must be passed to the decryption function.
|
||||
* @param resultingTag The array that will store the message authentication tag generated during encryption. Must be able to contain at least 16 bytes. The tag is not secret and must be passed to the decryption function.
|
||||
* @param aad Additional authenticated data. This data will be covered by the Poly1305 MAC, but not encrypted.
|
||||
* You can include the unencrypted parts of your message as AAD to ensure that the encrypted content cannot
|
||||
* be re-sent with replaced unencrypted data by an attacker.
|
||||
* Defaults to nullptr.
|
||||
* @param aadLength The length of the aad array in bytes. Defaults to 0.
|
||||
*/
|
||||
void chacha20Poly1305Encrypt(void *data, const size_t dataLength, const void *key, const void *keySalt, const size_t keySaltLength, void *resultingNonce, void *resultingTag, const void *aad = nullptr, const size_t aadLength = 0);
|
||||
|
||||
/**
|
||||
* Decrypt the data array using the ChaCha20 stream cipher and use Poly1305 for message authentication.
|
||||
* The function generates in place an equal-length ChaCha20 decrypted version of the data array.
|
||||
* More information about this encryption standard can be found here: https://tools.ietf.org/html/rfc7539 , https://tools.ietf.org/html/rfc8439
|
||||
* Uses the BearSSL cryptographic library.
|
||||
*
|
||||
* Decryption of small messages (up to a few hundred data bytes) takes around 0.5-1 ms, half of this without keySalt.
|
||||
*
|
||||
* The output values of chacha20Poly1305Encrypt should be passed as input values to chacha20Poly1305Decrypt.
|
||||
*
|
||||
* @param data An array containing the data to decrypt. The decrypted data is generated in place, so when the function returns the data array will contain the decrypted data.
|
||||
* @param dataLength The length of the data array in bytes.
|
||||
* @param key The secret encryption key to use. Must be 32 bytes (ENCRYPTION_KEY_LENGTH) long.
|
||||
* @param keySalt The salt to use when generating a subkey from key. Note that the same salt must be used during decryption as during encryption. Set to nullptr to prevent subkey generation.
|
||||
* @param keySaltLength The length of keySalt in bytes.
|
||||
* @param encryptionNonce An array containing the nonce that was generated during encryption. The nonce should be 12 bytes.
|
||||
* @param encryptionTag An array containing the message authentication tag that was generated during encryption. The tag should be 16 bytes.
|
||||
* @param aad Additional authenticated data. This data will be covered by the Poly1305 MAC, but not decrypted.
|
||||
* You can include the unencrypted parts of your message as AAD to ensure that the encrypted content cannot
|
||||
* be re-sent with replaced unencrypted data by an attacker.
|
||||
* Defaults to nullptr.
|
||||
* @param aadLength The length of the aad array in bytes. Defaults to 0.
|
||||
*
|
||||
* @return True if the decryption was successful (the generated tag matches encryptionTag). False otherwise. Note that the data array is modified regardless of this outcome.
|
||||
*/
|
||||
bool chacha20Poly1305Decrypt(void *data, const size_t dataLength, const void *key, const void *keySalt, const size_t keySaltLength, const void *encryptionNonce, const void *encryptionTag, const void *aad = nullptr, const size_t aadLength = 0);
|
||||
}
|
||||
|
||||
#endif
|
@ -50,8 +50,9 @@ EncryptedConnectionData::EncryptedConnectionData(const uint8_t peerStaMac[6], co
|
||||
}
|
||||
|
||||
EncryptedConnectionData::EncryptedConnectionData(const EncryptedConnectionData &other)
|
||||
: _peerSessionKey(other.getPeerSessionKey()), _ownSessionKey(other.getOwnSessionKey()), _desync(other.desync()),
|
||||
_timeTracker(other.temporary() ? new ExpiringTimeTracker(*other.temporary()) : nullptr)
|
||||
: _peerSessionKey(other.getPeerSessionKey()), _ownSessionKey(other.getOwnSessionKey()),
|
||||
_timeTracker(other.temporary() ? new ExpiringTimeTracker(*other.temporary()) : nullptr),
|
||||
_desync(other.desync())
|
||||
{
|
||||
other.getPeerStaMac(_peerStaMac);
|
||||
other.getPeerApMac(_peerApMac);
|
||||
@ -132,8 +133,8 @@ uint64_t EncryptedConnectionData::getOwnSessionKey() const { return _ownSessionK
|
||||
uint64_t EncryptedConnectionData::incrementSessionKey(const uint64_t sessionKey, const uint8_t *hashKey, const uint8_t hashKeyLength)
|
||||
{
|
||||
uint8_t inputArray[8] {0};
|
||||
uint8_t hmacArray[CryptoInterface::SHA256_NATURAL_LENGTH] {0};
|
||||
CryptoInterface::sha256Hmac(TypeCast::uint64ToUint8Array(sessionKey, inputArray), 8, hashKey, hashKeyLength, hmacArray, CryptoInterface::SHA256_NATURAL_LENGTH);
|
||||
uint8_t hmacArray[experimental::crypto::SHA256::NATURAL_LENGTH] {0};
|
||||
experimental::crypto::SHA256::hmac(TypeCast::uint64ToUint8Array(sessionKey, inputArray), 8, hashKey, hashKeyLength, hmacArray, experimental::crypto::SHA256::NATURAL_LENGTH);
|
||||
|
||||
/* HMAC truncation should be OK since hmac sha256 is a PRF and we are truncating to the leftmost (MSB) bits.
|
||||
PRF: https://crypto.stackexchange.com/questions/26410/whats-the-gcm-sha-256-of-a-tls-protocol/26434#26434
|
||||
@ -141,7 +142,7 @@ uint64_t EncryptedConnectionData::incrementSessionKey(const uint64_t sessionKey,
|
||||
uint64_t newLeftmostBits = TypeCast::uint8ArrayToUint64(hmacArray) & EspnowProtocolInterpreter::uint64LeftmostBits;
|
||||
|
||||
if(newLeftmostBits == 0)
|
||||
newLeftmostBits = ((uint64_t)RANDOM_REG32 | (1 << 31)) << 32; // We never want newLeftmostBits == 0 since that would indicate an unencrypted transmission.
|
||||
newLeftmostBits = ((uint64_t)ESP.random() | (1 << 31)) << 32; // We never want newLeftmostBits == 0 since that would indicate an unencrypted transmission.
|
||||
|
||||
uint64_t newRightmostBits = (uint32_t)(sessionKey + 1);
|
||||
|
||||
|
@ -90,9 +90,9 @@ private:
|
||||
uint8_t _peerApMac[6] {0};
|
||||
uint64_t _peerSessionKey;
|
||||
uint64_t _ownSessionKey;
|
||||
std::unique_ptr<ExpiringTimeTracker> _timeTracker = nullptr;
|
||||
uint8_t _hashKey[EspnowProtocolInterpreter::hashKeyLength] {0};
|
||||
bool _desync = false;
|
||||
std::unique_ptr<ExpiringTimeTracker> _timeTracker = nullptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -141,12 +141,12 @@ private:
|
||||
ConditionalPrinter & _conditionalPrinter;
|
||||
EspnowDatabase & _database;
|
||||
|
||||
uint8_t _encryptedConnectionsSoftLimit = 6;
|
||||
static ConnectionType getConnectionInfoHelper(const EncryptedConnectionLog *encryptedConnection, uint32_t *remainingDuration, uint8_t *peerMac = nullptr);
|
||||
|
||||
uint8_t _espnowEncryptedConnectionKey[EspnowProtocolInterpreter::encryptedConnectionKeyLength] {0};
|
||||
uint8_t _espnowHashKey[EspnowProtocolInterpreter::hashKeyLength] {0};
|
||||
|
||||
static ConnectionType getConnectionInfoHelper(const EncryptedConnectionLog *encryptedConnection, uint32_t *remainingDuration, uint8_t *peerMac = nullptr);
|
||||
uint8_t _encryptedConnectionsSoftLimit = 6;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -203,11 +203,6 @@ private:
|
||||
|
||||
uint32_t _autoEncryptionDuration = 50;
|
||||
|
||||
uint8_t _senderMac[6] = {0};
|
||||
uint8_t _senderAPMac[6] = {0};
|
||||
|
||||
uint8 _espnowWiFiChannel;
|
||||
|
||||
template <typename T, typename U>
|
||||
static void deleteExpiredLogEntries(std::map<std::pair<U, uint64_t>, T> &logEntries, const uint32_t maxEntryLifetimeMs);
|
||||
|
||||
@ -218,6 +213,11 @@ private:
|
||||
|
||||
template <typename T>
|
||||
static void deleteExpiredLogEntries(std::list<T> &logEntries, const uint32_t maxEntryLifetimeMs);
|
||||
|
||||
uint8_t _senderMac[6] = {0};
|
||||
uint8_t _senderAPMac[6] = {0};
|
||||
|
||||
uint8 _espnowWiFiChannel;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -37,10 +37,10 @@ namespace
|
||||
namespace TypeCast = MeshTypeConversionFunctions;
|
||||
|
||||
String _ongoingPeerRequestNonce;
|
||||
uint8_t _ongoingPeerRequestMac[6] = {0};
|
||||
EspnowMeshBackend *_ongoingPeerRequester = nullptr;
|
||||
EncryptedConnectionStatus _ongoingPeerRequestResult = EncryptedConnectionStatus::MAX_CONNECTIONS_REACHED_SELF;
|
||||
ExpiringTimeTracker _ongoingPeerRequestEncryptionTimeout([](){ return EspnowDatabase::getEncryptionRequestTimeout(); });
|
||||
uint8_t _ongoingPeerRequestMac[6] = {0};
|
||||
bool _reciprocalPeerRequestConfirmation = false;
|
||||
}
|
||||
|
||||
@ -465,7 +465,7 @@ bool EspnowEncryptionBroker::verifyEncryptionRequestHmac(const String &encryptio
|
||||
if(hmacStartIndex < 0)
|
||||
return false;
|
||||
|
||||
if(hmac.length() == 2*CryptoInterface::SHA256_NATURAL_LENGTH // We know that each HMAC byte should become 2 String characters due to uint8ArrayToHexString.
|
||||
if(hmac.length() == 2*experimental::crypto::SHA256::NATURAL_LENGTH // We know that each HMAC byte should become 2 String characters due to uint8ArrayToHexString.
|
||||
&& verifyMeshHmac(TypeCast::macToString(requesterStaMac) + TypeCast::macToString(requesterApMac) + encryptionRequestHmacMessage.substring(0, hmacStartIndex), hmac, hashKey, hashKeyLength))
|
||||
{
|
||||
return true;
|
||||
|
@ -81,8 +81,6 @@ private:
|
||||
EspnowConnectionManager & _connectionManager;
|
||||
EspnowTransmitter & _transmitter;
|
||||
|
||||
bool _receivedEncryptedTransmission = false;
|
||||
|
||||
using encryptionRequestBuilderType = std::function<String(const String &, const ExpiringTimeTracker &)>;
|
||||
static String defaultEncryptionRequestBuilder(const String &requestHeader, const uint32_t durationMs, const uint8_t *hashKey, const String &requestNonce, const ExpiringTimeTracker &existingTimeTracker);
|
||||
static String flexibleEncryptionRequestBuilder(const uint32_t minDurationMs, const uint8_t *hashKey, const String &requestNonce, const ExpiringTimeTracker &existingTimeTracker);
|
||||
@ -100,6 +98,8 @@ private:
|
||||
EncryptedConnectionStatus requestEncryptedConnectionKernel(const uint8_t *peerMac, const encryptionRequestBuilderType &encryptionRequestBuilder, EspnowMeshBackend &espnowInstance);
|
||||
|
||||
static bool verifyEncryptionRequestHmac(const String &encryptionRequestHmacMessage, const uint8_t *requesterStaMac, const uint8_t *requesterApMac, const uint8_t *hashKey, const uint8_t hashKeyLength);
|
||||
|
||||
bool _receivedEncryptedTransmission = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -57,9 +57,9 @@ EspnowMeshBackend::EspnowMeshBackend(const requestHandlerType requestHandler, co
|
||||
const broadcastFilterType broadcastFilter, const String &meshPassword, const String &ssidPrefix, const String &ssidSuffix, const bool verboseMode,
|
||||
const uint8 meshWiFiChannel)
|
||||
: MeshBackendBase(requestHandler, responseHandler, networkFilter, MeshBackendType::ESP_NOW),
|
||||
_database(_conditionalPrinter, meshWiFiChannel), _connectionManager(_conditionalPrinter, _database),
|
||||
_transmitter(_conditionalPrinter, _database, _connectionManager),
|
||||
_encryptionBroker(_conditionalPrinter, _database, _connectionManager, _transmitter)
|
||||
_database(*getConditionalPrinter(), meshWiFiChannel), _connectionManager(*getConditionalPrinter(), *getDatabase()),
|
||||
_transmitter(*getConditionalPrinter(), *getDatabase(), *getConnectionManager()),
|
||||
_encryptionBroker(*getConditionalPrinter(), *getDatabase(), *getConnectionManager(), *getTransmitter())
|
||||
{
|
||||
setBroadcastFilter(broadcastFilter);
|
||||
setSSID(ssidPrefix, emptyString, ssidSuffix);
|
||||
@ -241,7 +241,7 @@ void EspnowMeshBackend::espnowReceiveCallbackWrapper(uint8_t *macaddr, uint8_t *
|
||||
{
|
||||
// chacha20Poly1305Decrypt decrypts dataArray in place.
|
||||
// We are using the protocol bytes as a key salt.
|
||||
if(!CryptoInterface::chacha20Poly1305Decrypt(dataArray + metadataSize(), len - metadataSize(), getEspnowMessageEncryptionKey(), dataArray,
|
||||
if(!experimental::crypto::ChaCha20Poly1305::decrypt(dataArray + metadataSize(), len - metadataSize(), getEspnowMessageEncryptionKey(), dataArray,
|
||||
protocolBytesSize, dataArray + protocolBytesSize, dataArray + protocolBytesSize + 12))
|
||||
{
|
||||
return; // Decryption of message failed.
|
||||
@ -470,7 +470,7 @@ void EspnowMeshBackend::espnowReceiveCallback(const uint8_t *macaddr, uint8_t *d
|
||||
|
||||
if(response.length() > 0)
|
||||
{
|
||||
EspnowDatabase::responsesToSend().push_back(ResponseData(response, macaddr, messageID));
|
||||
EspnowDatabase::responsesToSend().emplace_back(response, macaddr, messageID);
|
||||
|
||||
//Serial.println("methodStart Q done " + String(millis() - methodStart));
|
||||
}
|
||||
@ -626,7 +626,7 @@ void EspnowMeshBackend::setUseEncryptedMessages(const bool useEncryptedMessages)
|
||||
}
|
||||
bool EspnowMeshBackend::useEncryptedMessages() { return EspnowTransmitter::useEncryptedMessages(); }
|
||||
|
||||
void EspnowMeshBackend::setEspnowMessageEncryptionKey(const uint8_t espnowMessageEncryptionKey[CryptoInterface::ENCRYPTION_KEY_LENGTH])
|
||||
void EspnowMeshBackend::setEspnowMessageEncryptionKey(const uint8_t espnowMessageEncryptionKey[experimental::crypto::ENCRYPTION_KEY_LENGTH])
|
||||
{
|
||||
EspnowTransmitter::setEspnowMessageEncryptionKey(espnowMessageEncryptionKey);
|
||||
}
|
||||
@ -762,10 +762,10 @@ TransmissionStatusType EspnowMeshBackend::initiateTransmission(const String &mes
|
||||
assert(recipientInfo.BSSID() != nullptr); // We need at least the BSSID to connect
|
||||
recipientInfo.getBSSID(targetBSSID);
|
||||
|
||||
if(_conditionalPrinter.verboseMode()) // Avoid string generation if not required
|
||||
if(verboseMode()) // Avoid string generation if not required
|
||||
{
|
||||
printAPInfo(recipientInfo);
|
||||
_conditionalPrinter.verboseModePrint(emptyString);
|
||||
verboseModePrint(emptyString);
|
||||
}
|
||||
|
||||
return initiateTransmissionKernel(message, targetBSSID);
|
||||
@ -778,7 +778,7 @@ TransmissionStatusType EspnowMeshBackend::initiateTransmissionKernel(const Strin
|
||||
|
||||
uint32_t transmissionDuration = millis() - transmissionStartTime;
|
||||
|
||||
if(_conditionalPrinter.verboseMode() && transmissionResult == TransmissionStatusType::TRANSMISSION_COMPLETE) // Avoid calculations if not required
|
||||
if(verboseMode() && transmissionResult == TransmissionStatusType::TRANSMISSION_COMPLETE) // Avoid calculations if not required
|
||||
{
|
||||
totalDurationWhenSuccessful_AT += transmissionDuration;
|
||||
++successfulTransmissions_AT;
|
||||
@ -793,14 +793,14 @@ TransmissionStatusType EspnowMeshBackend::initiateTransmissionKernel(const Strin
|
||||
|
||||
void EspnowMeshBackend::printTransmissionStatistics() const
|
||||
{
|
||||
if(_conditionalPrinter.verboseMode() && successfulTransmissions_AT > 0) // Avoid calculations if not required
|
||||
if(verboseMode() && successfulTransmissions_AT > 0) // Avoid calculations if not required
|
||||
{
|
||||
_conditionalPrinter.verboseModePrint(String(F("Average duration of successful transmissions: ")) + String(totalDurationWhenSuccessful_AT/successfulTransmissions_AT) + String(F(" ms.")));
|
||||
_conditionalPrinter.verboseModePrint(String(F("Maximum duration of successful transmissions: ")) + String(maxTransmissionDuration_AT) + String(F(" ms.")));
|
||||
verboseModePrint(String(F("Average duration of successful transmissions: ")) + String(totalDurationWhenSuccessful_AT/successfulTransmissions_AT) + String(F(" ms.")));
|
||||
verboseModePrint(String(F("Maximum duration of successful transmissions: ")) + String(maxTransmissionDuration_AT) + String(F(" ms.")));
|
||||
}
|
||||
else
|
||||
{
|
||||
_conditionalPrinter.verboseModePrint(String(F("No successful transmission.")));
|
||||
verboseModePrint(String(F("No successful transmission.")));
|
||||
}
|
||||
}
|
||||
|
||||
@ -947,6 +947,15 @@ uint8_t EspnowMeshBackend::getBroadcastTransmissionRedundancy() const { return _
|
||||
void EspnowMeshBackend::setResponseTransmittedHook(const EspnowTransmitter::responseTransmittedHookType responseTransmittedHook) { _transmitter.setResponseTransmittedHook(responseTransmittedHook); }
|
||||
EspnowTransmitter::responseTransmittedHookType EspnowMeshBackend::getResponseTransmittedHook() const { return _transmitter.getResponseTransmittedHook(); }
|
||||
|
||||
EspnowDatabase *EspnowMeshBackend::getDatabase() { return &_database; }
|
||||
const EspnowDatabase *EspnowMeshBackend::getDatabaseConst() const { return &_database; }
|
||||
EspnowConnectionManager *EspnowMeshBackend::getConnectionManager() { return &_connectionManager; }
|
||||
const EspnowConnectionManager *EspnowMeshBackend::getConnectionManagerConst() const { return &_connectionManager; }
|
||||
EspnowTransmitter *EspnowMeshBackend::getTransmitter() { return &_transmitter; }
|
||||
const EspnowTransmitter *EspnowMeshBackend::getTransmitterConst() const { return &_transmitter; }
|
||||
EspnowEncryptionBroker *EspnowMeshBackend::getEncryptionBroker() { return &_encryptionBroker; }
|
||||
const EspnowEncryptionBroker *EspnowMeshBackend::getEncryptionBrokerConst() const { return &_encryptionBroker; }
|
||||
|
||||
void EspnowMeshBackend::sendStoredEspnowMessages(const ExpiringTimeTracker *estimatedMaxDurationTracker)
|
||||
{
|
||||
EspnowEncryptionBroker::sendPeerRequestConfirmations(estimatedMaxDurationTracker);
|
||||
@ -974,12 +983,12 @@ uint32_t EspnowMeshBackend::getMaxMessageLength()
|
||||
return EspnowTransmitter::getMaxMessageLength();
|
||||
}
|
||||
|
||||
void EspnowMeshBackend::setVerboseModeState(const bool enabled) {_conditionalPrinter.setVerboseModeState(enabled); ConditionalPrinter::setStaticVerboseModeState(enabled);}
|
||||
void EspnowMeshBackend::setVerboseModeState(const bool enabled) {(*getConditionalPrinter()).setVerboseModeState(enabled); ConditionalPrinter::setStaticVerboseModeState(enabled);}
|
||||
bool EspnowMeshBackend::verboseMode() const {return ConditionalPrinter::staticVerboseMode();}
|
||||
|
||||
void EspnowMeshBackend::verboseModePrint(const String &stringToPrint, const bool newline) const
|
||||
{
|
||||
_conditionalPrinter.verboseModePrint(stringToPrint, newline);
|
||||
(*getConditionalPrinterConst()).verboseModePrint(stringToPrint, newline);
|
||||
}
|
||||
|
||||
bool EspnowMeshBackend::staticVerboseMode() {return ConditionalPrinter::staticVerboseMode();}
|
||||
|
@ -92,7 +92,6 @@
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include "EspnowNetworkInfo.h"
|
||||
#include "CryptoInterface.h"
|
||||
|
||||
/**
|
||||
* An alternative to standard delay(). Will continuously call performEspnowMaintenance() during the waiting time, so that the ESP-NOW node remains responsive.
|
||||
@ -483,9 +482,9 @@ public:
|
||||
*
|
||||
* This changes the message encryption key for all EspnowMeshBackend instances on this ESP8266.
|
||||
*
|
||||
* @param espnowMessageEncryptionKey An array containing the CryptoInterface::ENCRYPTION_KEY_LENGTH bytes that will be used as the message encryption key.
|
||||
* @param espnowMessageEncryptionKey An array containing the experimental::crypto::ENCRYPTION_KEY_LENGTH bytes that will be used as the message encryption key.
|
||||
*/
|
||||
static void setEspnowMessageEncryptionKey(const uint8_t espnowMessageEncryptionKey[CryptoInterface::ENCRYPTION_KEY_LENGTH]);
|
||||
static void setEspnowMessageEncryptionKey(const uint8_t espnowMessageEncryptionKey[experimental::crypto::ENCRYPTION_KEY_LENGTH]);
|
||||
|
||||
/**
|
||||
* Change the key used to encrypt/decrypt messages when using AEAD encryption.
|
||||
@ -501,7 +500,7 @@ public:
|
||||
/**
|
||||
* Get the key used to encrypt/decrypt messages when using AEAD encryption.
|
||||
*
|
||||
* @return An uint8_t array with size CryptoInterface::ENCRYPTION_KEY_LENGTH containing the currently used message encryption key.
|
||||
* @return An uint8_t array with size experimental::crypto::ENCRYPTION_KEY_LENGTH containing the currently used message encryption key.
|
||||
*/
|
||||
static const uint8_t *getEspnowMessageEncryptionKey();
|
||||
|
||||
@ -935,6 +934,15 @@ public:
|
||||
|
||||
protected:
|
||||
|
||||
EspnowDatabase *getDatabase();
|
||||
const EspnowDatabase *getDatabaseConst() const;
|
||||
EspnowConnectionManager *getConnectionManager();
|
||||
const EspnowConnectionManager *getConnectionManagerConst() const;
|
||||
EspnowTransmitter *getTransmitter();
|
||||
const EspnowTransmitter *getTransmitterConst() const;
|
||||
EspnowEncryptionBroker *getEncryptionBroker();
|
||||
const EspnowEncryptionBroker *getEncryptionBrokerConst() const;
|
||||
|
||||
bool activateEspnow();
|
||||
|
||||
/*
|
||||
|
@ -114,7 +114,7 @@ namespace EspnowProtocolInterpreter
|
||||
uint64_t createSessionKey()
|
||||
{
|
||||
uint64_t newSessionKey = MeshUtilityFunctions::randomUint64();
|
||||
return usesEncryption(newSessionKey) ? newSessionKey : (newSessionKey | ((uint64_t)RANDOM_REG32) << 32 | uint64MSB); // TODO: Replace RANDOM_REG32 use with ESP.random().
|
||||
return usesEncryption(newSessionKey) ? newSessionKey : (newSessionKey | ((uint64_t)ESP.random()) << 32 | uint64MSB);
|
||||
}
|
||||
|
||||
macAndType_td createMacAndTypeValue(const uint64_t uint64Mac, const char messageType)
|
||||
|
@ -54,16 +54,16 @@ namespace EspnowProtocolInterpreter
|
||||
constexpr uint8_t transmissionMacIndex = 2;
|
||||
constexpr uint8_t messageIDIndex = 8;
|
||||
|
||||
constexpr uint8_t maxEncryptedConnections = 6; // This is limited by the ESP-NOW API. Max 6 in AP or AP+STA mode. Max 10 in STA mode. See "ESP-NOW User Guide" for more info.
|
||||
|
||||
constexpr uint8_t protocolBytesSize = 16;
|
||||
constexpr uint8_t aeadMetadataSize = 28;
|
||||
uint8_t metadataSize();
|
||||
uint32_t getMaxBytesPerTransmission();
|
||||
uint32_t getMaxMessageBytesPerTransmission();
|
||||
|
||||
constexpr uint8_t broadcastMac[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||
constexpr uint64_t uint64BroadcastMac = 0xFFFFFFFFFFFF;
|
||||
|
||||
constexpr uint8_t maxEncryptedConnections = 6; // This is limited by the ESP-NOW API. Max 6 in AP or AP+STA mode. Max 10 in STA mode. See "ESP-NOW User Guide" for more info.
|
||||
constexpr uint8_t broadcastMac[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||
|
||||
constexpr uint8_t encryptedConnectionKeyLength = 16; // This is restricted to exactly 16 bytes by the ESP-NOW API. It should not be changed unless the ESP-NOW API is changed.
|
||||
constexpr uint8_t hashKeyLength = 16; // This can be changed to any value up to 255. Common values are 16 and 32.
|
||||
@ -85,7 +85,7 @@ namespace EspnowProtocolInterpreter
|
||||
bool usesConstantSessionKey(const char messageType);
|
||||
|
||||
/**
|
||||
* Create a new session key for an encrypted connection using the built in RANDOM_REG32 of the ESP8266.
|
||||
* Create a new session key for an encrypted connection using the built in RANDOM_REG32/ESP.random() of the ESP8266.
|
||||
* Should only be used when initializing a new connection.
|
||||
* Use generateMessageID instead when the encrypted connection is already initialized to keep the connection synchronized.
|
||||
*
|
||||
|
@ -36,17 +36,17 @@ namespace
|
||||
std::shared_ptr<bool> _espnowTransmissionMutex = std::make_shared<bool>(false);
|
||||
std::shared_ptr<bool> _espnowSendToNodeMutex = std::make_shared<bool>(false);
|
||||
|
||||
uint32_t _espnowTransmissionTimeoutMs = 40;
|
||||
uint32_t _espnowRetransmissionIntervalMs = 15;
|
||||
|
||||
uint8_t _espnowMessageEncryptionKey[experimental::crypto::ENCRYPTION_KEY_LENGTH] = { 0 };
|
||||
bool _useEncryptedMessages = false;
|
||||
uint8_t _espnowMessageEncryptionKey[CryptoInterface::ENCRYPTION_KEY_LENGTH] = { 0 };
|
||||
|
||||
uint8_t _transmissionTargetBSSID[6] = {0};
|
||||
|
||||
bool _espnowSendConfirmed = false;
|
||||
|
||||
uint8_t _maxTransmissionsPerMessage = 3;
|
||||
|
||||
uint32_t _espnowTransmissionTimeoutMs = 40;
|
||||
uint32_t _espnowRetransmissionIntervalMs = 15;
|
||||
}
|
||||
|
||||
EspnowTransmitter::EspnowTransmitter(ConditionalPrinter &conditionalPrinterInstance, EspnowDatabase &databaseInstance, EspnowConnectionManager &connectionManagerInstance)
|
||||
@ -74,11 +74,11 @@ void EspnowTransmitter::setUseEncryptedMessages(const bool useEncryptedMessages)
|
||||
}
|
||||
bool EspnowTransmitter::useEncryptedMessages() { return _useEncryptedMessages; }
|
||||
|
||||
void EspnowTransmitter::setEspnowMessageEncryptionKey(const uint8_t espnowMessageEncryptionKey[CryptoInterface::ENCRYPTION_KEY_LENGTH])
|
||||
void EspnowTransmitter::setEspnowMessageEncryptionKey(const uint8_t espnowMessageEncryptionKey[experimental::crypto::ENCRYPTION_KEY_LENGTH])
|
||||
{
|
||||
assert(espnowMessageEncryptionKey != nullptr);
|
||||
|
||||
for(int i = 0; i < CryptoInterface::ENCRYPTION_KEY_LENGTH; ++i)
|
||||
for(int i = 0; i < experimental::crypto::ENCRYPTION_KEY_LENGTH; ++i)
|
||||
{
|
||||
_espnowMessageEncryptionKey[i] = espnowMessageEncryptionKey[i];
|
||||
}
|
||||
@ -86,7 +86,7 @@ void EspnowTransmitter::setEspnowMessageEncryptionKey(const uint8_t espnowMessag
|
||||
|
||||
void EspnowTransmitter::setEspnowMessageEncryptionKey(const String &espnowMessageEncryptionKeySeed)
|
||||
{
|
||||
MeshCryptoInterface::initializeKey(_espnowMessageEncryptionKey, CryptoInterface::ENCRYPTION_KEY_LENGTH, espnowMessageEncryptionKeySeed);
|
||||
MeshCryptoInterface::initializeKey(_espnowMessageEncryptionKey, experimental::crypto::ENCRYPTION_KEY_LENGTH, espnowMessageEncryptionKeySeed);
|
||||
}
|
||||
|
||||
const uint8_t *EspnowTransmitter::getEspnowMessageEncryptionKey()
|
||||
@ -331,7 +331,7 @@ TransmissionStatusType EspnowTransmitter::espnowSendToNodeUnsynchronized(const S
|
||||
{
|
||||
// chacha20Poly1305Encrypt encrypts transmission in place.
|
||||
// We are using the protocol bytes as a key salt.
|
||||
CryptoInterface::chacha20Poly1305Encrypt(transmission + espnowMetadataSize, transmissionSize - espnowMetadataSize, getEspnowMessageEncryptionKey(), transmission,
|
||||
experimental::crypto::ChaCha20Poly1305::encrypt(transmission + espnowMetadataSize, transmissionSize - espnowMetadataSize, getEspnowMessageEncryptionKey(), transmission,
|
||||
protocolBytesSize, transmission + protocolBytesSize, transmission + protocolBytesSize + 12);
|
||||
}
|
||||
|
||||
|
@ -18,11 +18,11 @@
|
||||
#define __ESPNOWTRANSMITTER_H__
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Crypto.h>
|
||||
#include "ExpiringTimeTracker.h"
|
||||
#include "EspnowDatabase.h"
|
||||
#include "EspnowConnectionManager.h"
|
||||
#include "ConditionalPrinter.h"
|
||||
#include "CryptoInterface.h"
|
||||
|
||||
class EspnowMeshBackend;
|
||||
|
||||
@ -57,7 +57,7 @@ public:
|
||||
|
||||
static void setUseEncryptedMessages(const bool useEncryptedMessages);
|
||||
static bool useEncryptedMessages();
|
||||
static void setEspnowMessageEncryptionKey(const uint8_t espnowMessageEncryptionKey[CryptoInterface::ENCRYPTION_KEY_LENGTH]);
|
||||
static void setEspnowMessageEncryptionKey(const uint8_t espnowMessageEncryptionKey[experimental::crypto::ENCRYPTION_KEY_LENGTH]);
|
||||
static void setEspnowMessageEncryptionKey(const String &espnowMessageEncryptionKeySeed);
|
||||
static const uint8_t *getEspnowMessageEncryptionKey();
|
||||
|
||||
@ -101,9 +101,9 @@ private:
|
||||
EspnowDatabase & _database;
|
||||
EspnowConnectionManager & _connectionManager;
|
||||
|
||||
uint8_t _broadcastTransmissionRedundancy = 1;
|
||||
|
||||
responseTransmittedHookType _responseTransmittedHook = [](const String &, const uint8_t *, uint32_t, EspnowMeshBackend &){ return true; };
|
||||
|
||||
uint8_t _broadcastTransmissionRedundancy = 1;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -69,10 +69,11 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
bool useCalculator = false;
|
||||
calculatorType _durationCalculator;
|
||||
|
||||
void setTimeout(const uint32_t newUserTimeout);
|
||||
|
||||
bool useCalculator = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -465,7 +465,7 @@ void FloodingMesh::_defaultNetworkFilter(const int numberOfNetworks, MeshBackend
|
||||
{
|
||||
if(EspnowMeshBackend *espnowInstance = TypeCast::meshBackendCast<EspnowMeshBackend *>(&meshInstance))
|
||||
{
|
||||
espnowInstance->connectionQueue().push_back(networkIndex);
|
||||
espnowInstance->connectionQueue().emplace_back(networkIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -312,13 +312,6 @@ private:
|
||||
|
||||
messageHandlerType _messageHandler;
|
||||
|
||||
uint16_t _messageCount = 0;
|
||||
uint16_t _messageLogSize = 100;
|
||||
|
||||
uint8_t _broadcastReceptionRedundancy = 2;
|
||||
|
||||
uint8_t _originMac[6] = {0};
|
||||
|
||||
std::map<uint64_t, uint8_t> _messageIDs = {};
|
||||
std::queue<messageQueueElementType> _messageIdOrder = {};
|
||||
std::list<std::pair<String, bool>> _forwardingBacklog = {};
|
||||
@ -331,6 +324,13 @@ private:
|
||||
bool _defaultBroadcastFilter(String &firstTransmission, EspnowMeshBackend &meshInstance);
|
||||
bool _defaultTransmissionOutcomesUpdateHook(MeshBackendBase &meshInstance);
|
||||
bool _defaultResponseTransmittedHook(const String &response, const uint8_t *recipientMac, const uint32_t responseIndex, EspnowMeshBackend &meshInstance);
|
||||
|
||||
uint8_t _originMac[6] = {0};
|
||||
|
||||
uint16_t _messageCount = 0;
|
||||
uint16_t _messageLogSize = 100;
|
||||
|
||||
uint8_t _broadcastReceptionRedundancy = 2;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -264,6 +264,9 @@ void MeshBackendBase::setAPHidden(const bool apHidden)
|
||||
|
||||
bool MeshBackendBase::getAPHidden() const {return _apHidden;}
|
||||
|
||||
ConditionalPrinter *MeshBackendBase::getConditionalPrinter() {return &_conditionalPrinter;}
|
||||
const ConditionalPrinter *MeshBackendBase::getConditionalPrinterConst() const {return &_conditionalPrinter;}
|
||||
|
||||
bool MeshBackendBase::latestTransmissionSuccessfulBase(const std::vector<TransmissionOutcome> &latestTransmissionOutcomes)
|
||||
{
|
||||
if(latestTransmissionOutcomes.empty())
|
||||
|
@ -291,6 +291,9 @@ public:
|
||||
|
||||
protected:
|
||||
|
||||
ConditionalPrinter *getConditionalPrinter();
|
||||
const ConditionalPrinter *getConditionalPrinterConst() const;
|
||||
|
||||
/**
|
||||
* @param latestTransmissionOutcomes The transmission outcomes vector to check.
|
||||
*
|
||||
@ -316,26 +319,27 @@ protected:
|
||||
|
||||
static std::shared_ptr<bool> _scanMutex;
|
||||
|
||||
ConditionalPrinter _conditionalPrinter;
|
||||
|
||||
private:
|
||||
|
||||
MeshBackendType _classType;
|
||||
|
||||
ConditionalPrinter _conditionalPrinter;
|
||||
|
||||
String _SSID;
|
||||
String _SSIDPrefix;
|
||||
String _SSIDRoot;
|
||||
String _SSIDSuffix;
|
||||
String _meshPassword;
|
||||
uint8 _meshWiFiChannel;
|
||||
String _message;
|
||||
bool _scanHidden = false;
|
||||
bool _apHidden = false;
|
||||
|
||||
requestHandlerType _requestHandler;
|
||||
responseHandlerType _responseHandler;
|
||||
networkFilterType _networkFilter;
|
||||
transmissionOutcomesUpdateHookType _transmissionOutcomesUpdateHook = [](MeshBackendBase &){return true;};
|
||||
|
||||
uint8 _meshWiFiChannel;
|
||||
bool _scanHidden = false;
|
||||
bool _apHidden = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -29,7 +29,7 @@ namespace MeshCryptoInterface
|
||||
{
|
||||
String createMeshHmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
|
||||
{
|
||||
return CryptoInterface::sha256Hmac(message, hashKey, hashKeyLength, hmacLength);
|
||||
return experimental::crypto::SHA256::hmac(message, hashKey, hashKeyLength, hmacLength);
|
||||
}
|
||||
|
||||
bool verifyMeshHmac(const String &message, const String &messageHmac, const uint8_t *hashKey, const uint8_t hashKeyLength)
|
||||
@ -43,9 +43,9 @@ namespace MeshCryptoInterface
|
||||
|
||||
uint8_t *initializeKey(uint8_t *key, const uint8_t keyLength, const String &keySeed)
|
||||
{
|
||||
assert(keyLength <= CryptoInterface::SHA256_NATURAL_LENGTH);
|
||||
uint8_t hashArray[CryptoInterface::SHA256_NATURAL_LENGTH] {};
|
||||
CryptoInterface::sha256Hash(keySeed.c_str(), keySeed.length(), hashArray);
|
||||
assert(keyLength <= experimental::crypto::SHA256::NATURAL_LENGTH);
|
||||
uint8_t hashArray[experimental::crypto::SHA256::NATURAL_LENGTH] {};
|
||||
experimental::crypto::SHA256::hash(keySeed.c_str(), keySeed.length(), hashArray);
|
||||
memcpy(key, hashArray, keyLength);
|
||||
return key;
|
||||
}
|
||||
|
@ -26,7 +26,7 @@
|
||||
#define __MESHCRYPTOINTERFACE_H__
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "CryptoInterface.h"
|
||||
#include <Crypto.h>
|
||||
|
||||
namespace MeshCryptoInterface
|
||||
{
|
||||
@ -50,11 +50,11 @@ namespace MeshCryptoInterface
|
||||
* @param message The string from which to create the HMAC.
|
||||
* @param hashKey The hash key to use when creating the HMAC.
|
||||
* @param hashKeyLength The length of the hash key in bytes.
|
||||
* @param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to 32. Defaults to CryptoInterface::SHA256_NATURAL_LENGTH.
|
||||
* @param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to 32. Defaults to experimental::crypto::SHA256::NATURAL_LENGTH.
|
||||
*
|
||||
* @return A String with the generated HMAC in HEX format.
|
||||
*/
|
||||
String createMeshHmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength = CryptoInterface::SHA256_NATURAL_LENGTH);
|
||||
String createMeshHmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength = experimental::crypto::SHA256::NATURAL_LENGTH);
|
||||
|
||||
/**
|
||||
* Verify a SHA256 HMAC which was created from the message using the provided hashKey.
|
||||
@ -72,7 +72,7 @@ namespace MeshCryptoInterface
|
||||
* Initialize key with a SHA-256 hash of keySeed.
|
||||
*
|
||||
* @param key A uint8_t array containing the key to be initialized.
|
||||
* @param keyLength The length of the key array in bytes. Maximum value is CryptoInterface::SHA256_NATURAL_LENGTH.
|
||||
* @param keyLength The length of the key array in bytes. Maximum value is experimental::crypto::SHA256::NATURAL_LENGTH.
|
||||
* @param keySeed The key seed.
|
||||
*
|
||||
* @return A pointer to the initialized key array.
|
||||
|
@ -48,10 +48,9 @@ public:
|
||||
private:
|
||||
|
||||
TimeTracker _timeTracker;
|
||||
String _totalMessage;
|
||||
uint8_t _transmissionsReceived = 0;
|
||||
uint8_t _transmissionsExpected;
|
||||
String _totalMessage;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -61,13 +61,13 @@ NetworkInfoBase::NetworkInfoBase(const uint8_t networkIndex)
|
||||
}
|
||||
|
||||
NetworkInfoBase::NetworkInfoBase(const String &SSID, const int32_t wifiChannel, const uint8_t BSSID[6], const uint8_t encryptionType, const int32_t RSSI, const bool isHidden) :
|
||||
_SSID(SSID), _wifiChannel(wifiChannel), _encryptionType(encryptionType), _RSSI(RSSI), _isHidden(isHidden)
|
||||
_SSID(SSID), _wifiChannel(wifiChannel), _RSSI(RSSI), _encryptionType(encryptionType), _isHidden(isHidden)
|
||||
{
|
||||
storeBSSID(BSSID);
|
||||
}
|
||||
|
||||
NetworkInfoBase::NetworkInfoBase(const NetworkInfoBase &other) : _SSID(other.SSID()), _wifiChannel(other.wifiChannel()), _encryptionType(other.encryptionType()),
|
||||
_RSSI(other.RSSI()), _isHidden(other.isHidden())
|
||||
NetworkInfoBase::NetworkInfoBase(const NetworkInfoBase &other) : _SSID(other.SSID()), _wifiChannel(other.wifiChannel()), _RSSI(other.RSSI()),
|
||||
_encryptionType(other.encryptionType()), _isHidden(other.isHidden())
|
||||
{
|
||||
storeBSSID(other.BSSID());
|
||||
}
|
||||
|
@ -70,11 +70,11 @@ public:
|
||||
bool isHidden() const;
|
||||
|
||||
static uint8_t * const defaultBSSID;
|
||||
static const uint8_t defaultEncryptionType;
|
||||
static const bool defaultIsHidden;
|
||||
static const String defaultSSID;
|
||||
static const int32_t defaultWifiChannel;
|
||||
static const uint8_t defaultEncryptionType;
|
||||
static const int32_t defaultRSSI;
|
||||
static const bool defaultIsHidden;
|
||||
|
||||
protected:
|
||||
|
||||
@ -90,12 +90,12 @@ protected:
|
||||
|
||||
private:
|
||||
|
||||
uint8_t _bssidArray[6] {0};
|
||||
uint8_t *_BSSID = defaultBSSID;
|
||||
String _SSID = defaultSSID;
|
||||
int32_t _wifiChannel = defaultWifiChannel;
|
||||
uint8_t _encryptionType = defaultEncryptionType; // see enum wl_enc_type for values
|
||||
int32_t _RSSI = defaultRSSI;
|
||||
uint8_t _bssidArray[6] {0};
|
||||
uint8_t _encryptionType = defaultEncryptionType; // see enum wl_enc_type for values
|
||||
bool _isHidden = defaultIsHidden;
|
||||
};
|
||||
|
||||
|
@ -33,15 +33,15 @@ namespace
|
||||
PeerRequestLog::PeerRequestLog(const uint64_t requestID, const bool requestEncrypted, const String &authenticationPassword, const uint8_t encryptedConnectionsSoftLimit,
|
||||
const String &peerRequestNonce, const uint8_t peerStaMac[6], const uint8_t peerApMac[6], const uint8_t hashKey[hashKeyLength])
|
||||
: EncryptedConnectionData(peerStaMac, peerApMac, 0, 0, EspnowMeshBackend::getEncryptionRequestTimeout(), hashKey),
|
||||
_requestID(requestID), _requestEncrypted(requestEncrypted), _authenticationPassword(authenticationPassword),
|
||||
_encryptedConnectionsSoftLimit(encryptedConnectionsSoftLimit), _peerRequestNonce(peerRequestNonce)
|
||||
_requestID(requestID), _authenticationPassword(authenticationPassword), _peerRequestNonce(peerRequestNonce)
|
||||
, _requestEncrypted(requestEncrypted), _encryptedConnectionsSoftLimit(encryptedConnectionsSoftLimit)
|
||||
{ }
|
||||
|
||||
PeerRequestLog::PeerRequestLog(const uint64_t requestID, const bool requestEncrypted, const String &authenticationPassword, const uint8_t encryptedConnectionsSoftLimit, const String &peerRequestNonce,
|
||||
const uint8_t peerStaMac[6], const uint8_t peerApMac[6], const uint64_t peerSessionKey, const uint64_t ownSessionKey, const uint8_t hashKey[hashKeyLength])
|
||||
: EncryptedConnectionData(peerStaMac, peerApMac, peerSessionKey, ownSessionKey, EspnowMeshBackend::getEncryptionRequestTimeout(), hashKey),
|
||||
_requestID(requestID), _requestEncrypted(requestEncrypted), _authenticationPassword(authenticationPassword),
|
||||
_encryptedConnectionsSoftLimit(encryptedConnectionsSoftLimit), _peerRequestNonce(peerRequestNonce)
|
||||
_requestID(requestID), _authenticationPassword(authenticationPassword), _peerRequestNonce(peerRequestNonce)
|
||||
, _requestEncrypted(requestEncrypted), _encryptedConnectionsSoftLimit(encryptedConnectionsSoftLimit)
|
||||
{ }
|
||||
|
||||
void PeerRequestLog::setRequestID(const uint64_t requestID) { _requestID = requestID; }
|
||||
|
@ -56,10 +56,10 @@ public:
|
||||
private:
|
||||
|
||||
uint64_t _requestID;
|
||||
bool _requestEncrypted;
|
||||
String _authenticationPassword;
|
||||
uint8_t _encryptedConnectionsSoftLimit;
|
||||
String _peerRequestNonce;
|
||||
bool _requestEncrypted;
|
||||
uint8_t _encryptedConnectionsSoftLimit;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -54,10 +54,10 @@ private:
|
||||
|
||||
TimeTracker _timeTracker;
|
||||
|
||||
uint8_t _recipientMacArray[6] {0};
|
||||
uint8_t *_recipientMac = nullptr;
|
||||
String _message;
|
||||
uint64_t _requestID = 0;
|
||||
uint8_t _recipientMacArray[6] {0};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -254,15 +254,6 @@ protected:
|
||||
|
||||
private:
|
||||
|
||||
uint16_t _serverPort;
|
||||
WiFiServer _server;
|
||||
uint8_t _maxAPStations = 4; // Only affects TCP/IP connections, not ESP-NOW connections
|
||||
uint32_t _connectionAttemptTimeoutMs = 10000;
|
||||
int _stationModeTimeoutMs = 5000; // int is the type used in the Arduino core for this particular API, not uint32_t, which is why we use int here.
|
||||
uint32_t _apModeTimeoutMs = 4500;
|
||||
|
||||
bool useStaticIP;
|
||||
|
||||
void fullStop(WiFiClient &currClient);
|
||||
void initiateConnectionToAP(const String &targetSSID, const int targetChannel = NETWORK_INFO_DEFAULT_INT, const uint8_t *targetBSSID = NULL);
|
||||
TransmissionStatusType connectToNode(const String &targetSSID, const int targetChannel = NETWORK_INFO_DEFAULT_INT, const uint8_t *targetBSSID = NULL);
|
||||
@ -272,6 +263,16 @@ private:
|
||||
TransmissionStatusType attemptDataTransferKernel();
|
||||
TransmissionStatusType initiateTransmission(const TcpIpNetworkInfo &recipientInfo);
|
||||
void enterPostTransmissionState(const bool concludingDisconnect);
|
||||
|
||||
uint32_t _connectionAttemptTimeoutMs = 10000;
|
||||
int _stationModeTimeoutMs = 5000; // int is the type used in the Arduino core for this particular API, not uint32_t, which is why we use int here.
|
||||
uint32_t _apModeTimeoutMs = 4500;
|
||||
|
||||
WiFiServer _server;
|
||||
uint16_t _serverPort;
|
||||
uint8_t _maxAPStations = 4; // Only affects TCP/IP connections, not ESP-NOW connections
|
||||
|
||||
bool useStaticIP;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -27,14 +27,9 @@
|
||||
#include "MeshBackendBase.h"
|
||||
#include "TcpIpMeshBackend.h"
|
||||
#include "EspnowMeshBackend.h"
|
||||
#include "TypeConversion.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr char chars[36] PROGMEM = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
|
||||
constexpr uint8_t charValues[75] PROGMEM {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, // 0 to 9
|
||||
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 0, 0, 0, 0, 0, 0, // Upper case letters
|
||||
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35}; // Lower case letters
|
||||
}
|
||||
using namespace experimental::TypeConversion;
|
||||
|
||||
namespace MeshTypeConversionFunctions
|
||||
{
|
||||
@ -47,14 +42,14 @@ namespace MeshTypeConversionFunctions
|
||||
if(base == 16)
|
||||
{
|
||||
do {
|
||||
result += (char)pgm_read_byte(chars + number % base);
|
||||
result += (char)pgm_read_byte(base36Chars + number % base);
|
||||
number >>= 4; // We could write number /= 16; and the compiler would optimize it to a shift, but the explicit shift notation makes it clearer where the speed-up comes from.
|
||||
} while ( number );
|
||||
}
|
||||
else
|
||||
{
|
||||
do {
|
||||
result += (char)pgm_read_byte(chars + number % base);
|
||||
result += (char)pgm_read_byte(base36Chars + number % base);
|
||||
number /= base;
|
||||
} while ( number );
|
||||
}
|
||||
@ -75,7 +70,7 @@ namespace MeshTypeConversionFunctions
|
||||
for(uint32_t i = 0; i < string.length(); ++i)
|
||||
{
|
||||
result <<= 4; // We could write result *= 16; and the compiler would optimize it to a shift, but the explicit shift notation makes it clearer where the speed-up comes from.
|
||||
result += pgm_read_byte(charValues + string.charAt(i) - '0');
|
||||
result += pgm_read_byte(base36CharValues + string.charAt(i) - '0');
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -83,7 +78,7 @@ namespace MeshTypeConversionFunctions
|
||||
for(uint32_t i = 0; i < string.length(); ++i)
|
||||
{
|
||||
result *= base;
|
||||
result += pgm_read_byte(charValues + string.charAt(i) - '0');
|
||||
result += pgm_read_byte(base36CharValues + string.charAt(i) - '0');
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,29 +87,12 @@ namespace MeshTypeConversionFunctions
|
||||
|
||||
String uint8ArrayToHexString(const uint8_t *uint8Array, const uint32_t arrayLength)
|
||||
{
|
||||
String hexString;
|
||||
if(!hexString.reserve(2*arrayLength)) // Each uint8_t will become two characters (00 to FF)
|
||||
return emptyString;
|
||||
|
||||
for(uint32_t i = 0; i < arrayLength; ++i)
|
||||
{
|
||||
hexString += (char)pgm_read_byte(chars + (uint8Array[i] >> 4));
|
||||
hexString += (char)pgm_read_byte(chars + uint8Array[i] % 16 );
|
||||
}
|
||||
|
||||
return hexString;
|
||||
return experimental::TypeConversion::uint8ArrayToHexString(uint8Array, arrayLength);
|
||||
}
|
||||
|
||||
uint8_t *hexStringToUint8Array(const String &hexString, uint8_t *uint8Array, const uint32_t arrayLength)
|
||||
{
|
||||
assert(hexString.length() >= arrayLength*2); // Each array element can hold two hexString characters
|
||||
|
||||
for(uint32_t i = 0; i < arrayLength; ++i)
|
||||
{
|
||||
uint8Array[i] = (pgm_read_byte(charValues + hexString.charAt(i*2) - '0') << 4) + pgm_read_byte(charValues + hexString.charAt(i*2 + 1) - '0');
|
||||
}
|
||||
|
||||
return uint8Array;
|
||||
return experimental::TypeConversion::hexStringToUint8Array(hexString, uint8Array, arrayLength);
|
||||
}
|
||||
|
||||
String uint8ArrayToMultiString(uint8_t *uint8Array, const uint32_t arrayLength)
|
||||
@ -193,24 +171,12 @@ namespace MeshTypeConversionFunctions
|
||||
|
||||
uint8_t *uint64ToUint8Array(const uint64_t value, uint8_t *resultArray)
|
||||
{
|
||||
resultArray[7] = value;
|
||||
resultArray[6] = value >> 8;
|
||||
resultArray[5] = value >> 16;
|
||||
resultArray[4] = value >> 24;
|
||||
resultArray[3] = value >> 32;
|
||||
resultArray[2] = value >> 40;
|
||||
resultArray[1] = value >> 48;
|
||||
resultArray[0] = value >> 56;
|
||||
|
||||
return resultArray;
|
||||
return uint64ToUint8ArrayBE(value, resultArray);
|
||||
}
|
||||
|
||||
uint64_t uint8ArrayToUint64(const uint8_t *inputArray)
|
||||
{
|
||||
uint64_t result = (uint64_t)inputArray[0] << 56 | (uint64_t)inputArray[1] << 48 | (uint64_t)inputArray[2] << 40 | (uint64_t)inputArray[3] << 32
|
||||
| (uint64_t)inputArray[4] << 24 | (uint64_t)inputArray[5] << 16 | (uint64_t)inputArray[6] << 8 | (uint64_t)inputArray[7];
|
||||
|
||||
return result;
|
||||
return uint8ArrayToUint64BE(inputArray);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -137,7 +137,7 @@ namespace MeshTypeConversionFunctions
|
||||
uint8_t *uint64ToMac(const uint64_t macValue, uint8_t *macArray);
|
||||
|
||||
/**
|
||||
* Takes a uint64_t value and stores the bits in a uint8_t array. Assumes index 0 of the array should contain MSB.
|
||||
* Takes a uint64_t value and stores the bits in a uint8_t array. Assumes index 0 of the array should contain MSB (big endian).
|
||||
*
|
||||
* @param value The uint64_t value to convert to a uint8_t array.
|
||||
* @param resultArray A uint8_t array that will hold the result once the function returns. Should have a size of at least 8 bytes.
|
||||
@ -146,7 +146,7 @@ namespace MeshTypeConversionFunctions
|
||||
uint8_t *uint64ToUint8Array(const uint64_t value, uint8_t *resultArray);
|
||||
|
||||
/**
|
||||
* Takes a uint8_t array and converts the first 8 (lowest index) elements to a uint64_t. Assumes index 0 of the array contains MSB.
|
||||
* Takes a uint8_t array and converts the first 8 (lowest index) elements to a uint64_t. Assumes index 0 of the array contains MSB (big endian).
|
||||
*
|
||||
* @param inputArray A uint8_t array containing the data to convert to a uint64_t. Should have a size of at least 8 bytes.
|
||||
* @return A uint64_t representation of the first 8 bytes of the array.
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
#include "UtilityFunctions.h"
|
||||
#include <esp8266_peri.h>
|
||||
#include <Esp.h>
|
||||
|
||||
namespace MeshUtilityFunctions
|
||||
{
|
||||
@ -43,7 +44,7 @@ namespace MeshUtilityFunctions
|
||||
|
||||
uint64_t randomUint64()
|
||||
{
|
||||
return (((uint64_t)RANDOM_REG32 << 32) | (uint64_t)RANDOM_REG32);
|
||||
return (((uint64_t)ESP.random() << 32) | (uint64_t)ESP.random());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
Loading…
x
Reference in New Issue
Block a user