diff --git a/include/interface/ESP32CryptoSource.h b/include/interface/ESP32CryptoSource.h new file mode 100644 index 0000000..cb1d5fb --- /dev/null +++ b/include/interface/ESP32CryptoSource.h @@ -0,0 +1,62 @@ +#pragma once + +#include "relay/interface/CryptoSource.h" +#include "ESP32NoiseSource.h" + +class ESP32CryptoSource: CryptoSource { + +public: + + ESP32CryptoSource(const char* rngInitTag); + + bool isAvailable() override { + return true; + } + + /** + * @brief Create a new random private key + * + * @param key The output buffer where the key will be stored + * @return true The key was created + * @return false The key could not be created + */ + bool createPrivateKey(PrivateKey* key) override; + + /** + * @brief Create a the public key corresponding to a private key + * + * @param privateKey The private key to use + * @param publicKey The output buffer where the public key will be stored + * @return true The key was created + * @return false The key could not be created + */ + bool createPublicKey(const PrivateKey* privateKey, PublicKey* publicKey) override; + + /** + * @brief Sign a message + * + * @param message The message payload to include in the message + * @param length The length of the payload + * @param signature The output buffer where the signature is written + * @return true The signature was created + * @return false The signature creation failed + */ + bool sign(const uint8_t *message, uint16_t length, Signature* signature, const PrivateKey* privateKey, const PublicKey* publicKey) override; + + /** + * @brief Verify a message + * + * @param signature The message signature + * @param publicKey The public key with which the message was signed + * @param message The pointer to the message data + * @param length The length of the message + * @return true The signature is valid + * @return false The signature is invalid + */ + bool + verify(const Signature* signature, const PublicKey* publicKey, const void *message, uint16_t length) override; + +private: + + ESP32NoiseSource noise{}; +}; diff --git a/include/interface/ESP32NoiseSource.h b/include/interface/ESP32NoiseSource.h new file mode 100644 index 0000000..a6ed07f --- /dev/null +++ b/include/interface/ESP32NoiseSource.h @@ -0,0 +1,64 @@ +#pragma once + +#include + +/// @brief The size of the internal buffer when generating entropy +constexpr size_t randomNumberBatchSize = 32; + +/** + * @brief A noise source for crypto operations specifically for the ESP32. + * + */ +class ESP32NoiseSource: public NoiseSource { + +public: + /** + * \brief Constructs a new random noise source. + */ + ESP32NoiseSource(); + + /** + * \brief Destroys this random noise source. + */ + ~ESP32NoiseSource() {} + + /** + * @brief Determine if the noise source is still calibrating itself. + * + * Noise sources that require calibration start doing so at system startup + * and then switch over to random data generation once calibration is complete. + * Since no random data is being generated during calibration, the output + * from `RNGClass::rand()` and `RNG.rand()` may be predictable. + * Use `RNGClass::available()` or `RNG.available()` to determine + * when sufficient entropy is available to generate good random values. + * + * It is possible that the noise source never exits calibration. This can + * happen if the input voltage is insufficient to trigger noise or if the + * noise source is not connected. Noise sources may also periodically + * recalibrate themselves. + * + * @return Returns true if calibration is in progress; false if the noise + * source is generating valid random data. + * + */ + bool calibrating() const override; + + /** + * @brief Stirs entropy from this noise source into the global random + * number pool. + * + * This function should call `output()` to add the entropy from this noise + * source to the global random number pool. + * + * The noise source should batch up the entropy data, providing between + * 16 and 48 bytes of data each time. If the noise source does not have + * sufficient entropy data at the moment, it should return without stiring + * the current data in. + */ + void stir() override; + +private: + + /// @brief Temporary buffer to hold random bytes before handing them to the crypto module + uint8_t data[randomNumberBatchSize]; +}; \ No newline at end of file diff --git a/include/interface/ESP32StorageSource.h b/include/interface/ESP32StorageSource.h new file mode 100644 index 0000000..91ba815 --- /dev/null +++ b/include/interface/ESP32StorageSource.h @@ -0,0 +1,17 @@ + +#include "relay/interface/StorageSource.h" + +class ESP32StorageSource: StorageSource { + + bool writeByteAtIndex(uint8_t byte, uint16_t index) override; + + bool canProvideStorageWithSize(uint16_t size) override; + + bool commitData(); + + uint8_t readByteAtIndex(uint16_t index) override; + + uint16_t readBytes(uint16_t startIndex, uint16_t count, uint8_t* output) override; + + uint16_t writeBytes(uint8_t* bytes, uint16_t count, uint16_t startIndex) override; +}; \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 74f509b..6466228 100644 --- a/platformio.ini +++ b/platformio.ini @@ -18,6 +18,6 @@ lib_deps = ottowinter/ESPAsyncWebServer-esphome@^3.0.0 arduino-libraries/Ethernet@^2.0.2 https://github.com/christophhagen/arduinoWebSockets#master - + rweather/Crypto@^0.4.0 monitor_speed = 115200 build_flags= -D WEBSOCKETS_NETWORK_TYPE=NETWORK_W5100 \ No newline at end of file diff --git a/src/crypto/ESP32CryptoSource.cpp b/src/crypto/ESP32CryptoSource.cpp new file mode 100644 index 0000000..6eb41ca --- /dev/null +++ b/src/crypto/ESP32CryptoSource.cpp @@ -0,0 +1,29 @@ +#include "interface/ESP32CryptoSource.h" + +#include +#include +#include + +ESP32CryptoSource::ESP32CryptoSource(const char* rngInitTag) { + RNG.begin(rngInitTag); + RNG.addNoiseSource(noise); +} + +bool ESP32CryptoSource::createPrivateKey(PrivateKey* key) { + Ed25519::generatePrivateKey(key->bytes); + return true; +} + +bool ESP32CryptoSource::createPublicKey(const PrivateKey* privateKey, PublicKey* publicKey) { + Ed25519::derivePublicKey(publicKey->bytes, privateKey->bytes); + return true; +} + +bool ESP32CryptoSource::sign(const uint8_t *message, uint16_t length, Signature* signature, const PrivateKey* privateKey, const PublicKey* publicKey) { + Ed25519::sign(signature->bytes, privateKey->bytes, publicKey->bytes, message, length); + return true; +} + +bool ESP32CryptoSource::verify(const Signature* signature, const PublicKey* publicKey, const void *message, uint16_t length) { + return Ed25519::verify(signature->bytes, publicKey->bytes, message, length); +} \ No newline at end of file diff --git a/src/crypto/ESP32NoiseSource.cpp b/src/crypto/ESP32NoiseSource.cpp new file mode 100644 index 0000000..76be273 --- /dev/null +++ b/src/crypto/ESP32NoiseSource.cpp @@ -0,0 +1,18 @@ +#include "interface/ESP32NoiseSource.h" + +#include +#include + +ESP32NoiseSource::ESP32NoiseSource() { + // Ensure that there is randomness even if Bluetooth and WiFi are disabled + bootloader_random_enable(); +} + +bool ESP32NoiseSource::calibrating() const { + return false; +} + +void ESP32NoiseSource::stir() { + esp_fill_random(data, randomNumberBatchSize); + output(data, randomNumberBatchSize, randomNumberBatchSize * 8); +} diff --git a/src/crypto/ESP32StorageSource.cpp b/src/crypto/ESP32StorageSource.cpp new file mode 100644 index 0000000..238647d --- /dev/null +++ b/src/crypto/ESP32StorageSource.cpp @@ -0,0 +1,28 @@ +#include "interface/ESP32StorageSource.h" +#include + +bool ESP32StorageSource::writeByteAtIndex(uint8_t byte, uint16_t index) { + // TODO: What does return value mean? + EEPROM.writeByte((int) index, byte); + return true; +} + +bool ESP32StorageSource::canProvideStorageWithSize(uint16_t size) { + return EEPROM.begin(size); +} + +bool ESP32StorageSource::commitData() { + return EEPROM.commit(); +} + +uint8_t ESP32StorageSource::readByteAtIndex(uint16_t index) { + return EEPROM.readByte((int) index); +} + +uint16_t ESP32StorageSource::readBytes(uint16_t startIndex, uint16_t count, uint8_t* output) { + return EEPROM.readBytes(startIndex, output, count); +} + +uint16_t ESP32StorageSource::writeBytes(uint8_t* bytes, uint16_t count, uint16_t startIndex) { + return EEPROM.writeBytes(startIndex, bytes, count); +} \ No newline at end of file