1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-22 21:23:07 +03:00

- Generalize CryptoInterface.

- Add more HMAC and hash functions to CryptoInterface.

- Add MeshCryptoInterface as a holder of mesh specific crypto functionality.

- Rename broadcastMetadataDelimiter to metadataDelimiter in FloodingMesh since it is not just used for broadcasts, and to save some typing.
This commit is contained in:
Anders 2019-11-10 21:50:43 +01:00
parent 3132325bf8
commit 2fef67dcb0
9 changed files with 1055 additions and 138 deletions

View File

@ -55,7 +55,7 @@ bool useLED = false; // Change this to true if you wish the onboard LED to mark
@return True if this node should forward the received message to other nodes. False otherwise.
*/
bool meshMessageHandler(String &message, FloodingMesh &meshInstance) {
int32_t delimiterIndex = message.indexOf(meshInstance.broadcastMetadataDelimiter());
int32_t delimiterIndex = message.indexOf(meshInstance.metadataDelimiter());
if (delimiterIndex == 0) {
Serial.print("Message received from STA MAC " + meshInstance.getEspnowMeshBackend().getSenderMac() + ": ");
Serial.println(message.substring(2, 102));
@ -172,7 +172,7 @@ void loop() {
ledState = ledState ^ bool(benchmarkCount); // Make other nodes' LEDs alternate between on and off once benchmarking begins.
// Note: The maximum length of an unencrypted broadcast message is given by floodingMesh.maxUnencryptedMessageSize(). It is around 670 bytes by default.
floodingMesh.broadcast(String(floodingMesh.broadcastMetadataDelimiter()) + String(ledState) + theOneMac + " is The One.");
floodingMesh.broadcast(String(floodingMesh.metadataDelimiter()) + String(ledState) + theOneMac + " is The One.");
Serial.println("Proclamation broadcast done in " + String(millis() - startTime) + " ms.");
timeOfLastProclamation = millis();
@ -181,7 +181,7 @@ void loop() {
if (millis() - loopStart > 23000) { // Start benchmarking the mesh once three proclamations have been made
uint32_t startTime = millis();
floodingMesh.broadcast(String(benchmarkCount++) + String(floodingMesh.broadcastMetadataDelimiter()) + ": Not a spoon in sight.");
floodingMesh.broadcast(String(benchmarkCount++) + String(floodingMesh.metadataDelimiter()) + ": Not a spoon in sight.");
Serial.println("Benchmark broadcast done in " + String(millis() - startTime) + " ms.");
floodingMeshDelay(20);
}

View File

@ -28,9 +28,14 @@
#include <bearssl/bearssl.h>
namespace CryptoInterface
namespace
{
uint8_t *createBearsslHmac(const String &message, const uint8_t *hashKey, uint8_t hashKeyLength, uint8_t *resultArray, size_t resultArrayLength)
size_t _ctMinDataLength = 0;
size_t _ctMaxDataLength = 1024;
bool _warningsEnabled = true;
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 resultArrayLength)
{
assert(1 <= resultArrayLength);
@ -38,13 +43,9 @@ namespace CryptoInterface
// 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.
// hashType alternatives: &br_md5_vtable; &br_sha1_vtable; &br_sha224_vtable; &br_sha256_vtable; &br_sha384_vtable; &br_sha512_vtable;
// Use SHA256 to create the hash.
const br_hash_class *hashType = &br_sha256_vtable;
br_hmac_key_context keyContext; // Holds general HMAC info
br_hmac_context hmacContext; // Holds general HMAC info + specific info for the current message
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.
@ -57,9 +58,9 @@ namespace CryptoInterface
br_hmac_init(&hmacContext, &keyContext, resultArrayLength);
// Provide the HMAC context with the data to create a HMAC from.
// The provided message.length() bytes are injected as extra input in the HMAC computation incarnated by the hmacContext.
// It is acceptable that message.length() is zero, in which case data is ignored (and may be NULL) and this function does nothing.
br_hmac_update(&hmacContext, message.c_str(), message.length());
// 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).
@ -70,29 +71,27 @@ namespace CryptoInterface
return resultArray;
}
String createBearsslHmac(const String &message, const uint8_t *hashKey, uint8_t hashKeyLength, size_t hmacLength)
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 <= SHA256HMAC_NATURAL_LENGTH);
byte hmac[hmacLength];
createBearsslHmac(message, hashKey, hashKeyLength, hmac, hmacLength);
assert(1 <= hmacLength && hmacLength <= hashTypeNaturalLength);
uint8_t hmac[hmacLength];
createBearsslHmac(hashType, message.c_str(), message.length(), hashKey, hashKeyLength, hmac, hmacLength);
return uint8ArrayToHexString(hmac, hmacLength);
}
uint8_t *createBearsslHmacCT(const String &message, const uint8_t *hashKey, uint8_t hashKeyLength, uint8_t *resultArray, size_t resultArrayLength)
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 resultArrayLength)
{
assert(1 <= resultArrayLength);
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.
// hashType alternatives: &br_md5_vtable; &br_sha1_vtable; &br_sha224_vtable; &br_sha256_vtable; &br_sha384_vtable; &br_sha512_vtable;
// Use SHA256 to create the hash.
const br_hash_class *hashType = &br_sha256_vtable;
br_hmac_key_context keyContext; // Holds general HMAC info
br_hmac_context hmacContext; // Holds general HMAC info + specific info for the current message
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.
@ -105,43 +104,323 @@ namespace CryptoInterface
br_hmac_init(&hmacContext, &keyContext, resultArrayLength);
// Provide the HMAC context with the data to create a HMAC from.
// The provided message.length() bytes are injected as extra input in the HMAC computation incarnated by the hmacContext.
// It is acceptable that message.length() is zero, in which case data is ignored (and may be NULL) and this function does nothing.
// 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, message.c_str(), message.length());
// br_hmac_update(&hmacContext, data, dataLength);
// Compute the HMAC output. Assumes message is minimum 0 bytes and maximum 1000 bytes.
// 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 message.length() bytes pointed to by message.c_str(). The message.length() parameter must lie between min_len and max_len (inclusive);
// max_len bytes are actually read from data (indicating each data byte can be read multiple times, if message.length() < max_len).
// Computing time (and memory access pattern) will not depend upon the data byte contents or the value of len.
// 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 max_len - min_len MUST be less than 2^30 (i.e. about one gigabyte).
// 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.
size_t min_len = 0;
size_t max_len = 1000;
assert(min_len <= message.length() && message.length() <= max_len);
br_hmac_outCT(&hmacContext, message.c_str(), message.length(), min_len, max_len, resultArray); // returns size_t outputLength
br_hmac_outCT(&hmacContext, data, dataLength, _ctMinDataLength, _ctMaxDataLength, resultArray); // returns size_t outputLength
return resultArray;
}
String createBearsslHmacCT(const String &message, const uint8_t *hashKey, uint8_t hashKeyLength, size_t hmacLength)
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 <= SHA256HMAC_NATURAL_LENGTH);
byte hmac[hmacLength];
createBearsslHmacCT(message, hashKey, hashKeyLength, hmac, hmacLength);
assert(1 <= hmacLength && hmacLength <= hashTypeNaturalLength);
uint8_t hmac[hmacLength];
createBearsslHmacCT(hashType, message.c_str(), message.length(), hashKey, hashKeyLength, hmac, hmacLength);
return uint8ArrayToHexString(hmac, hmacLength);
}
bool verifyBearsslHmac(const String &message, const String &messageHmac, const uint8_t *hashKey, uint8_t hashKeyLength)
{
String generatedHmac = createBearsslHmac(message, hashKey, hashKeyLength, messageHmac.length()/2); // We know that each HMAC byte should become 2 String characters due to uint8ArrayToHexString.
if(generatedHmac == messageHmac)
return true;
else
return false;
}
}
namespace CryptoInterface
{
void setCtMinDataLength(const size_t ctMinDataLength)
{
assert(ctMaxDataLength() - ctMinDataLength <= ctMaxDiff);
_ctMinDataLength = ctMinDataLength;
}
size_t ctMinDataLength() {return _ctMinDataLength;}
void setCtMaxDataLength(const size_t ctMaxDataLength)
{
assert(ctMaxDataLength - ctMinDataLength() <= ctMaxDiff);
_ctMaxDataLength = ctMaxDataLength;
}
size_t ctMaxDataLength() {return _ctMaxDataLength;}
void setWarningsEnabled(bool warningsEnabled) { _warningsEnabled = warningsEnabled; }
bool warningsEnabled() { return _warningsEnabled; }
// #################### MD5 ####################
// resultArray must have size MD5_NATURAL_LENGTH or greater
void *md5Hash(const void *data, const size_t dataLength, void *resultArray)
{
if(warningsEnabled())
Serial.println(F("\nWARNING! The MD5 hash is broken in terms of attacker resistance.\n"
"Only use it in those cases where attacker resistance is not important. Prefer SHA-256 or higher otherwise.\n"
"Use CryptoInterface::setWarningsEnabled(false) to turn off this warning.\n"));
br_md5_context context;
br_md5_init(&context);
br_md5_update(&context, data, dataLength);
br_md5_out(&context, resultArray);
return resultArray;
}
String md5Hash(const String &message)
{
uint8_t hash[MD5_NATURAL_LENGTH];
md5Hash(message.c_str(), message.length(), hash);
return 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 resultArrayLength)
{
return createBearsslHmac(&br_md5_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, resultArrayLength);
}
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 resultArrayLength)
{
return createBearsslHmacCT(&br_md5_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, resultArrayLength);
}
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)
{
if(warningsEnabled())
Serial.println(F("\nWARNING! The SHA-1 hash is broken in terms of attacker resistance.\n"
"Only use it in those cases where attacker resistance is not important. Prefer SHA-256 or higher otherwise.\n"
"Use CryptoInterface::setWarningsEnabled(false) to turn off this warning.\n"));
br_sha1_context context;
br_sha1_init(&context);
br_sha1_update(&context, data, dataLength);
br_sha1_out(&context, resultArray);
return resultArray;
}
String sha1Hash(const String &message)
{
uint8_t hash[SHA1_NATURAL_LENGTH];
sha1Hash(message.c_str(), message.length(), hash);
return 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 resultArrayLength)
{
return createBearsslHmac(&br_sha1_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, resultArrayLength);
}
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 resultArrayLength)
{
return createBearsslHmacCT(&br_sha1_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, resultArrayLength);
}
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 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 resultArrayLength)
{
return createBearsslHmac(&br_sha224_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, resultArrayLength);
}
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 resultArrayLength)
{
return createBearsslHmacCT(&br_sha224_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, resultArrayLength);
}
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 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 resultArrayLength)
{
return createBearsslHmac(&br_sha256_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, resultArrayLength);
}
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 resultArrayLength)
{
return createBearsslHmacCT(&br_sha256_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, resultArrayLength);
}
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 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 resultArrayLength)
{
return createBearsslHmac(&br_sha384_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, resultArrayLength);
}
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 resultArrayLength)
{
return createBearsslHmacCT(&br_sha384_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, resultArrayLength);
}
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 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 resultArrayLength)
{
return createBearsslHmac(&br_sha512_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, resultArrayLength);
}
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 resultArrayLength)
{
return createBearsslHmacCT(&br_sha512_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, resultArrayLength);
}
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 uint8ArrayToHexString(hash, MD5SHA1_NATURAL_LENGTH);
}
}

View File

@ -23,98 +23,622 @@
* THE SOFTWARE.
*/
#include <Arduino.h>
#ifndef __ESP8266ARDUINOCRYPTOINTERFACE_H__
#define __ESP8266ARDUINOCRYPTOINTERFACE_H__
#ifndef __MESHCRYPTOINTERFACE_H__
#define __MESHCRYPTOINTERFACE_H__
#include <Arduino.h>
namespace CryptoInterface
{
const uint8_t SHA256HMAC_NATURAL_LENGTH = 32;
/**
* Create a SHA256 HMAC from the message, using the provided hashKey. The result will be resultArrayLength bytes long and stored in resultArray.
* Uses the BearSSL cryptographic library.
* Regarding constant-time (CT) HMAC:
*
* @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 resultArray The array wherein to store the resulting HMAC.
* @param resultArrayLength The length of resultArray in bytes. Determines the HMAC length. If resultArrayLength is greater than SHA256HMAC_NATURAL_LENGTH,
* the first (lowest index) SHA256HMAC_NATURAL_LENGTH bytes of resultArray will be used for the 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
*
* @return A pointer to resultArray.
*/
uint8_t *createBearsslHmac(const String &message, const uint8_t *hashKey, uint8_t hashKeyLength, uint8_t *resultArray, size_t resultArrayLength);
/**
* 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 32. Defaults to SHA256HMAC_NATURAL_LENGTH.
*
* @return A String with the generated HMAC in HEX format.
*/
String createBearsslHmac(const String &message, const uint8_t *hashKey, uint8_t hashKeyLength, size_t hmacLength = SHA256HMAC_NATURAL_LENGTH);
/**
* Create a SHA256 HMAC from the message, using the provided hashKey. The result will be resultArrayLength bytes long and stored in resultArray.
* Uses the BearSSL cryptographic library.
*
* Constant-time version of createBearsslHmac(). More constant-time info here: https://www.bearssl.org/constanttime.html
* For small messages, it takes substantially longer time to complete than a normal HMAC (5 ms vs 2 ms in a quick benchmark,
* determined by the difference between min and max allowed message length), and it also sets a maximum length that messages can be (set to 1000 bytes here).
* Making the fixed max length variable would defeat the whole purpose of using constant-time, and not making it variable would create the wrong HMAC if message size exceeds the maximum.
*
* Also, HMAC is already partially constant-time. Quoting the link above:
* 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 ctMaxDataLength(), constant-time processing takes substantially longer time to complete than a normal HMAC,
* determined by the size of (ctMaxDataLength() - ctMinDataLength()).
* Constant-time processing also sets limits on the data length.
*
* Thus the non constant-time version is used within the mesh framework instead.
* 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.
*/
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 uint32_t ctMaxDiff = 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.
*
* @param message The string from which to create the HMAC. Min size 0 bytes. Max size 1000 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 resultArrayLength The length of resultArray in bytes. Determines the HMAC length. If resultArrayLength is greater than SHA256HMAC_NATURAL_LENGTH,
* the first (lowest index) SHA256HMAC_NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
* The difference ctMaxDataLength() - ctMinDataLength() MUST be less than 2^30 (i.e. about one gigabyte).
*/
void setCtMinDataLength(const size_t ctMinDataLength);
/**
* 0 by default.
*/
size_t ctMinDataLength();
/**
* 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 ctMaxDataLength() - ctMinDataLength() MUST be less than 2^30 (i.e. about one gigabyte).
*/
void setCtMaxDataLength(const size_t ctMaxDataLength);
/**
* 1024 by default.
*/
size_t ctMaxDataLength();
/**
* Turn on or off warning Serial prints from the CryptoInterface functions.
*
* @param warningsEnabled If true, warnings will be printed to Serial.
*/
void setWarningsEnabled(bool warningsEnabled);
bool warningsEnabled();
// #################### MD5 ####################
/**
* 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.
*/
uint8_t *createBearsslHmacCT(const String &message, const uint8_t *hashKey, uint8_t hashKeyLength, uint8_t *resultArray, size_t resultArrayLength);
void *md5Hash(const void *data, const size_t dataLength, void *resultArray);
/**
* 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);
/**
* Create a MD5 HMAC from the data, using the provided hashKey. The result will be resultArrayLength 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 resultArrayLength The length of resultArray in bytes. Determines the HMAC length. If resultArrayLength is greater than MD5_NATURAL_LENGTH,
* the first (lowest index) MD5_NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
*
* @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 resultArrayLength);
/**
* 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 resultArrayLength 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 [ctMinDataLength(), ctMaxDataLength()].
* @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 resultArrayLength The length of resultArray in bytes. Determines the HMAC length. If resultArrayLength is greater than MD5_NATURAL_LENGTH,
* the first (lowest index) MD5_NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
*
* @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 resultArrayLength);
/**
* 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 [ctMinDataLength(), ctMaxDataLength()].
* @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 ####################
/**
* 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);
/**
* 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);
/**
* Create a SHA1 HMAC from the data, using the provided hashKey. The result will be resultArrayLength 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 resultArrayLength The length of resultArray in bytes. Determines the HMAC length. If resultArrayLength is greater than SHA1_NATURAL_LENGTH,
* the first (lowest index) SHA1_NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
*
* @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 resultArrayLength);
/**
* 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 resultArrayLength 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 [ctMinDataLength(), ctMaxDataLength()].
* @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 resultArrayLength The length of resultArray in bytes. Determines the HMAC length. If resultArrayLength is greater than SHA1_NATURAL_LENGTH,
* the first (lowest index) SHA1_NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
*
* @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 resultArrayLength);
/**
* 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 [ctMinDataLength(), ctMaxDataLength()].
* @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 resultArrayLength 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 resultArrayLength The length of resultArray in bytes. Determines the HMAC length. If resultArrayLength is greater than SHA224_NATURAL_LENGTH,
* the first (lowest index) SHA224_NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
*
* @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 resultArrayLength);
/**
* 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 resultArrayLength 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 [ctMinDataLength(), ctMaxDataLength()].
* @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 resultArrayLength The length of resultArray in bytes. Determines the HMAC length. If resultArrayLength is greater than SHA224_NATURAL_LENGTH,
* the first (lowest index) SHA224_NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
*
* @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 resultArrayLength);
/**
* 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 [ctMinDataLength(), ctMaxDataLength()].
* @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 resultArrayLength 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 resultArrayLength The length of resultArray in bytes. Determines the HMAC length. If resultArrayLength is greater than SHA256_NATURAL_LENGTH,
* the first (lowest index) SHA256_NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
*
* @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 resultArrayLength);
/**
* 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.
*
* Constant-time version of createBearsslHmac(). More constant-time info here: https://www.bearssl.org/constanttime.html
* Not used within the mesh framework for reasons outlined in the uint8_t *createBearsslHmacCT() description.
*
* @param message The string from which to create the HMAC. Min size 0 bytes. Max size 1000 bytes.
* @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 SHA256HMAC_NATURAL_LENGTH.
* @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 createBearsslHmacCT(const String &message, const uint8_t *hashKey, uint8_t hashKeyLength, size_t hmacLength = SHA256HMAC_NATURAL_LENGTH);
String sha256Hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
/**
* Verify a SHA256 HMAC which was created from the message using the provided hashKey.
* Create a SHA256 HMAC from the data, using the provided hashKey. The result will be resultArrayLength bytes long and stored in resultArray.
* Constant-time version.
* Uses the BearSSL cryptographic library.
*
* @param message The string from which the HMAC was created.
* @param messageHmac A string with the generated HMAC in HEX format. Valid messageHmac.length() is 2 to 64.
* @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 [ctMinDataLength(), ctMaxDataLength()].
* @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 resultArrayLength The length of resultArray in bytes. Determines the HMAC length. If resultArrayLength is greater than SHA256_NATURAL_LENGTH,
* the first (lowest index) SHA256_NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
*
* @return True if the HMAC is correct. False otherwise.
* @return A pointer to resultArray.
*/
bool verifyBearsslHmac(const String &message, const String &messageHmac, const uint8_t *hashKey, uint8_t hashKeyLength);
void *sha256HmacCT(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t resultArrayLength);
/**
* 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 [ctMinDataLength(), ctMaxDataLength()].
* @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 resultArrayLength 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 resultArrayLength The length of resultArray in bytes. Determines the HMAC length. If resultArrayLength is greater than SHA384_NATURAL_LENGTH,
* the first (lowest index) SHA384_NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
*
* @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 resultArrayLength);
/**
* 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 resultArrayLength 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 [ctMinDataLength(), ctMaxDataLength()].
* @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 resultArrayLength The length of resultArray in bytes. Determines the HMAC length. If resultArrayLength is greater than SHA384_NATURAL_LENGTH,
* the first (lowest index) SHA384_NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
*
* @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 resultArrayLength);
/**
* 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 [ctMinDataLength(), ctMaxDataLength()].
* @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 resultArrayLength 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 resultArrayLength The length of resultArray in bytes. Determines the HMAC length. If resultArrayLength is greater than SHA512_NATURAL_LENGTH,
* the first (lowest index) SHA512_NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
*
* @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 resultArrayLength);
/**
* 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 resultArrayLength 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 [ctMinDataLength(), ctMaxDataLength()].
* @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 resultArrayLength The length of resultArray in bytes. Determines the HMAC length. If resultArrayLength is greater than SHA512_NATURAL_LENGTH,
* the first (lowest index) SHA512_NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
*
* @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 resultArrayLength);
/**
* 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 [ctMinDataLength(), ctMaxDataLength()].
* @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);
}
#endif

View File

@ -26,7 +26,7 @@
#include "UtilityFunctions.h"
#include "TypeConversionFunctions.h"
#include "JsonTranslator.h"
#include "CryptoInterface.h"
#include "MeshCryptoInterface.h"
using EspnowProtocolInterpreter::espnowHashKeyLength;
@ -128,7 +128,7 @@ uint64_t EncryptedConnectionData::getOwnSessionKey() const { return _ownSessionK
uint64_t EncryptedConnectionData::incrementSessionKey(uint64_t sessionKey, const uint8_t *hashKey, uint8_t hashKeyLength)
{
String hmac = CryptoInterface::createBearsslHmac(uint64ToString(sessionKey), hashKey, hashKeyLength);
String hmac = MeshCryptoInterface::createMeshHmac(uint64ToString(sessionKey), hashKey, hashKeyLength);
/* 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

View File

@ -28,7 +28,7 @@
std::set<FloodingMesh *> FloodingMesh::availableFloodingMeshes = {};
char FloodingMesh::_broadcastMetadataDelimiter = 23;
char FloodingMesh::_metadataDelimiter = 23;
void floodingMeshDelay(uint32_t durationMs)
{
@ -156,10 +156,10 @@ void FloodingMesh::broadcast(const String &message)
String messageID = generateMessageID();
// Remove getEspnowMeshBackend().getMeshName() from the broadcastMetadata below to broadcast to all ESP-NOW nodes regardless of MeshName.
// Remove getEspnowMeshBackend().getMeshName() from the metadata below to broadcast to all ESP-NOW nodes regardless of MeshName.
String targetMeshName = getEspnowMeshBackend().getMeshName();
broadcastKernel(targetMeshName + String(broadcastMetadataDelimiter()) + messageID + String(broadcastMetadataDelimiter()) + message);
broadcastKernel(targetMeshName + String(metadataDelimiter()) + messageID + String(metadataDelimiter()) + message);
}
void FloodingMesh::broadcastKernel(const String &message)
@ -180,7 +180,7 @@ void FloodingMesh::encryptedBroadcast(const String &message)
String messageID = generateMessageID();
encryptedBroadcastKernel(messageID + String(broadcastMetadataDelimiter()) + message);
encryptedBroadcastKernel(messageID + String(metadataDelimiter()) + message);
}
void FloodingMesh::encryptedBroadcastKernel(const String &message)
@ -232,15 +232,15 @@ void FloodingMesh::setMessageLogSize(uint16_t messageLogSize)
}
uint16_t FloodingMesh::messageLogSize() { return _messageLogSize; }
void FloodingMesh::setBroadcastMetadataDelimiter(char broadcastMetadataDelimiter)
void FloodingMesh::setMetadataDelimiter(char metadataDelimiter)
{
// Using HEX number characters as a delimiter is a bad idea regardless of broadcast type, since they are always in the broadcast metadata
assert(broadcastMetadataDelimiter < 48 || 57 < broadcastMetadataDelimiter);
assert(broadcastMetadataDelimiter < 65 || 70 < broadcastMetadataDelimiter);
assert(metadataDelimiter < 48 || 57 < metadataDelimiter);
assert(metadataDelimiter < 65 || 70 < metadataDelimiter);
_broadcastMetadataDelimiter = broadcastMetadataDelimiter;
_metadataDelimiter = metadataDelimiter;
}
char FloodingMesh::broadcastMetadataDelimiter() { return _broadcastMetadataDelimiter; }
char FloodingMesh::metadataDelimiter() { return _metadataDelimiter; }
EspnowMeshBackend &FloodingMesh::getEspnowMeshBackend()
{
@ -342,12 +342,12 @@ String FloodingMesh::_defaultRequestHandler(const String &request, MeshBackendBa
String broadcastTarget = "";
String remainingRequest = "";
if(request.charAt(0) == broadcastMetadataDelimiter())
if(request.charAt(0) == metadataDelimiter())
{
int32_t broadcastTargetEndIndex = request.indexOf(broadcastMetadataDelimiter(), 1);
int32_t broadcastTargetEndIndex = request.indexOf(metadataDelimiter(), 1);
if(broadcastTargetEndIndex == -1)
return ""; // broadcastMetadataDelimiter not found
return ""; // metadataDelimiter not found
broadcastTarget = request.substring(1, broadcastTargetEndIndex + 1); // Include delimiter
remainingRequest = request.substring(broadcastTargetEndIndex + 1);
@ -357,10 +357,10 @@ String FloodingMesh::_defaultRequestHandler(const String &request, MeshBackendBa
remainingRequest = request;
}
int32_t messageIDEndIndex = remainingRequest.indexOf(broadcastMetadataDelimiter());
int32_t messageIDEndIndex = remainingRequest.indexOf(metadataDelimiter());
if(messageIDEndIndex == -1)
return ""; // broadcastMetadataDelimiter not found
return ""; // metadataDelimiter not found
uint64_t messageID = stringToUint64(remainingRequest.substring(0, messageIDEndIndex));
@ -447,16 +447,16 @@ void FloodingMesh::_defaultNetworkFilter(int numberOfNetworks, MeshBackendBase &
*/
bool FloodingMesh::_defaultBroadcastFilter(String &firstTransmission, EspnowMeshBackend &meshInstance)
{
// This broadcastFilter will accept a transmission if it contains the broadcastMetadataDelimiter
// This broadcastFilter will accept a transmission if it contains the metadataDelimiter
// and as metaData either no targetMeshName or a targetMeshName that matches the MeshName of meshInstance
// and insertPreliminaryMessageID(messageID) returns true.
// Broadcast firstTransmission String structure: targetMeshName+messageID+message.
int32_t metadataEndIndex = firstTransmission.indexOf(broadcastMetadataDelimiter());
int32_t metadataEndIndex = firstTransmission.indexOf(metadataDelimiter());
if(metadataEndIndex == -1)
return false; // broadcastMetadataDelimiter not found
return false; // metadataDelimiter not found
String targetMeshName = firstTransmission.substring(0, metadataEndIndex);
@ -466,17 +466,17 @@ bool FloodingMesh::_defaultBroadcastFilter(String &firstTransmission, EspnowMesh
}
else
{
int32_t messageIDEndIndex = firstTransmission.indexOf(broadcastMetadataDelimiter(), metadataEndIndex + 1);
int32_t messageIDEndIndex = firstTransmission.indexOf(metadataDelimiter(), metadataEndIndex + 1);
if(messageIDEndIndex == -1)
return false; // broadcastMetadataDelimiter not found
return false; // metadataDelimiter not found
uint64_t messageID = stringToUint64(firstTransmission.substring(metadataEndIndex + 1, messageIDEndIndex));
if(insertPreliminaryMessageID(messageID))
{
// Add broadcast identifier to stored message and mark as accepted broadcast.
firstTransmission = String(broadcastMetadataDelimiter()) + firstTransmission;
firstTransmission = String(metadataDelimiter()) + firstTransmission;
return true;
}
else

View File

@ -217,11 +217,11 @@ public:
* Set the delimiter character used for metadata by every FloodingMesh instance.
* Using characters found in the mesh name or in HEX numbers is unwise, as is using ','.
*
* @param broadcastMetadataDelimiter The metadata delimiter character to use.
* @param metadataDelimiter The metadata delimiter character to use.
* Defaults to 23 = End-of-Transmission-Block (ETB) control character in ASCII
*/
static void setBroadcastMetadataDelimiter(char broadcastMetadataDelimiter);
static char broadcastMetadataDelimiter();
static void setMetadataDelimiter(char metadataDelimiter);
static char metadataDelimiter();
/*
* Gives you access to the EspnowMeshBackend used by the mesh node.
@ -274,7 +274,7 @@ private:
uint8_t _broadcastReceptionRedundancy = 2;
static char _broadcastMetadataDelimiter; // Defaults to 23 = End-of-Transmission-Block (ETB) control character in ASCII
static char _metadataDelimiter; // Defaults to 23 = End-of-Transmission-Block (ETB) control character in ASCII
uint8_t _originMac[6] = {0};

View File

@ -25,7 +25,7 @@
#include "JsonTranslator.h"
#include "EspnowProtocolInterpreter.h"
#include "TypeConversionFunctions.h"
#include "CryptoInterface.h"
#include "MeshCryptoInterface.h"
namespace JsonTranslator
{
@ -69,14 +69,14 @@ namespace JsonTranslator
uint8_t staMac[6] {0};
uint8_t apMac[6] {0};
String requesterStaApMac = macToString(WiFi.macAddress(staMac)) + macToString(WiFi.softAPmacAddress(apMac));
String hmac = CryptoInterface::createBearsslHmac(requesterStaApMac + mainMessage, hashKey, hashKeyLength);
String hmac = MeshCryptoInterface::createMeshHmac(requesterStaApMac + mainMessage, hashKey, hashKeyLength);
return mainMessage + createJsonEndPair(jsonHmac, hmac);
}
bool verifyEncryptionRequestHmac(const String &encryptionRequestHmacMessage, const uint8_t *requesterStaMac, const uint8_t *requesterApMac,
const uint8_t *hashKey, uint8_t hashKeyLength)
{
using namespace CryptoInterface;
using MeshCryptoInterface::verifyMeshHmac;
String hmac = "";
if(getHmac(encryptionRequestHmacMessage, hmac))
@ -85,8 +85,8 @@ namespace JsonTranslator
if(hmacStartIndex < 0)
return false;
if(hmac.length() == 2*SHA256HMAC_NATURAL_LENGTH // We know that each HMAC byte should become 2 String characters due to uint8ArrayToHexString.
&& verifyBearsslHmac(macToString(requesterStaMac) + macToString(requesterApMac) + encryptionRequestHmacMessage.substring(0, hmacStartIndex), hmac, hashKey, hashKeyLength))
if(hmac.length() == 2*CryptoInterface::SHA256_NATURAL_LENGTH // We know that each HMAC byte should become 2 String characters due to uint8ArrayToHexString.
&& verifyMeshHmac(macToString(requesterStaMac) + macToString(requesterApMac) + encryptionRequestHmacMessage.substring(0, hmacStartIndex), hmac, hashKey, hashKeyLength))
{
return true;
}

View File

@ -0,0 +1,42 @@
/*
* 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 "MeshCryptoInterface.h"
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);
}
bool verifyMeshHmac(const String &message, const String &messageHmac, const uint8_t *hashKey, uint8_t hashKeyLength)
{
String generatedHmac = createMeshHmac(message, hashKey, hashKeyLength, messageHmac.length()/2); // We know that each HMAC byte should become 2 String characters due to uint8ArrayToHexString.
if(generatedHmac == messageHmac)
return true;
else
return false;
}
}

View File

@ -0,0 +1,72 @@
/*
* 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 __MESHCRYPTOINTERFACE_H__
#define __MESHCRYPTOINTERFACE_H__
#include <Arduino.h>
#include "CryptoInterface.h"
namespace MeshCryptoInterface
{
/**
* There is a constant-time HMAC version available. More constant-time info here: https://www.bearssl.org/constanttime.html
* For small messages, it takes substantially longer time to complete than a normal HMAC (5 ms vs 2 ms in a quick benchmark,
* determined by the difference between min and max allowed message length), and it also sets a maximum length that messages can be (1024 bytes by default).
* Making the fixed max length variable would defeat the whole purpose of using constant-time, and not making it variable would create the wrong HMAC if message size exceeds the maximum.
*
* Also, 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."
*
* Thus the non constant-time version is used within the mesh framework instead.
*/
/**
* 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.
*
* @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.
*
* @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);
/**
* Verify a SHA256 HMAC which was created from the message using the provided hashKey.
*
* @param message The string from which the HMAC was created.
* @param messageHmac A string with the generated HMAC in HEX format. Valid messageHmac.length() is 2 to 64.
* @param hashKey The hash key to use when creating the HMAC.
* @param hashKeyLength The length of the hash key in bytes.
*
* @return True if the HMAC is correct. False otherwise.
*/
bool verifyMeshHmac(const String &message, const String &messageHmac, const uint8_t *hashKey, uint8_t hashKeyLength);
}
#endif