Skip to content
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

Add CryptoInterface library #6961

Merged
merged 13 commits into from
Apr 29, 2020
91 changes: 91 additions & 0 deletions cores/esp8266/TypeConversion.cpp
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 *uint64ToUint8Array(const uint64_t value, uint8_t *resultArray)
aerlon marked this conversation as resolved.
Show resolved Hide resolved
{
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 uint8ArrayToUint64(const uint8_t *inputArray)
{
uint64_t result = (uint64_t)inputArray[0] << 56 | (uint64_t)inputArray[1] << 48 | (uint64_t)inputArray[2] << 40 | (uint64_t)inputArray[3] << 32
aerlon marked this conversation as resolved.
Show resolved Hide resolved
| (uint64_t)inputArray[4] << 24 | (uint64_t)inputArray[5] << 16 | (uint64_t)inputArray[6] << 8 | (uint64_t)inputArray[7];

return result;
}
}
}
80 changes: 80 additions & 0 deletions cores/esp8266/TypeConversion.h
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.

@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 *uint64ToUint8Array(const uint64_t value, uint8_t *resultArray);

/**
Takes a uint8_t array and converts the first 8 (lowest index) elements to a uint64_t. Assumes index 0 of the array contains MSB.

@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 uint8ArrayToUint64(const uint8_t *inputArray);
}
}

#endif
9 changes: 9 additions & 0 deletions libraries/CryptoInterface/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
ESP8266 CryptoInterface
=================

A library containing easy-to-use cryptographic functions. Primarily a frontend for the cryptographic library BearSSL which is used by `BearSSL::WiFiClientSecure` and `BearSSL::WiFiServerSecure` in the ESP8266 Arduino Core.

Usage
-----

There are a number of cryptographic functions in the library. See the included example for a guide on how to use some of them. Extensive documentation can be found in the library source code files and on the [BearSSL homepage](https://www.bearssl.org).
102 changes: 102 additions & 0 deletions libraries/CryptoInterface/examples/HelloCrypto/HelloCrypto.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
This example shows the functionality of the CryptoInterface library.
*/

#include <ESP8266WiFi.h>
#include <TypeConversion.h>
#include <CryptoInterface.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);
delay(50); // Wait for Serial.
aerlon marked this conversation as resolved.
Show resolved Hide resolved

//yield(); // Use this if you don't want to wait for Serial.

// The WiFi.disconnect() ensures that the WiFi is working correctly. If this is not done before receiving WiFi connections,
// those WiFi connections will take a long time to make or sometimes will not work at all.
aerlon marked this conversation as resolved.
Show resolved Hide resolved
WiFi.disconnect();

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[CryptoInterface::SHA256_NATURAL_LENGTH] { 0 };
uint8_t derivedKey[CryptoInterface::ENCRYPTION_KEY_LENGTH] { 0 };

static uint32_t encryptionCounter = 0;


// Generate the salt to use for HKDF
uint8_t hkdfSalt[16] { 0 };
CryptoInterface::getNonceGenerator()(hkdfSalt, sizeof hkdfSalt);

// Generate the key to use for HMAC and encryption
CryptoInterface::hkdfInit(FPSTR(masterKey), (sizeof masterKey) - 1, hkdfSalt, sizeof hkdfSalt); // (sizeof masterKey) - 1 removes the terminating null value of the c-string
CryptoInterface::hkdfProduce(derivedKey, sizeof derivedKey);

// Hash
CryptoInterface::sha256Hash(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")) + CryptoInterface::sha256Hash(exampleData));


// HMAC
// Note that HMAC output length is limited
CryptoInterface::sha256Hmac(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")) + CryptoInterface::sha256Hmac(exampleData, derivedKey, sizeof derivedKey, CryptoInterface::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.
CryptoInterface::chacha20Poly1305Encrypt(dataToEncrypt.begin(), dataToEncrypt.length(), derivedKey, &encryptionCounter, sizeof encryptionCounter, resultingNonce, resultingTag);
Serial.println(String(F("Encrypted data: ")) + dataToEncrypt);

bool decryptionSucceeded = CryptoInterface::chacha20Poly1305Decrypt(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);
}
63 changes: 63 additions & 0 deletions libraries/CryptoInterface/keywords.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#######################################
# Syntax Coloring Map For CryptoInterface
#######################################

#######################################
# Library (KEYWORD3)
#######################################

CryptoInterface KEYWORD3

#######################################
# Datatypes (KEYWORD1)
#######################################

nonceGeneratorType KEYWORD1

#######################################
# Methods and Functions (KEYWORD2)
#######################################

setCtMinDataLength KEYWORD2
getCtMinDataLength KEYWORD2
setCtMaxDataLength KEYWORD2
getCtMaxDataLength KEYWORD2
setNonceGenerator KEYWORD2
getNonceGenerator KEYWORD2
md5Hash KEYWORD2
md5Hmac KEYWORD2
md5HmacCT KEYWORD2
sha1Hash KEYWORD2
sha1Hmac KEYWORD2
sha1HmacCT KEYWORD2
sha224Hash KEYWORD2
sha224Hmac KEYWORD2
sha224HmacCT KEYWORD2
sha256Hash KEYWORD2
sha256Hmac KEYWORD2
sha256HmacCT KEYWORD2
sha384Hash KEYWORD2
sha384Hmac KEYWORD2
sha384HmacCT KEYWORD2
sha512Hash->KEYWORD2
sha512Hmac KEYWORD2
sha512HmacCT KEYWORD2
md5sha1Hash KEYWORD2
hkdfInit KEYWORD2
hkdfProduce KEYWORD2
chacha20Poly1305Encrypt KEYWORD2
chacha20Poly1305Decrypt KEYWORD2

#######################################
# Constants (LITERAL1)
#######################################

MD5_NATURAL_LENGTH LITERAL1
SHA1_NATURAL_LENGTH LITERAL1
SHA224_NATURAL_LENGTH LITERAL1
SHA256_NATURAL_LENGTH LITERAL1
SHA384_NATURAL_LENGTH LITERAL1
SHA512_NATURAL_LENGTH LITERAL1
MD5SHA1_NATURAL_LENGTH LITERAL1
ENCRYPTION_KEY_LENGTH LITERAL1
CT_MAX_DIFF LITERAL1
10 changes: 10 additions & 0 deletions libraries/CryptoInterface/library.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name=CryptoInterface
version=1.0
author=Anders Löfgren
maintainer=Anders Löfgren
sentence=Interface to the cryptographic library
paragraph=The library acts as a frontend to the cryptographic backend library in ESP8266 Arduino Core.
category=Other
url=
architectures=esp8266
dot_a_linkage=true
Loading