-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Add CryptoInterface library #6961
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b3362a0
2151f7a
6a448bc
b1fc47a
92dd1a2
9e5b63f
c09252e
efb11c1
884113b
77c5d8d
ef86d20
f1783e0
7f0324c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Up here can do something like: using namespace experimental::crypto; Then further down you can use directly, e.g.: ...
getNonceGenerator()(...);
...
SHA256::hash(...);
...
SHA256::hmac(...); etc. This eases use and reduces the long fully qualified names. It also allows easy remapping of namespaces, because you just change the namespace import at the top instead of having to change each fully qualified name through the code. |
||
|
||
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); | ||
} |
Uh oh!
There was an error while loading. Please reload this page.