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

Add CryptoInterface library (#6961)

* - Add CryptoInterface library.

- Add TypeConversion core files.

* Fix compiler errors.

- Make HelloCrypto.ino stylish.

- Include assert.h in CryptoInterface.cpp.

* - Move base36 arrays to PROGMEM in TypeConversionFunctions.cpp.

- Add deprecated attribute to SHA1 and MD5 hashes.

- Remove _warningsEnabled since this has been replaced by the deprecated attribute.

- Prefix all getters with "get".

- Move all CryptoInterface functionality to the experimental namespace.

- Change formatting of core files.

- Improve comments.

* - Update keywords.txt.

* - Remove WiFi.disconnect() from setup() in HelloCrypto example since it no longer seems to be required.

* - Classify everything.

- Remove delay in setup() from HelloCrypto example since it does not seem to be required to prevent missing initial Serial prints.

- Mark type conversion functions as big endian.

- Update keywords.txt.

* - Remove namespace experimental.

- Create ESP.random functions in the core based on the defaultNonceGenerator code, and use these in defaultNonceGenerator.

- Rename CryptoInterface to esp8266::Crypto and move all functionality to the core.

- Remove need to #include <bearssl/bearssl.h> in the Crypto header file by changing br_hkdf_context to ::br_hkdf_context.

- Restyle code files for core usage.

* - Re-add namespace experimental.

- Improve comments.

* - Remove namespace esp8266.

- Rename namespace Crypto to namespace crypto.

Co-authored-by: Anders <andlo151@student.liu.se>
Co-authored-by: Develo <deveyes@gmail.com>
This commit is contained in:
aerlon 2020-04-29 03:25:10 +02:00 committed by GitHub
parent ec7644227e
commit 3c9a75f831
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 1757 additions and 1 deletions

552
cores/esp8266/Crypto.cpp Normal file
View File

@ -0,0 +1,552 @@
/*
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 <bearssl/bearssl.h>
#include "Crypto.h"
#include <TypeConversion.h>
#include <assert.h>
namespace TypeCast = esp8266::TypeConversion;
namespace
{
size_t _ctMinDataLength = 0;
size_t _ctMaxDataLength = 1024;
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.
*/
return ESP.random(nonceArray, nonceLength);
}
experimental::crypto::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 experimental
{
namespace crypto
{
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 *MD5::hash(const void *data, const size_t dataLength, void *resultArray)
{
return md5HashHelper(data, dataLength, resultArray);
}
String MD5::hash(const String &message)
{
uint8_t hashArray[NATURAL_LENGTH];
md5HashHelper(message.c_str(), message.length(), hashArray);
return TypeCast::uint8ArrayToHexString(hashArray, NATURAL_LENGTH);
}
void *MD5::hmac(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 MD5::hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
{
return createBearsslHmac(&br_md5_vtable, NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
}
void *MD5::hmacCT(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 MD5::hmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
{
return createBearsslHmacCT(&br_md5_vtable, NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
}
// #################### SHA-1 ####################
// resultArray must have size SHA1::NATURAL_LENGTH or greater
void *SHA1::hash(const void *data, const size_t dataLength, void *resultArray)
{
return sha1HashHelper(data, dataLength, resultArray);
}
String SHA1::hash(const String &message)
{
uint8_t hashArray[NATURAL_LENGTH];
sha1HashHelper(message.c_str(), message.length(), hashArray);
return TypeCast::uint8ArrayToHexString(hashArray, NATURAL_LENGTH);
}
void *SHA1::hmac(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 SHA1::hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
{
return createBearsslHmac(&br_sha1_vtable, NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
}
void *SHA1::hmacCT(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 SHA1::hmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
{
return createBearsslHmacCT(&br_sha1_vtable, NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
}
// #################### SHA-224 ####################
// resultArray must have size SHA224::NATURAL_LENGTH or greater
void *SHA224::hash(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 SHA224::hash(const String &message)
{
uint8_t hashArray[NATURAL_LENGTH];
hash(message.c_str(), message.length(), hashArray);
return TypeCast::uint8ArrayToHexString(hashArray, NATURAL_LENGTH);
}
void *SHA224::hmac(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 SHA224::hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
{
return createBearsslHmac(&br_sha224_vtable, NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
}
void *SHA224::hmacCT(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 SHA224::hmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
{
return createBearsslHmacCT(&br_sha224_vtable, NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
}
// #################### SHA-256 ####################
// resultArray must have size SHA256::NATURAL_LENGTH or greater
void *SHA256::hash(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 SHA256::hash(const String &message)
{
uint8_t hashArray[NATURAL_LENGTH];
hash(message.c_str(), message.length(), hashArray);
return TypeCast::uint8ArrayToHexString(hashArray, NATURAL_LENGTH);
}
void *SHA256::hmac(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 SHA256::hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
{
return createBearsslHmac(&br_sha256_vtable, NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
}
void *SHA256::hmacCT(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 SHA256::hmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
{
return createBearsslHmacCT(&br_sha256_vtable, NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
}
// #################### SHA-384 ####################
// resultArray must have size SHA384::NATURAL_LENGTH or greater
void *SHA384::hash(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 SHA384::hash(const String &message)
{
uint8_t hashArray[NATURAL_LENGTH];
hash(message.c_str(), message.length(), hashArray);
return TypeCast::uint8ArrayToHexString(hashArray, NATURAL_LENGTH);
}
void *SHA384::hmac(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 SHA384::hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
{
return createBearsslHmac(&br_sha384_vtable, NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
}
void *SHA384::hmacCT(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 SHA384::hmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
{
return createBearsslHmacCT(&br_sha384_vtable, NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
}
// #################### SHA-512 ####################
// resultArray must have size SHA512::NATURAL_LENGTH or greater
void *SHA512::hash(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 SHA512::hash(const String &message)
{
uint8_t hashArray[NATURAL_LENGTH];
hash(message.c_str(), message.length(), hashArray);
return TypeCast::uint8ArrayToHexString(hashArray, NATURAL_LENGTH);
}
void *SHA512::hmac(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 SHA512::hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
{
return createBearsslHmac(&br_sha512_vtable, NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
}
void *SHA512::hmacCT(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 SHA512::hmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
{
return createBearsslHmacCT(&br_sha512_vtable, NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
}
// #################### MD5+SHA-1 ####################
// resultArray must have size MD5SHA1::NATURAL_LENGTH or greater
void *MD5SHA1::hash(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 MD5SHA1::hash(const String &message)
{
uint8_t hashArray[NATURAL_LENGTH];
hash(message.c_str(), message.length(), hashArray);
return TypeCast::uint8ArrayToHexString(hashArray, NATURAL_LENGTH);
}
// #################### HKDF ####################
HKDF::HKDF(const void *keyMaterial, const size_t keyMaterialLength, const void *salt, const size_t saltLength)
{
init(keyMaterial, keyMaterialLength, salt, saltLength);
}
void HKDF::init(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
// Initialize an HKDF context, with a hash function, and the salt. This starts the HKDF-Extract process.
br_hkdf_init(&hkdfContext, &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(&hkdfContext, keyMaterial, keyMaterialLength);
// End the HKDF-Extract process, and start the HKDF-Expand process.
br_hkdf_flip(&hkdfContext);
}
size_t HKDF::produce(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
// 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(&hkdfContext, 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
{
HKDF hkdfInstance(key, ENCRYPTION_KEY_LENGTH, keySalt, keySaltLength);
uint8_t derivedEncryptionKey[ENCRYPTION_KEY_LENGTH] {0};
hkdfInstance.produce(derivedEncryptionKey, ENCRYPTION_KEY_LENGTH);
br_poly1305_ctmul32_run(derivedEncryptionKey, nonce, data, dataLength, aad, aadLength, tag, br_chacha20_ct_run, encrypt);
}
}
void ChaCha20Poly1305::encrypt(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 ChaCha20Poly1305::decrypt(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;
}
}
}

845
cores/esp8266/Crypto.h Normal file
View File

@ -0,0 +1,845 @@
/*
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 __ESP8266_ARDUINO_CRYPTO_H__
#define __ESP8266_ARDUINO_CRYPTO_H__
#include <Arduino.h>
namespace experimental
{
namespace crypto
{
/**
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 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 Crypto functions.
@param nonceGenerator The nonce generator to use.
*/
void setNonceGenerator(nonceGeneratorType nonceGenerator);
nonceGeneratorType getNonceGenerator();
// #################### MD5 ####################
struct MD5
{
static constexpr uint8_t NATURAL_LENGTH = 16;
/**
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 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 NATURAL_LENGTH bytes or more.
@return A pointer to resultArray.
*/
static void *hash(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 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.
*/
static String hash(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 NATURAL_LENGTH,
the first (lowest index) 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.
*/
static void *hmac(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 NATURAL_LENGTH.
@return A String with the generated HMAC in HEX format.
*/
static String hmac(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 NATURAL_LENGTH,
the first (lowest index) 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.
*/
static void *hmacCT(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 NATURAL_LENGTH.
@return A String with the generated HMAC in HEX format.
*/
static String hmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
};
// #################### SHA-1 ####################
struct SHA1
{
static constexpr uint8_t NATURAL_LENGTH = 20;
/**
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 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 NATURAL_LENGTH bytes or more.
@return A pointer to resultArray.
*/
static void *hash(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 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.
*/
static String hash(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 NATURAL_LENGTH,
the first (lowest index) 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.
*/
static void *hmac(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 NATURAL_LENGTH.
@return A String with the generated HMAC in HEX format.
*/
static String hmac(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 NATURAL_LENGTH,
the first (lowest index) 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.
*/
static void *hmacCT(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 NATURAL_LENGTH.
@return A String with the generated HMAC in HEX format.
*/
static String hmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
};
// #################### SHA-224 ####################
struct SHA224
{
static constexpr uint8_t NATURAL_LENGTH = 28;
/**
Create a SHA224 hash of the data. The result will be 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 NATURAL_LENGTH bytes or more.
@return A pointer to resultArray.
*/
static void *hash(const void *data, const size_t dataLength, void *resultArray);
/**
Create a SHA224 hash of the data. The result will be 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.
*/
static String hash(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 NATURAL_LENGTH,
the first (lowest index) 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.
*/
static void *hmac(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 NATURAL_LENGTH.
@return A String with the generated HMAC in HEX format.
*/
static String hmac(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 NATURAL_LENGTH,
the first (lowest index) 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.
*/
static void *hmacCT(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 NATURAL_LENGTH.
@return A String with the generated HMAC in HEX format.
*/
static String hmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
};
// #################### SHA-256 ####################
struct SHA256
{
static constexpr uint8_t NATURAL_LENGTH = 32;
/**
Create a SHA256 hash of the data. The result will be 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 NATURAL_LENGTH bytes or more.
@return A pointer to resultArray.
*/
static void *hash(const void *data, const size_t dataLength, void *resultArray);
/**
Create a SHA256 hash of the data. The result will be 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.
*/
static String hash(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 NATURAL_LENGTH,
the first (lowest index) 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.
*/
static void *hmac(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 NATURAL_LENGTH.
@return A String with the generated HMAC in HEX format.
*/
static String hmac(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 NATURAL_LENGTH,
the first (lowest index) 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.
*/
static void *hmacCT(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 NATURAL_LENGTH.
@return A String with the generated HMAC in HEX format.
*/
static String hmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
};
// #################### SHA-384 ####################
struct SHA384
{
static constexpr uint8_t NATURAL_LENGTH = 48;
/**
Create a SHA384 hash of the data. The result will be 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 NATURAL_LENGTH bytes or more.
@return A pointer to resultArray.
*/
static void *hash(const void *data, const size_t dataLength, void *resultArray);
/**
Create a SHA384 hash of the data. The result will be 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.
*/
static String hash(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 NATURAL_LENGTH,
the first (lowest index) 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.
*/
static void *hmac(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 NATURAL_LENGTH.
@return A String with the generated HMAC in HEX format.
*/
static String hmac(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 NATURAL_LENGTH,
the first (lowest index) 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.
*/
static void *hmacCT(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 NATURAL_LENGTH.
@return A String with the generated HMAC in HEX format.
*/
static String hmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
};
// #################### SHA-512 ####################
struct SHA512
{
static constexpr uint8_t NATURAL_LENGTH = 64;
/**
Create a SHA512 hash of the data. The result will be 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 NATURAL_LENGTH bytes or more.
@return A pointer to resultArray.
*/
static void *hash(const void *data, const size_t dataLength, void *resultArray);
/**
Create a SHA512 hash of the data. The result will be 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.
*/
static String hash(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 NATURAL_LENGTH,
the first (lowest index) 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.
*/
static void *hmac(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 NATURAL_LENGTH.
@return A String with the generated HMAC in HEX format.
*/
static String hmac(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 NATURAL_LENGTH,
the first (lowest index) 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.
*/
static void *hmacCT(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 NATURAL_LENGTH.
@return A String with the generated HMAC in HEX format.
*/
static String hmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
};
// #################### MD5+SHA-1 ####################
struct MD5SHA1
{
static constexpr uint8_t NATURAL_LENGTH = 36;
/**
Create a MD5+SHA-1 hash of the data. The result will be 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 NATURAL_LENGTH bytes or more.
@return A pointer to resultArray.
*/
static void *hash(const void *data, const size_t dataLength, void *resultArray);
/**
Create a MD5+SHA-1 hash of the data. The result will be 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.
*/
static String hash(const String &message);
};
// #################### HKDF ####################
struct 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.
The constructor initializes the HKDF implementation with the input data to use for HKDF processing. (calls HKDF::init())
@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.
*/
HKDF(const void *keyMaterial, const size_t keyMaterialLength, const void *salt = nullptr, const size_t saltLength = 0);
/**
This method initializes the HKDF implementation with the input data to use for HKDF processing.
Uses the BearSSL cryptographic library.
@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 init(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 method 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 HKDF::init()/constructor call.
Uses the BearSSL cryptographic library.
@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 HKDF::init().
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 produce(void *resultArray, const size_t outputLength, const void *info = nullptr, size_t infoLength = 0);
private:
// Use an opaque type to avoid #include <bearssl/bearssl.h> which drags the lib declarations into userland. The global scope prefix is required for compilation to succeed, it seems.
::br_hkdf_context hkdfContext;
};
// #################### 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 ####################
struct ChaCha20Poly1305
{
/**
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 ChaCha20Poly1305::encrypt should be passed as input values to ChaCha20Poly1305::decrypt.
Note that a 12 byte nonce is generated via getNonceGenerator() every time ChaCha20Poly1305::encrypt 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.
*/
static void encrypt(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 ChaCha20Poly1305::encrypt should be passed as input values to ChaCha20Poly1305::decrypt.
@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.
*/
static bool decrypt(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

View File

@ -522,6 +522,63 @@ bool EspClass::eraseConfig(void) {
return true; return true;
} }
uint8_t *EspClass::random(uint8_t *resultArray, const size_t outputSizeBytes) const
{
/**
* 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 < outputSizeBytes; ++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();
}
resultArray[byteIndex] = randomNumber;
randomNumber >>= 8;
}
return resultArray;
}
uint32_t EspClass::random() const
{
union { uint32_t b32; uint8_t b8[4]; } result;
random(result.b8, 4);
return result.b32;
}
uint32_t EspClass::getSketchSize() { uint32_t EspClass::getSketchSize() {
static uint32_t result = 0; static uint32_t result = 0;
if (result) if (result)

View File

@ -164,6 +164,9 @@ class EspClass {
bool eraseConfig(); bool eraseConfig();
uint8_t *random(uint8_t *resultArray, const size_t outputSizeBytes) const;
uint32_t random() const;
#ifndef CORE_MOCK #ifndef CORE_MOCK
inline uint32_t getCycleCount() __attribute__((always_inline)); inline uint32_t getCycleCount() __attribute__((always_inline));
#else #else

View File

@ -0,0 +1,91 @@
/*
TypeConversion functionality
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 <assert.h>
#include "TypeConversion.h"
namespace esp8266
{
namespace TypeConversion
{
const char base36Chars[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'};
const uint8_t base36CharValues[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
};
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(base36Chars + (uint8Array[i] >> 4));
hexString += (char)pgm_read_byte(base36Chars + uint8Array[i] % 16);
}
return hexString;
}
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(base36CharValues + hexString.charAt(i * 2) - '0') << 4) + pgm_read_byte(base36CharValues + hexString.charAt(i * 2 + 1) - '0');
}
return uint8Array;
}
uint8_t *uint64ToUint8ArrayBE(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;
}
uint64_t uint8ArrayToUint64BE(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;
}
}
}

View File

@ -0,0 +1,80 @@
/*
TypeConversion functionality
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 __ESP8266_TYPECONVERSION_H__
#define __ESP8266_TYPECONVERSION_H__
#include <Arduino.h>
namespace esp8266
{
namespace TypeConversion
{
extern const char base36Chars[36];
// Subtract '0' to normalize the char before lookup.
extern const uint8_t base36CharValues[75];
/**
Convert the contents of a uint8_t array to a String in HEX format. The resulting String starts from index 0 of the array.
All array elements will be padded with zeroes to ensure they are converted to 2 String characters each.
@param uint8Array The array to make into a HEX String.
@param arrayLength The size of uint8Array, in bytes.
@return Normally a String containing the HEX representation of the uint8Array. An empty String if the memory allocation for the String failed.
*/
String uint8ArrayToHexString(const uint8_t *uint8Array, const uint32_t arrayLength);
/**
Convert the contents of a String in HEX format to a uint8_t array. Index 0 of the array will represent the start of the String.
There must be 2 String characters for each array element. Use padding with zeroes where required.
@param hexString The HEX String to convert to a uint8_t array. Must contain at least 2*arrayLength characters.
@param uint8Array The array to fill with the contents of the hexString.
@param arrayLength The number of bytes to fill in uint8Array.
@return A pointer to the uint8Array.
*/
uint8_t *hexStringToUint8Array(const String &hexString, uint8_t *uint8Array, const uint32_t arrayLength);
/**
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.
@return The resultArray.
*/
uint8_t *uint64ToUint8ArrayBE(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 (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.
*/
uint64_t uint8ArrayToUint64BE(const uint8_t *inputArray);
}
}
#endif

View File

@ -0,0 +1,97 @@
/**
This example demonstrates the usage of the ESP8266 Crypto implementation, which aims to contain easy-to-use cryptographic functions.
Crypto is currently primarily a frontend for the cryptographic library BearSSL which is used by `BearSSL::WiFiClientSecure` and `BearSSL::WiFiServerSecure` in the ESP8266 Arduino Core.
Extensive documentation can be found in the Crypto source code files and on the [BearSSL homepage](https://www.bearssl.org).
*/
#include <ESP8266WiFi.h>
#include <TypeConversion.h>
#include <Crypto.h>
namespace TypeCast = esp8266::TypeConversion;
using namespace experimental;
/**
NOTE: Although we could define the strings below as normal String variables,
here we are using PROGMEM combined with the FPSTR() macro (and also just the F() macro further down in the file).
The reason is that this approach will place the strings in flash memory which will help save RAM during program execution.
Reading strings from flash will be slower than reading them from RAM,
but this will be a negligible difference when printing them to Serial.
More on F(), FPSTR() and PROGMEM:
https://github.com/esp8266/Arduino/issues/1143
https://arduino-esp8266.readthedocs.io/en/latest/PROGMEM.html
*/
constexpr char masterKey[] PROGMEM = "w86vn@rpfA O+S"; // Use 8 random characters or more
void setup() {
// Prevents the flash memory from being worn out, see: https://github.com/esp8266/Arduino/issues/1054 .
// This will however delay node WiFi start-up by about 700 ms. The delay is 900 ms if we otherwise would have stored the WiFi network we want to connect to.
WiFi.persistent(false);
Serial.begin(115200);
Serial.println();
Serial.println();
}
void loop() {
// This serves only to demonstrate the library use. See the header file for a full list of functions.
String exampleData = F("Hello Crypto World!");
Serial.println(String(F("This is our example data: ")) + exampleData);
uint8_t resultArray[crypto::SHA256::NATURAL_LENGTH] { 0 };
uint8_t derivedKey[crypto::ENCRYPTION_KEY_LENGTH] { 0 };
static uint32_t encryptionCounter = 0;
// Generate the salt to use for HKDF
uint8_t hkdfSalt[16] { 0 };
crypto::getNonceGenerator()(hkdfSalt, sizeof hkdfSalt);
// Generate the key to use for HMAC and encryption
crypto::HKDF hkdfInstance(FPSTR(masterKey), (sizeof masterKey) - 1, hkdfSalt, sizeof hkdfSalt); // (sizeof masterKey) - 1 removes the terminating null value of the c-string
hkdfInstance.produce(derivedKey, sizeof derivedKey);
// Hash
crypto::SHA256::hash(exampleData.c_str(), exampleData.length(), resultArray);
Serial.println(String(F("\nThis is the SHA256 hash of our example data, in HEX format:\n")) + TypeCast::uint8ArrayToHexString(resultArray, sizeof resultArray));
Serial.println(String(F("This is the SHA256 hash of our example data, in HEX format, using String output:\n")) + crypto::SHA256::hash(exampleData));
// HMAC
// Note that HMAC output length is limited
crypto::SHA256::hmac(exampleData.c_str(), exampleData.length(), derivedKey, sizeof derivedKey, resultArray, sizeof resultArray);
Serial.println(String(F("\nThis is the SHA256 HMAC of our example data, in HEX format:\n")) + TypeCast::uint8ArrayToHexString(resultArray, sizeof resultArray));
Serial.println(String(F("This is the SHA256 HMAC of our example data, in HEX format, using String output:\n")) + crypto::SHA256::hmac(exampleData, derivedKey, sizeof derivedKey, crypto::SHA256::NATURAL_LENGTH));
// Authenticated Encryption with Associated Data (AEAD)
String dataToEncrypt = F("This data is not encrypted.");
uint8_t resultingNonce[12] { 0 }; // The nonce is always 12 bytes
uint8_t resultingTag[16] { 0 }; // The tag is always 16 bytes
Serial.println(String(F("\nThis is the data to encrypt: ")) + dataToEncrypt);
// Note that the key must be ENCRYPTION_KEY_LENGTH long.
crypto::ChaCha20Poly1305::encrypt(dataToEncrypt.begin(), dataToEncrypt.length(), derivedKey, &encryptionCounter, sizeof encryptionCounter, resultingNonce, resultingTag);
Serial.println(String(F("Encrypted data: ")) + dataToEncrypt);
bool decryptionSucceeded = crypto::ChaCha20Poly1305::decrypt(dataToEncrypt.begin(), dataToEncrypt.length(), derivedKey, &encryptionCounter, sizeof encryptionCounter, resultingNonce, resultingTag);
encryptionCounter++;
if (decryptionSucceeded) {
Serial.print(F("Decryption succeeded. Result: "));
} else {
Serial.print(F("Decryption failed. Result: "));
}
Serial.println(dataToEncrypt);
Serial.println(F("\n##########################################################################################################\n"));
delay(10000);
}

View File

@ -12,6 +12,18 @@
ESP KEYWORD1 ESP KEYWORD1
crypto KEYWORD1
nonceGeneratorType KEYWORD1
MD5 KEYWORD1
SHA1 KEYWORD1
SHA224 KEYWORD1
SHA256 KEYWORD1
SHA384 KEYWORD1
SHA512 KEYWORD1
MD5SHA1 KEYWORD1
HKDF KEYWORD1
ChaCha20Poly1305 KEYWORD1
####################################### #######################################
# Methods and Functions (KEYWORD2) # Methods and Functions (KEYWORD2)
####################################### #######################################
@ -60,6 +72,21 @@ getResetInfo KEYWORD2
getResetInfoPtr KEYWORD2 getResetInfoPtr KEYWORD2
eraseConfig KEYWORD2 eraseConfig KEYWORD2
getCycleCount KEYWORD2 getCycleCount KEYWORD2
random->KEYWORD2
setCtMinDataLength KEYWORD2
getCtMinDataLength KEYWORD2
setCtMaxDataLength KEYWORD2
getCtMaxDataLength KEYWORD2
setNonceGenerator KEYWORD2
getNonceGenerator KEYWORD2
hash KEYWORD2
hmac KEYWORD2
hmacCT KEYWORD2
init KEYWORD2
produce KEYWORD2
encrypt KEYWORD2
decrypt KEYWORD2
####################################### #######################################
# Constants (LITERAL1) # Constants (LITERAL1)
@ -79,6 +106,10 @@ WAKE_RF_DISABLED LITERAL1
ADC_VCC LITERAL1 ADC_VCC LITERAL1
ADC_TOUT LITERAL1 ADC_TOUT LITERAL1
NATURAL_LENGTH LITERAL1
ENCRYPTION_KEY_LENGTH LITERAL1
CT_MAX_DIFF LITERAL1
####################################### #######################################
# namespace esp8266 # namespace esp8266
####################################### #######################################

View File

@ -1,6 +1,6 @@
name=ESP8266 name=ESP8266
version=1.0 version=1.0
author=Simon Peter,Markus Sattler,Ivan Grokhotkov author=Anders Löfgren,Simon Peter,Markus Sattler,Ivan Grokhotkov
maintainer=Ivan Grokhtkov <ivan@esp8266.com> maintainer=Ivan Grokhtkov <ivan@esp8266.com>
sentence=ESP8266 sketches examples sentence=ESP8266 sketches examples
paragraph= paragraph=