From 3d5a8605202ef60dbe7cd90361c0ebf2706d3aef Mon Sep 17 00:00:00 2001 From: ViezeVingertjes Date: Sun, 8 Feb 2026 16:29:02 +0100 Subject: [PATCH 1/2] Deduplicate crypto libraries, saving ~49KB flash Fixes ed25519_verify() stack overflow by making large locals static, removing the need for rweather's Ed25519::verify() workaround. Vendors only the AES128+SHA256 subset of rweather/Crypto instead of all 37 files. --- lib/ed25519/ge.c | 6 +- lib/rweather_crypto/AES.h | 269 ++++++++ lib/rweather_crypto/AES128.cpp | 356 +++++++++++ lib/rweather_crypto/AESCommon.cpp | 363 +++++++++++ lib/rweather_crypto/AESEsp32.cpp | 99 +++ lib/rweather_crypto/BlockCipher.cpp | 124 ++++ lib/rweather_crypto/BlockCipher.h | 46 ++ lib/rweather_crypto/Cipher.cpp | 154 +++++ lib/rweather_crypto/Cipher.h | 47 ++ lib/rweather_crypto/Crypto.cpp | 114 ++++ lib/rweather_crypto/Crypto.h | 46 ++ lib/rweather_crypto/Hash.cpp | 202 ++++++ lib/rweather_crypto/Hash.h | 61 ++ lib/rweather_crypto/SHA256.cpp | 269 ++++++++ lib/rweather_crypto/SHA256.h | 60 ++ lib/rweather_crypto/library.json | 19 + lib/rweather_crypto/utility/EndianUtil.h | 77 +++ lib/rweather_crypto/utility/ProgMemUtil.h | 62 ++ lib/rweather_crypto/utility/RotateUtil.h | 696 +++++++++++++++++++++ library.json | 1 - platformio.ini | 1 - src/Identity.cpp | 6 - src/helpers/RegionMap.cpp | 1 - variants/heltec_mesh_solar/platformio.ini | 1 - variants/mesh_pocket/platformio.ini | 1 - variants/minewsemi_me25ls01/platformio.ini | 1 - variants/nano_g2_ultra/platformio.ini | 1 - 27 files changed, 3067 insertions(+), 16 deletions(-) create mode 100644 lib/rweather_crypto/AES.h create mode 100644 lib/rweather_crypto/AES128.cpp create mode 100644 lib/rweather_crypto/AESCommon.cpp create mode 100644 lib/rweather_crypto/AESEsp32.cpp create mode 100644 lib/rweather_crypto/BlockCipher.cpp create mode 100644 lib/rweather_crypto/BlockCipher.h create mode 100644 lib/rweather_crypto/Cipher.cpp create mode 100644 lib/rweather_crypto/Cipher.h create mode 100644 lib/rweather_crypto/Crypto.cpp create mode 100644 lib/rweather_crypto/Crypto.h create mode 100644 lib/rweather_crypto/Hash.cpp create mode 100644 lib/rweather_crypto/Hash.h create mode 100644 lib/rweather_crypto/SHA256.cpp create mode 100644 lib/rweather_crypto/SHA256.h create mode 100644 lib/rweather_crypto/library.json create mode 100644 lib/rweather_crypto/utility/EndianUtil.h create mode 100644 lib/rweather_crypto/utility/ProgMemUtil.h create mode 100644 lib/rweather_crypto/utility/RotateUtil.h diff --git a/lib/ed25519/ge.c b/lib/ed25519/ge.c index 87c691bff..3c6c02acc 100644 --- a/lib/ed25519/ge.c +++ b/lib/ed25519/ge.c @@ -65,9 +65,9 @@ B is the Ed25519 base point (x,4/5) with x positive. */ void ge_double_scalarmult_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b) { - signed char aslide[256]; - signed char bslide[256]; - ge_cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */ + static signed char aslide[256]; + static signed char bslide[256]; + static ge_cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */ ge_p1p1 t; ge_p3 u; ge_p3 A2; diff --git a/lib/rweather_crypto/AES.h b/lib/rweather_crypto/AES.h new file mode 100644 index 000000000..16c2ce0f1 --- /dev/null +++ b/lib/rweather_crypto/AES.h @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2015,2018 Southern Storm Software, Pty Ltd. + * + * 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 CRYPTO_AES_h +#define CRYPTO_AES_h + +#include "BlockCipher.h" + +// Determine which AES implementation to export to applications. +#if defined(ESP32) +#define CRYPTO_AES_ESP32 1 +#else +#define CRYPTO_AES_DEFAULT 1 +#endif + +#if defined(CRYPTO_AES_DEFAULT) || defined(CRYPTO_DOC) + +class AESTiny128; +class AESTiny256; +class AESSmall128; +class AESSmall256; + +class AESCommon : public BlockCipher +{ +public: + virtual ~AESCommon(); + + size_t blockSize() const; + + void encryptBlock(uint8_t *output, const uint8_t *input); + void decryptBlock(uint8_t *output, const uint8_t *input); + + void clear(); + +protected: + AESCommon(); + + /** @cond aes_internal */ + uint8_t rounds; + uint8_t *schedule; + + static void subBytesAndShiftRows(uint8_t *output, const uint8_t *input); + static void inverseShiftRowsAndSubBytes(uint8_t *output, const uint8_t *input); + static void mixColumn(uint8_t *output, uint8_t *input); + static void inverseMixColumn(uint8_t *output, const uint8_t *input); + static void keyScheduleCore(uint8_t *output, const uint8_t *input, uint8_t iteration); + static void applySbox(uint8_t *output, const uint8_t *input); + /** @endcond */ + + friend class AESTiny128; + friend class AESTiny256; + friend class AESSmall128; + friend class AESSmall256; +}; + +class AES128 : public AESCommon +{ +public: + AES128(); + virtual ~AES128(); + + size_t keySize() const; + + bool setKey(const uint8_t *key, size_t len); + +private: + uint8_t sched[176]; +}; + +class AES192 : public AESCommon +{ +public: + AES192(); + virtual ~AES192(); + + size_t keySize() const; + + bool setKey(const uint8_t *key, size_t len); + +private: + uint8_t sched[208]; +}; + +class AES256 : public AESCommon +{ +public: + AES256(); + virtual ~AES256(); + + size_t keySize() const; + + bool setKey(const uint8_t *key, size_t len); + +private: + uint8_t sched[240]; +}; + +class AESTiny256 : public BlockCipher +{ +public: + AESTiny256(); + virtual ~AESTiny256(); + + size_t blockSize() const; + size_t keySize() const; + + bool setKey(const uint8_t *key, size_t len); + + void encryptBlock(uint8_t *output, const uint8_t *input); + void decryptBlock(uint8_t *output, const uint8_t *input); + + void clear(); + +private: + uint8_t schedule[32]; +}; + +class AESSmall256 : public AESTiny256 +{ +public: + AESSmall256(); + virtual ~AESSmall256(); + + bool setKey(const uint8_t *key, size_t len); + + void decryptBlock(uint8_t *output, const uint8_t *input); + + void clear(); + +private: + uint8_t reverse[32]; +}; + +class AESTiny128 : public BlockCipher +{ +public: + AESTiny128(); + virtual ~AESTiny128(); + + size_t blockSize() const; + size_t keySize() const; + + bool setKey(const uint8_t *key, size_t len); + + void encryptBlock(uint8_t *output, const uint8_t *input); + void decryptBlock(uint8_t *output, const uint8_t *input); + + void clear(); + +private: + uint8_t schedule[16]; +}; + +class AESSmall128 : public AESTiny128 +{ +public: + AESSmall128(); + virtual ~AESSmall128(); + + bool setKey(const uint8_t *key, size_t len); + + void decryptBlock(uint8_t *output, const uint8_t *input); + + void clear(); + +private: + uint8_t reverse[16]; +}; + +#endif // CRYPTO_AES_DEFAULT + +#if defined(CRYPTO_AES_ESP32) + +/** @cond aes_esp_rename */ + +// The esp32 SDK keeps moving where aes.h is located, so we have to +// declare the API functions ourselves and make the context opaque. +// +// About the only thing the various SDK versions agree on is that the +// first byte is the length of the key in bytes. +// +// Some versions of esp-idf have a 33 byte AES context, and others 34. +// Allocate up to 40 to make space for future expansion. +#define CRYPTO_ESP32_CONTEXT_SIZE 40 + +// Some of the esp-idf system headers define enumerations for AES128, +// AES192, and AES256 to identify the hardware-accelerated algorithms. +// These can cause conflicts with the names we use in our library. +// Define our class names to something else to work around esp-idf. +#undef AES128 +#undef AES192 +#undef AES256 +#define AES128 AES128_ESP +#define AES192 AES192_ESP +#define AES256 AES256_ESP + +/** @endcond */ + +class AESCommon : public BlockCipher +{ +public: + virtual ~AESCommon(); + + size_t blockSize() const; + size_t keySize() const; + + bool setKey(const uint8_t *key, size_t len); + + void encryptBlock(uint8_t *output, const uint8_t *input); + void decryptBlock(uint8_t *output, const uint8_t *input); + + void clear(); + +protected: + AESCommon(uint8_t keySize); + +private: + uint8_t ctx[CRYPTO_ESP32_CONTEXT_SIZE]; +}; + +class AES128 : public AESCommon +{ +public: + AES128() : AESCommon(16) {} + virtual ~AES128(); +}; + +class AES192 : public AESCommon +{ +public: + AES192() : AESCommon(24) {} + virtual ~AES192(); +}; + +class AES256 : public AESCommon +{ +public: + AES256() : AESCommon(32) {} + virtual ~AES256(); +}; + +// The ESP32 AES context is so small that it already qualifies as "tiny". +typedef AES128 AESTiny128; +typedef AES256 AESTiny256; +typedef AES128 AESSmall128; +typedef AES256 AESSmall256; + +#endif // CRYPTO_AES_ESP32 + +#endif diff --git a/lib/rweather_crypto/AES128.cpp b/lib/rweather_crypto/AES128.cpp new file mode 100644 index 000000000..533e19363 --- /dev/null +++ b/lib/rweather_crypto/AES128.cpp @@ -0,0 +1,356 @@ +/* + * Copyright (C) 2015,2018 Southern Storm Software, Pty Ltd. + * + * 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 "AES.h" +#include "Crypto.h" +#include + +#if defined(CRYPTO_AES_DEFAULT) || defined(CRYPTO_DOC) + +/** + * \class AES128 AES.h + * \brief AES block cipher with 128-bit keys. + * + * \sa AES192, AES256, AESTiny128, AESSmall128 + */ + +/** + * \brief Constructs an AES 128-bit block cipher with no initial key. + * + * This constructor must be followed by a call to setKey() before the + * block cipher can be used for encryption or decryption. + */ +AES128::AES128() +{ + rounds = 10; + schedule = sched; +} + +AES128::~AES128() +{ + clean(sched); +} + +/** + * \brief Size of a 128-bit AES key in bytes. + * \return Always returns 16. + */ +size_t AES128::keySize() const +{ + return 16; +} + +bool AES128::setKey(const uint8_t *key, size_t len) +{ + if (len != 16) + return false; + + // Copy the key itself into the first 16 bytes of the schedule. + uint8_t *schedule = sched; + memcpy(schedule, key, 16); + + // Expand the key schedule until we have 176 bytes of expanded key. + uint8_t iteration = 1; + uint8_t n = 16; + uint8_t w = 4; + while (n < 176) { + if (w == 4) { + // Every 16 bytes (4 words) we need to apply the key schedule core. + keyScheduleCore(schedule + 16, schedule + 12, iteration); + schedule[16] ^= schedule[0]; + schedule[17] ^= schedule[1]; + schedule[18] ^= schedule[2]; + schedule[19] ^= schedule[3]; + ++iteration; + w = 0; + } else { + // Otherwise just XOR the word with the one 16 bytes previous. + schedule[16] = schedule[12] ^ schedule[0]; + schedule[17] = schedule[13] ^ schedule[1]; + schedule[18] = schedule[14] ^ schedule[2]; + schedule[19] = schedule[15] ^ schedule[3]; + } + + // Advance to the next word in the schedule. + schedule += 4; + n += 4; + ++w; + } + + return true; +} + +/** + * \class AESTiny128 AES.h + * \brief AES block cipher with 128-bit keys and tiny memory usage. + * + * This class differs from the AES128 class in the following ways: + * + * \li RAM requirements are vastly reduced. The key is stored directly + * and then expanded to the full key schedule round by round. The setKey() + * method is very fast because of this. + * \li Performance of encryptBlock() is slower than for AES128 due to + * expanding the key on the fly rather than ahead of time. + * \li The decryptBlock() function is not supported, which means that CBC + * mode cannot be used but the CTR, CFB, OFB, EAX, and GCM modes can be used. + * + * This class is useful when RAM is at a premium, CBC mode is not required, + * and reduced encryption performance is not a hindrance to the application. + * + * The companion AESSmall128 class supports decryptBlock() at the cost of + * some additional memory and slower setKey() times. + * + * \sa AESSmall128, AES128 + */ + +/** @cond */ + +// Helper macros. +#define KCORE(n) \ + do { \ + AESCommon::keyScheduleCore(temp, schedule + 12, (n)); \ + schedule[0] ^= temp[0]; \ + schedule[1] ^= temp[1]; \ + schedule[2] ^= temp[2]; \ + schedule[3] ^= temp[3]; \ + } while (0) +#define KXOR(a, b) \ + do { \ + schedule[(a) * 4] ^= schedule[(b) * 4]; \ + schedule[(a) * 4 + 1] ^= schedule[(b) * 4 + 1]; \ + schedule[(a) * 4 + 2] ^= schedule[(b) * 4 + 2]; \ + schedule[(a) * 4 + 3] ^= schedule[(b) * 4 + 3]; \ + } while (0) + +/** @endcond */ + +/** + * \brief Constructs an AES 128-bit block cipher with no initial key. + * + * This constructor must be followed by a call to setKey() before the + * block cipher can be used for encryption or decryption. + */ +AESTiny128::AESTiny128() +{ +} + +AESTiny128::~AESTiny128() +{ + clean(schedule); +} + +/** + * \brief Size of an AES block in bytes. + * \return Always returns 16. + */ +size_t AESTiny128::blockSize() const +{ + return 16; +} + +/** + * \brief Size of a 128-bit AES key in bytes. + * \return Always returns 16. + */ +size_t AESTiny128::keySize() const +{ + return 16; +} + +bool AESTiny128::setKey(const uint8_t *key, size_t len) +{ + if (len == 16) { + // Make a copy of the key - it will be expanded in encryptBlock(). + memcpy(schedule, key, 16); + return true; + } + return false; +} + +void AESTiny128::encryptBlock(uint8_t *output, const uint8_t *input) +{ + uint8_t schedule[16]; + uint8_t posn; + uint8_t round; + uint8_t state1[16]; + uint8_t state2[16]; + uint8_t temp[4]; + + // Start with the key in the schedule buffer. + memcpy(schedule, this->schedule, 16); + + // Copy the input into the state and XOR with the key schedule. + for (posn = 0; posn < 16; ++posn) + state1[posn] = input[posn] ^ schedule[posn]; + + // Perform the first 9 rounds of the cipher. + for (round = 1; round <= 9; ++round) { + // Expand the next 16 bytes of the key schedule. + KCORE(round); + KXOR(1, 0); + KXOR(2, 1); + KXOR(3, 2); + + // Encrypt using the key schedule. + AESCommon::subBytesAndShiftRows(state2, state1); + AESCommon::mixColumn(state1, state2); + AESCommon::mixColumn(state1 + 4, state2 + 4); + AESCommon::mixColumn(state1 + 8, state2 + 8); + AESCommon::mixColumn(state1 + 12, state2 + 12); + for (posn = 0; posn < 16; ++posn) + state1[posn] ^= schedule[posn]; + } + + // Expand the final 16 bytes of the key schedule. + KCORE(10); + KXOR(1, 0); + KXOR(2, 1); + KXOR(3, 2); + + // Perform the final round. + AESCommon::subBytesAndShiftRows(state2, state1); + for (posn = 0; posn < 16; ++posn) + output[posn] = state2[posn] ^ schedule[posn]; +} + +void AESTiny128::decryptBlock(uint8_t *output, const uint8_t *input) +{ + // Decryption is not supported by AESTiny128. +} + +void AESTiny128::clear() +{ + clean(schedule); +} + +/** + * \class AESSmall128 AES.h + * \brief AES block cipher with 128-bit keys and reduced memory usage. + * + * This class differs from the AES128 class in that the RAM requirements are + * vastly reduced. The key schedule is expanded round by round instead of + * being generated and stored by setKey(). The performance of encryption + * and decryption is slightly less because of this. + * + * This class is useful when RAM is at a premium and reduced encryption + * performance is not a hindrance to the application. + * + * The companion AESTiny128 class uses even less RAM but only supports the + * encryptBlock() operation. Block cipher modes like CTR, EAX, and GCM + * do not need the decryptBlock() operation, so AESTiny128 may be a better + * option than AESSmall128 for many applications. + * + * \sa AESTiny128, AES128 + */ + +/** + * \brief Constructs an AES 128-bit block cipher with no initial key. + * + * This constructor must be followed by a call to setKey() before the + * block cipher can be used for encryption or decryption. + */ +AESSmall128::AESSmall128() +{ +} + +AESSmall128::~AESSmall128() +{ + clean(reverse); +} + +bool AESSmall128::setKey(const uint8_t *key, size_t len) +{ + uint8_t *schedule; + uint8_t round; + uint8_t temp[4]; + + // Set the encryption key first. + if (!AESTiny128::setKey(key, len)) + return false; + + // Expand the key schedule up to the last round which gives + // us the round keys to use for the final two rounds. We can + // then work backwards from there in decryptBlock(). + schedule = reverse; + memcpy(schedule, key, 16); + for (round = 1; round <= 10; ++round) { + KCORE(round); + KXOR(1, 0); + KXOR(2, 1); + KXOR(3, 2); + } + + // Key is ready to go. + return true; +} + +void AESSmall128::decryptBlock(uint8_t *output, const uint8_t *input) +{ + uint8_t schedule[16]; + uint8_t round; + uint8_t posn; + uint8_t state1[16]; + uint8_t state2[16]; + uint8_t temp[4]; + + // Start with the end of the decryption schedule. + memcpy(schedule, reverse, 16); + + // Copy the input into the state and reverse the final round. + for (posn = 0; posn < 16; ++posn) + state1[posn] = input[posn] ^ schedule[posn]; + AESCommon::inverseShiftRowsAndSubBytes(state2, state1); + KXOR(3, 2); + KXOR(2, 1); + KXOR(1, 0); + KCORE(10); + + // Perform the next 9 rounds of the decryption process. + for (round = 9; round >= 1; --round) { + // Decrypt using the key schedule. + for (posn = 0; posn < 16; ++posn) + state2[posn] ^= schedule[posn]; + AESCommon::inverseMixColumn(state1, state2); + AESCommon::inverseMixColumn(state1 + 4, state2 + 4); + AESCommon::inverseMixColumn(state1 + 8, state2 + 8); + AESCommon::inverseMixColumn(state1 + 12, state2 + 12); + AESCommon::inverseShiftRowsAndSubBytes(state2, state1); + + // Expand the next 16 bytes of the key schedule in reverse. + KXOR(3, 2); + KXOR(2, 1); + KXOR(1, 0); + KCORE(round); + } + + // Reverse the initial round and create the output words. + for (posn = 0; posn < 16; ++posn) + output[posn] = state2[posn] ^ schedule[posn]; +} + +void AESSmall128::clear() +{ + clean(reverse); + AESTiny128::clear(); +} + +#endif // CRYPTO_AES_DEFAULT diff --git a/lib/rweather_crypto/AESCommon.cpp b/lib/rweather_crypto/AESCommon.cpp new file mode 100644 index 000000000..aee6d32f9 --- /dev/null +++ b/lib/rweather_crypto/AESCommon.cpp @@ -0,0 +1,363 @@ +/* + * Copyright (C) 2015 Southern Storm Software, Pty Ltd. + * + * 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 "AES.h" +#include "Crypto.h" +#include "utility/ProgMemUtil.h" + +#if defined(CRYPTO_AES_DEFAULT) || defined(CRYPTO_DOC) + +/** + * \class AESCommon AES.h + * \brief Abstract base class for AES block ciphers. + * + * This class is abstract. The caller should instantiate AES128, + * AES192, or AES256 to create an AES block cipher with a specific + * key size. + * + * \note This AES implementation does not have constant cache behaviour due + * to the use of table lookups. It may not be safe to use this implementation + * in an environment where the attacker can observe the timing of encryption + * and decryption operations. Unless AES compatibility is required, + * it is recommended that the ChaCha stream cipher be used instead. + * + * Reference: http://en.wikipedia.org/wiki/Advanced_Encryption_Standard + * + * \sa ChaCha, AES128, AES192, AES256 + */ + +/** @cond sbox */ + +// AES S-box (http://en.wikipedia.org/wiki/Rijndael_S-box) +static uint8_t const sbox[256] PROGMEM = { + 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, // 0x00 + 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, + 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, // 0x10 + 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, + 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, // 0x20 + 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, + 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, // 0x30 + 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, + 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, // 0x40 + 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, + 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, // 0x50 + 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, + 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, // 0x60 + 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, + 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, // 0x70 + 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, + 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, // 0x80 + 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, + 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, // 0x90 + 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, + 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, // 0xA0 + 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, + 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, // 0xB0 + 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, + 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, // 0xC0 + 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, + 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, // 0xD0 + 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, + 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, // 0xE0 + 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, + 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, // 0xF0 + 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 +}; + +// AES inverse S-box (http://en.wikipedia.org/wiki/Rijndael_S-box) +static uint8_t const sbox_inverse[256] PROGMEM = { + 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, // 0x00 + 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, + 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, // 0x10 + 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, + 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, // 0x20 + 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, + 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, // 0x30 + 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, + 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, // 0x40 + 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, + 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, // 0x50 + 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, + 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, // 0x60 + 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, + 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, // 0x70 + 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, + 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, // 0x80 + 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, + 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, // 0x90 + 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, + 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, // 0xA0 + 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, + 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, // 0xB0 + 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, + 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, // 0xC0 + 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, + 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, // 0xD0 + 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, + 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, // 0xE0 + 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, // 0xF0 + 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D +}; + +/** @endcond */ + +/** + * \brief Constructs an AES block cipher object. + */ +AESCommon::AESCommon() + : rounds(0), schedule(0) +{ +} + +/** + * \brief Destroys this AES block cipher object after clearing + * sensitive information. + */ +AESCommon::~AESCommon() +{ +} + +/** + * \brief Size of an AES block in bytes. + * \return Always returns 16. + */ +size_t AESCommon::blockSize() const +{ + return 16; +} + +// Constants to correct Galois multiplication for the high bits +// that are shifted out when multiplying by powers of two. +static uint8_t const K[8] = { + 0x00, + 0x1B, + (0x1B << 1), + (0x1B << 1) ^ 0x1B, + (0x1B << 2), + (0x1B << 2) ^ 0x1B, + (0x1B << 2) ^ (0x1B << 1), + (0x1B << 2) ^ (0x1B << 1) ^ 0x1B +}; + +// Multiply x by 2 in the Galois field, to achieve the effect of the following: +// +// if (x & 0x80) +// return (x << 1) ^ 0x1B; +// else +// return (x << 1); +// +// However, we don't want to use runtime conditionals if we can help it +// to avoid leaking timing information from the implementation. +// In this case, multiplication is slightly faster than table lookup on AVR. +#define gmul2(x) (t = ((uint16_t)(x)) << 1, \ + ((uint8_t)t) ^ (uint8_t)(0x1B * ((uint8_t)(t >> 8)))) + +// Multiply x by 4 in the Galois field. +#define gmul4(x) (t = ((uint16_t)(x)) << 2, ((uint8_t)t) ^ K[t >> 8]) + +// Multiply x by 8 in the Galois field. +#define gmul8(x) (t = ((uint16_t)(x)) << 3, ((uint8_t)t) ^ K[t >> 8]) + +#define OUT(col, row) output[(col) * 4 + (row)] +#define IN(col, row) input[(col) * 4 + (row)] + +/** @cond aes_funcs */ + +void AESCommon::subBytesAndShiftRows(uint8_t *output, const uint8_t *input) +{ + OUT(0, 0) = pgm_read_byte(sbox + IN(0, 0)); + OUT(0, 1) = pgm_read_byte(sbox + IN(1, 1)); + OUT(0, 2) = pgm_read_byte(sbox + IN(2, 2)); + OUT(0, 3) = pgm_read_byte(sbox + IN(3, 3)); + OUT(1, 0) = pgm_read_byte(sbox + IN(1, 0)); + OUT(1, 1) = pgm_read_byte(sbox + IN(2, 1)); + OUT(1, 2) = pgm_read_byte(sbox + IN(3, 2)); + OUT(1, 3) = pgm_read_byte(sbox + IN(0, 3)); + OUT(2, 0) = pgm_read_byte(sbox + IN(2, 0)); + OUT(2, 1) = pgm_read_byte(sbox + IN(3, 1)); + OUT(2, 2) = pgm_read_byte(sbox + IN(0, 2)); + OUT(2, 3) = pgm_read_byte(sbox + IN(1, 3)); + OUT(3, 0) = pgm_read_byte(sbox + IN(3, 0)); + OUT(3, 1) = pgm_read_byte(sbox + IN(0, 1)); + OUT(3, 2) = pgm_read_byte(sbox + IN(1, 2)); + OUT(3, 3) = pgm_read_byte(sbox + IN(2, 3)); +} + +void AESCommon::inverseShiftRowsAndSubBytes(uint8_t *output, const uint8_t *input) +{ + OUT(0, 0) = pgm_read_byte(sbox_inverse + IN(0, 0)); + OUT(0, 1) = pgm_read_byte(sbox_inverse + IN(3, 1)); + OUT(0, 2) = pgm_read_byte(sbox_inverse + IN(2, 2)); + OUT(0, 3) = pgm_read_byte(sbox_inverse + IN(1, 3)); + OUT(1, 0) = pgm_read_byte(sbox_inverse + IN(1, 0)); + OUT(1, 1) = pgm_read_byte(sbox_inverse + IN(0, 1)); + OUT(1, 2) = pgm_read_byte(sbox_inverse + IN(3, 2)); + OUT(1, 3) = pgm_read_byte(sbox_inverse + IN(2, 3)); + OUT(2, 0) = pgm_read_byte(sbox_inverse + IN(2, 0)); + OUT(2, 1) = pgm_read_byte(sbox_inverse + IN(1, 1)); + OUT(2, 2) = pgm_read_byte(sbox_inverse + IN(0, 2)); + OUT(2, 3) = pgm_read_byte(sbox_inverse + IN(3, 3)); + OUT(3, 0) = pgm_read_byte(sbox_inverse + IN(3, 0)); + OUT(3, 1) = pgm_read_byte(sbox_inverse + IN(2, 1)); + OUT(3, 2) = pgm_read_byte(sbox_inverse + IN(1, 2)); + OUT(3, 3) = pgm_read_byte(sbox_inverse + IN(0, 3)); +} + +void AESCommon::mixColumn(uint8_t *output, uint8_t *input) +{ + uint16_t t; // Needed by the gmul2 macro. + uint8_t a = input[0]; + uint8_t b = input[1]; + uint8_t c = input[2]; + uint8_t d = input[3]; + uint8_t a2 = gmul2(a); + uint8_t b2 = gmul2(b); + uint8_t c2 = gmul2(c); + uint8_t d2 = gmul2(d); + output[0] = a2 ^ b2 ^ b ^ c ^ d; + output[1] = a ^ b2 ^ c2 ^ c ^ d; + output[2] = a ^ b ^ c2 ^ d2 ^ d; + output[3] = a2 ^ a ^ b ^ c ^ d2; +} + +void AESCommon::inverseMixColumn(uint8_t *output, const uint8_t *input) +{ + uint16_t t; // Needed by the gmul2, gmul4, and gmul8 macros. + uint8_t a = input[0]; + uint8_t b = input[1]; + uint8_t c = input[2]; + uint8_t d = input[3]; + uint8_t a2 = gmul2(a); + uint8_t b2 = gmul2(b); + uint8_t c2 = gmul2(c); + uint8_t d2 = gmul2(d); + uint8_t a4 = gmul4(a); + uint8_t b4 = gmul4(b); + uint8_t c4 = gmul4(c); + uint8_t d4 = gmul4(d); + uint8_t a8 = gmul8(a); + uint8_t b8 = gmul8(b); + uint8_t c8 = gmul8(c); + uint8_t d8 = gmul8(d); + output[0] = a8 ^ a4 ^ a2 ^ b8 ^ b2 ^ b ^ c8 ^ c4 ^ c ^ d8 ^ d; + output[1] = a8 ^ a ^ b8 ^ b4 ^ b2 ^ c8 ^ c2 ^ c ^ d8 ^ d4 ^ d; + output[2] = a8 ^ a4 ^ a ^ b8 ^ b ^ c8 ^ c4 ^ c2 ^ d8 ^ d2 ^ d; + output[3] = a8 ^ a2 ^ a ^ b8 ^ b4 ^ b ^ c8 ^ c ^ d8 ^ d4 ^ d2; +} + +/** @endcond */ + +void AESCommon::encryptBlock(uint8_t *output, const uint8_t *input) +{ + const uint8_t *roundKey = schedule; + uint8_t posn; + uint8_t round; + uint8_t state1[16]; + uint8_t state2[16]; + + // Copy the input into the state and XOR with the first round key. + for (posn = 0; posn < 16; ++posn) + state1[posn] = input[posn] ^ roundKey[posn]; + roundKey += 16; + + // Perform all rounds except the last. + for (round = rounds; round > 1; --round) { + subBytesAndShiftRows(state2, state1); + mixColumn(state1, state2); + mixColumn(state1 + 4, state2 + 4); + mixColumn(state1 + 8, state2 + 8); + mixColumn(state1 + 12, state2 + 12); + for (posn = 0; posn < 16; ++posn) + state1[posn] ^= roundKey[posn]; + roundKey += 16; + } + + // Perform the final round. + subBytesAndShiftRows(state2, state1); + for (posn = 0; posn < 16; ++posn) + output[posn] = state2[posn] ^ roundKey[posn]; +} + +void AESCommon::decryptBlock(uint8_t *output, const uint8_t *input) +{ + const uint8_t *roundKey = schedule + rounds * 16; + uint8_t round; + uint8_t posn; + uint8_t state1[16]; + uint8_t state2[16]; + + // Copy the input into the state and reverse the final round. + for (posn = 0; posn < 16; ++posn) + state1[posn] = input[posn] ^ roundKey[posn]; + inverseShiftRowsAndSubBytes(state2, state1); + + // Perform all other rounds in reverse. + for (round = rounds; round > 1; --round) { + roundKey -= 16; + for (posn = 0; posn < 16; ++posn) + state2[posn] ^= roundKey[posn]; + inverseMixColumn(state1, state2); + inverseMixColumn(state1 + 4, state2 + 4); + inverseMixColumn(state1 + 8, state2 + 8); + inverseMixColumn(state1 + 12, state2 + 12); + inverseShiftRowsAndSubBytes(state2, state1); + } + + // Reverse the initial round and create the output words. + roundKey -= 16; + for (posn = 0; posn < 16; ++posn) + output[posn] = state2[posn] ^ roundKey[posn]; +} + +void AESCommon::clear() +{ + clean(schedule, (rounds + 1) * 16); +} + +/** @cond aes_keycore */ + +void AESCommon::keyScheduleCore(uint8_t *output, const uint8_t *input, uint8_t iteration) +{ + // Rcon(i), 2^i in the Rijndael finite field, for i = 0..10. + // http://en.wikipedia.org/wiki/Rijndael_key_schedule + static uint8_t const rcon[11] PROGMEM = { + 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, // 0x00 + 0x80, 0x1B, 0x36 + }; + output[0] = pgm_read_byte(sbox + input[1]) ^ pgm_read_byte(rcon + iteration); + output[1] = pgm_read_byte(sbox + input[2]); + output[2] = pgm_read_byte(sbox + input[3]); + output[3] = pgm_read_byte(sbox + input[0]); +} + +void AESCommon::applySbox(uint8_t *output, const uint8_t *input) +{ + output[0] = pgm_read_byte(sbox + input[0]); + output[1] = pgm_read_byte(sbox + input[1]); + output[2] = pgm_read_byte(sbox + input[2]); + output[3] = pgm_read_byte(sbox + input[3]); +} + +/** @endcond */ + +#endif // CRYPTO_AES_DEFAULT diff --git a/lib/rweather_crypto/AESEsp32.cpp b/lib/rweather_crypto/AESEsp32.cpp new file mode 100644 index 000000000..d37761f51 --- /dev/null +++ b/lib/rweather_crypto/AESEsp32.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2018 Southern Storm Software, Pty Ltd. + * + * 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 "AES.h" +#include "Crypto.h" +#include + +// AES implementation for ESP32 using the hardware crypto module. + +#if defined(CRYPTO_AES_ESP32) + +// Declare the functions in the esp-idf SDK that we need. +extern "C" { +int esp_aes_setkey(unsigned char *ctx, const unsigned char *key, + unsigned int keybits); +int esp_aes_crypt_ecb(unsigned char *ctx, int mode, + const unsigned char *input, + unsigned char *output); +}; + +AESCommon::AESCommon(uint8_t keySize) +{ + memset(ctx, 0, sizeof(ctx)); + ctx[0] = keySize; +} + +AESCommon::~AESCommon() +{ + clean(ctx, sizeof(ctx)); +} + +size_t AESCommon::blockSize() const +{ + return 16; +} + +size_t AESCommon::keySize() const +{ + return ctx[0]; +} + +bool AESCommon::setKey(const uint8_t *key, size_t len) +{ + if (len == ctx[0]) { + esp_aes_setkey(ctx, key, len * 8); + return true; + } + return false; +} + +void AESCommon::encryptBlock(uint8_t *output, const uint8_t *input) +{ + esp_aes_crypt_ecb(ctx, 1, input, output); +} + +void AESCommon::decryptBlock(uint8_t *output, const uint8_t *input) +{ + esp_aes_crypt_ecb(ctx, 0, input, output); +} + +void AESCommon::clear() +{ + uint8_t keySize = ctx[0]; + clean(ctx, sizeof(ctx)); + ctx[0] = keySize; +} + +AES128::~AES128() +{ +} + +AES192::~AES192() +{ +} + +AES256::~AES256() +{ +} + +#endif // CRYPTO_AES_ESP32 diff --git a/lib/rweather_crypto/BlockCipher.cpp b/lib/rweather_crypto/BlockCipher.cpp new file mode 100644 index 000000000..e98eeb665 --- /dev/null +++ b/lib/rweather_crypto/BlockCipher.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2015 Southern Storm Software, Pty Ltd. + * + * 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 "BlockCipher.h" + +/** + * \class BlockCipher BlockCipher.h + * \brief Abstract base class for block ciphers. + * + * Block ciphers always operate in electronic codebook (ECB) mode. + * Higher-level classes such as CFB128 and CTR128 wrap the block cipher to + * create more useful classes for encryption and decryption of bulk data. + * + * References: http://en.wikipedia.org/wiki/Block_cipher, + * http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29 + */ + +/** + * \brief Constructs a block cipher. + */ +BlockCipher::BlockCipher() +{ +} + +/** + * \brief Destroys this block cipher object. + * + * Subclasses are responsible for clearing temporary key schedules + * and other buffers so as to avoid leaking sensitive information. + * + * \sa clear() + */ +BlockCipher::~BlockCipher() +{ +} + +/** + * \fn size_t BlockCipher::blockSize() const + * \brief Size of a single block processed by this cipher, in bytes. + * + * \return Returns the size of a block in bytes. + * + * \sa keySize(), encryptBlock() + */ + +/** + * \fn size_t BlockCipher::keySize() const + * \brief Default size of the key for this block cipher, in bytes. + * + * This value indicates the default, or recommended, size for the key. + * + * \sa setKey(), blockSize() + */ + +/** + * \fn bool BlockCipher::setKey(const uint8_t *key, size_t len) + * \brief Sets the key to use for future encryption and decryption operations. + * + * \param key The key to use. + * \param len The length of the key. + * \return Returns false if the key length is not supported, or the key + * is somehow "weak" and unusable by this cipher. + * + * Use clear() or the destructor to remove the key and any other sensitive + * data from the object once encryption or decryption is complete. + * + * \sa keySize(), clear() + */ + +/** + * \fn void BlockCipher::encryptBlock(uint8_t *output, const uint8_t *input) + * \brief Encrypts a single block using this cipher. + * + * \param output The output buffer to put the ciphertext into. + * Must be at least blockSize() bytes in length. + * \param input The input buffer to read the plaintext from which is + * allowed to overlap with \a output. Must be at least blockSize() + * bytes in length. + * + * \sa decryptBlock(), blockSize() + */ + +/** + * \fn void BlockCipher::decryptBlock(uint8_t *output, const uint8_t *input) + * \brief Decrypts a single block using this cipher. + * + * \param output The output buffer to put the plaintext into. + * Must be at least blockSize() bytes in length. + * \param input The input buffer to read the ciphertext from which is + * allowed to overlap with \a output. Must be at least blockSize() + * bytes in length. + * + * \sa encryptBlock(), blockSize() + */ + +/** + * \fn void BlockCipher::clear() + * \brief Clears all security-sensitive state from this block cipher. + * + * Security-sensitive information includes key schedules and any + * temporary state that is used by encryptBlock() or decryptBlock() + * which is stored in the object itself. + * + * \sa setKey(), encryptBlock(), decryptBlock() + */ diff --git a/lib/rweather_crypto/BlockCipher.h b/lib/rweather_crypto/BlockCipher.h new file mode 100644 index 000000000..1d374d020 --- /dev/null +++ b/lib/rweather_crypto/BlockCipher.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015 Southern Storm Software, Pty Ltd. + * + * 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 CRYPTO_BLOCKCIPHER_h +#define CRYPTO_BLOCKCIPHER_h + +#include +#include + +class BlockCipher +{ +public: + BlockCipher(); + virtual ~BlockCipher(); + + virtual size_t blockSize() const = 0; + virtual size_t keySize() const = 0; + + virtual bool setKey(const uint8_t *key, size_t len) = 0; + + virtual void encryptBlock(uint8_t *output, const uint8_t *input) = 0; + virtual void decryptBlock(uint8_t *output, const uint8_t *input) = 0; + + virtual void clear() = 0; +}; + +#endif diff --git a/lib/rweather_crypto/Cipher.cpp b/lib/rweather_crypto/Cipher.cpp new file mode 100644 index 000000000..f91a14a26 --- /dev/null +++ b/lib/rweather_crypto/Cipher.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2015 Southern Storm Software, Pty Ltd. + * + * 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 "Cipher.h" + +/** + * \class Cipher Cipher.h + * \brief Abstract base class for stream ciphers. + * + * This class is intended for implementing ciphers that operate on arbitrary + * amounts of data. In particular, stream ciphers where the number of + * bytes that are input to encrypt() or decrypt() is exactly the same as + * the number of bytes that are output. + * + * All of the stream ciphers such as ChaCha inherit directly from this class, + * together with block cipher modes such as CTR and CFB. + */ + +/** + * \brief Constructs a new cipher object. + */ +Cipher::Cipher() +{ +} + +/** + * \brief Destroys this cipher object. + * + * Subclasses are responsible for clearing temporary key schedules + * and other buffers so as to avoid leaking sensitive information. + * + * \sa clear() + */ +Cipher::~Cipher() +{ +} + +/** + * \fn size_t Cipher::keySize() const + * \brief Default size of the key for this cipher, in bytes. + * + * If the cipher supports variable-sized keys, keySize() indicates the + * default or recommended key size. The cipher may support other key sizes. + * + * \sa setKey(), ivSize() + */ + +/** + * \fn size_t Cipher::ivSize() const + * \brief Size of the initialization vector for this cipher, in bytes. + * + * If the cipher does not need an initialization vector, this function + * will return zero. + */ + +/** + * \fn bool Cipher::setKey(const uint8_t *key, size_t len) + * \brief Sets the key to use for future encryption and decryption operations. + * + * \param key The key to use. + * \param len The length of the key in bytes. + * \return Returns false if the key length is not supported, or the key + * is somehow "weak" and unusable by this cipher. + * + * Use clear() or the destructor to remove the key and any other sensitive + * data from the object once encryption or decryption is complete. + * + * Calling setKey() resets the cipher. Any temporary data that was being + * retained for encrypting partial blocks will be abandoned. + * + * \sa keySize(), clear() + */ + +/** + * \fn bool Cipher::setIV(const uint8_t *iv, size_t len) + * \brief Sets the initialization vector to use for future encryption and + * decryption operations. + * + * \param iv The initialization vector to use. + * \param len The length of the initialization vector in bytes. + * \return Returns false if the length is not supported. + * + * Initialization vectors should be set before the first call to + * encrypt() or decrypt() after a setKey() call. If the initialization + * vector is changed after encryption or decryption begins, + * then the behaviour is undefined. + * + * \note The IV is not encoded into the output stream by encrypt(). + * The caller is responsible for communicating the IV to the other party. + * + * \sa ivSize() + */ + +/** + * \fn void Cipher::encrypt(uint8_t *output, const uint8_t *input, size_t len) + * \brief Encrypts an input buffer and writes the ciphertext to an + * output buffer. + * + * \param output The output buffer to write to, which may be the same + * buffer as \a input. The \a output buffer must have at least as many + * bytes as the \a input buffer. + * \param input The input buffer to read from. + * \param len The number of bytes to encrypt. + * + * The encrypt() function can be called multiple times with different + * regions of the plaintext data. + * + * \sa decrypt() + */ + +/** + * \fn void Cipher::decrypt(uint8_t *output, const uint8_t *input, size_t len) + * \brief Decrypts an input buffer and writes the plaintext to an + * output buffer. + * + * \param output The output buffer to write to, which may be the same + * buffer as \a input. The \a output buffer must have at least as many + * bytes as the \a input buffer. + * \param input The input buffer to read from. + * \param len The number of bytes to decrypt. + * + * The decrypt() function can be called multiple times with different + * regions of the ciphertext data. + * + * \sa encrypt() + */ + +/** + * \fn void Cipher::clear() + * \brief Clears all security-sensitive state from this cipher. + * + * Security-sensitive information includes key schedules, initialization + * vectors, and any temporary state that is used by encrypt() or decrypt() + * which is stored in the cipher itself. + */ diff --git a/lib/rweather_crypto/Cipher.h b/lib/rweather_crypto/Cipher.h new file mode 100644 index 000000000..8d498d8fa --- /dev/null +++ b/lib/rweather_crypto/Cipher.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2015 Southern Storm Software, Pty Ltd. + * + * 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 CRYPTO_CIPHER_h +#define CRYPTO_CIPHER_h + +#include +#include + +class Cipher +{ +public: + Cipher(); + virtual ~Cipher(); + + virtual size_t keySize() const = 0; + virtual size_t ivSize() const = 0; + + virtual bool setKey(const uint8_t *key, size_t len) = 0; + virtual bool setIV(const uint8_t *iv, size_t len) = 0; + + virtual void encrypt(uint8_t *output, const uint8_t *input, size_t len) = 0; + virtual void decrypt(uint8_t *output, const uint8_t *input, size_t len) = 0; + + virtual void clear() = 0; +}; + +#endif diff --git a/lib/rweather_crypto/Crypto.cpp b/lib/rweather_crypto/Crypto.cpp new file mode 100644 index 000000000..6c537bac8 --- /dev/null +++ b/lib/rweather_crypto/Crypto.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2015 Southern Storm Software, Pty Ltd. + * + * 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 "Crypto.h" + +/** + * \brief Cleans a block of bytes. + * + * \param dest The destination block to be cleaned. + * \param size The size of the destination to be cleaned in bytes. + * + * Unlike memset(), this function attempts to prevent the compiler + * from optimizing away the clear on a memory buffer. + */ +void clean(void *dest, size_t size) +{ + // Force the use of volatile so that we actually clear the memory. + // Otherwise the compiler might optimise the entire contents of this + // function away, which will not be secure. + volatile uint8_t *d = (volatile uint8_t *)dest; + while (size > 0) { + *d++ = 0; + --size; + } +} + +/** + * \fn void clean(T &var) + * \brief Template function that cleans a variable. + * + * \param var A reference to the variable to clean. + * + * The variable will be cleared to all-zeroes in a secure manner. + * Unlike memset(), this function attempts to prevent the compiler + * from optimizing away the variable clear. + */ + +/** + * \brief Compares two memory blocks for equality. + * + * \param data1 Points to the first memory block. + * \param data2 Points to the second memory block. + * \param len The size of the memory blocks in bytes. + * + * Unlike memcmp(), this function attempts to compare the two memory blocks + * in a way that will not reveal the contents in the instruction timing. + * In particular, this function will not stop early if a byte is different. + * It will instead continue onto the end of the array. + */ +bool secure_compare(const void *data1, const void *data2, size_t len) +{ + uint8_t result = 0; + const uint8_t *d1 = (const uint8_t *)data1; + const uint8_t *d2 = (const uint8_t *)data2; + while (len > 0) { + result |= (*d1++ ^ *d2++); + --len; + } + return (bool)((((uint16_t)0x0100) - result) >> 8); +} + +/** + * \brief Calculates the CRC-8 value over an array in memory. + * + * \param tag Starting tag to distinguish this calculation. + * \param data The data to checksum. + * \param size The number of bytes to checksum. + * \return The CRC-8 value over the data. + * + * This function does not provide any real security. It is a simple + * check that seed values have been initialized within EEPROM or Flash. + * If the CRC-8 check fails, then it is assumed that the EEPROM/Flash + * contents are invalid and should be re-initialized. + * + * Reference: http://www.sunshine2k.de/articles/coding/crc/understanding_crc.html#ch4 + */ +uint8_t crypto_crc8(uint8_t tag, const void *data, unsigned size) +{ + const uint8_t *d = (const uint8_t *)data; + uint8_t crc = 0xFF ^ tag; + uint8_t bit; + while (size > 0) { + crc ^= *d++; + for (bit = 0; bit < 8; ++bit) { + // if (crc & 0x80) + // crc = (crc << 1) ^ 0x1D; + // else + // crc = (crc << 1); + uint8_t generator = (uint8_t)((((int8_t)crc) >> 7) & 0x1D); + crc = (crc << 1) ^ generator; + } + --size; + } + return crc; +} diff --git a/lib/rweather_crypto/Crypto.h b/lib/rweather_crypto/Crypto.h new file mode 100644 index 000000000..b780f257e --- /dev/null +++ b/lib/rweather_crypto/Crypto.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015 Southern Storm Software, Pty Ltd. + * + * 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 CRYPTO_h +#define CRYPTO_h + +#include +#include + +void clean(void *dest, size_t size); + +template +inline void clean(T &var) +{ + clean(&var, sizeof(T)); +} + +bool secure_compare(const void *data1, const void *data2, size_t len); + +#if defined(ESP8266) +extern "C" void system_soft_wdt_feed(void); +#define crypto_feed_watchdog() system_soft_wdt_feed() +#else +#define crypto_feed_watchdog() do { ; } while (0) +#endif + +#endif diff --git a/lib/rweather_crypto/Hash.cpp b/lib/rweather_crypto/Hash.cpp new file mode 100644 index 000000000..5f03f8c81 --- /dev/null +++ b/lib/rweather_crypto/Hash.cpp @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2015 Southern Storm Software, Pty Ltd. + * + * 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 "Hash.h" +#include + +/** + * \class Hash Hash.h + * \brief Abstract base class for cryptographic hash algorithms. + * + * \sa SHA224, SHA256, SHA384, SHA3_256, BLAKE2s + */ + +/** + * \brief Constructs a new hash object. + */ +Hash::Hash() +{ +} + +/** + * \brief Destroys this hash object. + * + * \note Subclasses are responsible for clearing any sensitive data + * that remains in the hash object when it is destroyed. + * + * \sa clear() + */ +Hash::~Hash() +{ +} + +/** + * \fn size_t Hash::hashSize() const + * \brief Size of the hash result from finalize(). + * + * \sa finalize(), blockSize() + */ + +/** + * \fn size_t Hash::blockSize() const + * \brief Size of the internal block used by the hash algorithm. + * + * \sa update(), hashSize() + */ + +/** + * \fn void Hash::reset() + * \brief Resets the hash ready for a new hashing process. + * + * \sa update(), finalize(), resetHMAC() + */ + +/** + * \fn void Hash::update(const void *data, size_t len) + * \brief Updates the hash with more data. + * + * \param data Data to be hashed. + * \param len Number of bytes of data to be hashed. + * + * If finalize() has already been called, then the behavior of update() will + * be undefined. Call reset() first to start a new hashing process. + * + * \sa reset(), finalize() + */ + +/** + * \fn void Hash::finalize(void *hash, size_t len) + * \brief Finalizes the hashing process and returns the hash. + * + * \param hash The buffer to return the hash value in. + * \param len The length of the \a hash buffer, normally hashSize(). + * + * If \a len is less than hashSize(), then the hash value will be + * truncated to the first \a len bytes. If \a len is greater than + * hashSize(), then the remaining bytes will left unchanged. + * + * If finalize() is called again, then the returned \a hash value is + * undefined. Call reset() first to start a new hashing process. + * + * \sa reset(), update(), finalizeHMAC() + */ + +/** + * \fn void Hash::clear() + * \brief Clears the hash state, removing all sensitive data, and then + * resets the hash ready for a new hashing process. + * + * \sa reset() + */ + +/** + * \fn void Hash::resetHMAC(const void *key, size_t keyLen) + * \brief Resets the hash ready for a new HMAC hashing process. + * + * \param key Points to the HMAC key for the hashing process. + * \param keyLen Size of the HMAC \a key in bytes. + * + * The following example computes a HMAC over a series of data blocks + * with a specific key: + * + * \code + * hash.resetHMAC(key, sizeof(key)); + * hash.update(data1, sizeof(data1)); + * hash.update(data2, sizeof(data2)); + * ... + * hash.update(dataN, sizeof(dataN)); + * hash.finalizeHMAC(key, sizeof(key), hmac, sizeof(hmac)); + * \endcode + * + * The same key must be passed to both resetHMAC() and finalizeHMAC(). + * + * \sa finalizeHMAC(), reset() + */ + +/** + * \fn void Hash::finalizeHMAC(const void *key, size_t keyLen, void *hash, size_t hashLen) + * \brief Finalizes the HMAC hashing process and returns the hash. + * + * \param key Points to the HMAC key for the hashing process. The contents + * of this array must be identical to the value passed to resetHMAC(). + * \param keyLen Size of the HMAC \a key in bytes. + * \param hash The buffer to return the hash value in. + * \param hashLen The length of the \a hash buffer, normally hashSize(). + * + * \sa resetHMAC(), finalize() + */ + +/** + * \brief Formats a HMAC key into a block. + * + * \param block The block to format the key into. Must be at least + * blockSize() bytes in length. + * \param key Points to the HMAC key for the hashing process. + * \param len Length of the HMAC \a key in bytes. + * \param pad Inner (0x36) or outer (0x5C) padding value to XOR with + * the formatted HMAC key. + * + * This function is intended to help subclasses implement resetHMAC() and + * finalizeHMAC() by directly formatting the HMAC key into the subclass's + * internal block buffer and resetting the hash. + */ +void Hash::formatHMACKey(void *block, const void *key, size_t len, uint8_t pad) +{ + size_t size = blockSize(); + reset(); + if (len <= size) { + memcpy(block, key, len); + } else { + update(key, len); + len = hashSize(); + finalize(block, len); + reset(); + } + uint8_t *b = (uint8_t *)block; + memset(b + len, pad, size - len); + while (len > 0) { + *b++ ^= pad; + --len; + } +} + +/** + * \fn void hmac(void *out, size_t outLen, const void *key, size_t keyLen, const void *data, size_t dataLen) + * \brief All-in-one convenience function for computing HMAC values. + * + * \param out Points to the buffer to receive the output HMAC value. + * \param outLen Length of the buffer to receive the output HMAC value. + * \param key Points to the HMAC key for the hashing process. + * \param keyLen Length of the HMAC \a key in bytes. + * \param data Points to the data to hash under the HMAC \a key. + * \param dataLen Length of the input \a data in bytes. + * + * This is a convenience function for computing a HMAC value over a block + * of input data under a given key. The template argument T must be the + * name of a class that inherits from Hash. The following example + * computes a HMAC value using the SHA256 hash algorithm: + * + * \code + * uint8_t out[SHA256::HASH_SIZE]; + * hmac(out, sizeof(out), key, keyLen, data, dataLen); + * \endcode + */ diff --git a/lib/rweather_crypto/Hash.h b/lib/rweather_crypto/Hash.h new file mode 100644 index 000000000..18c72ffff --- /dev/null +++ b/lib/rweather_crypto/Hash.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2015 Southern Storm Software, Pty Ltd. + * + * 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 CRYPTO_HASH_h +#define CRYPTO_HASH_h + +#include +#include + +class Hash +{ +public: + Hash(); + virtual ~Hash(); + + virtual size_t hashSize() const = 0; + virtual size_t blockSize() const = 0; + + virtual void reset() = 0; + virtual void update(const void *data, size_t len) = 0; + virtual void finalize(void *hash, size_t len) = 0; + + virtual void clear() = 0; + + virtual void resetHMAC(const void *key, size_t keyLen) = 0; + virtual void finalizeHMAC(const void *key, size_t keyLen, void *hash, size_t hashLen) = 0; + +protected: + void formatHMACKey(void *block, const void *key, size_t len, uint8_t pad); +}; + +template void hmac + (void *out, size_t outLen, const void *key, size_t keyLen, + const void *data, size_t dataLen) +{ + T context; + context.resetHMAC(key, keyLen); + context.update(data, dataLen); + context.finalizeHMAC(key, keyLen, out, outLen); +} + +#endif diff --git a/lib/rweather_crypto/SHA256.cpp b/lib/rweather_crypto/SHA256.cpp new file mode 100644 index 000000000..078326957 --- /dev/null +++ b/lib/rweather_crypto/SHA256.cpp @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2015 Southern Storm Software, Pty Ltd. + * + * 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 "SHA256.h" +#include "Crypto.h" +#include "utility/RotateUtil.h" +#include "utility/EndianUtil.h" +#include "utility/ProgMemUtil.h" +#include + +/** + * \class SHA256 SHA256.h + * \brief SHA-256 hash algorithm. + * + * Reference: http://en.wikipedia.org/wiki/SHA-2 + * + * \sa SHA224, SHA384, SHA512, SHA3_256, BLAKE2s + */ + +/** + * \var SHA256::HASH_SIZE + * \brief Constant for the size of the hash output of SHA256. + */ + +/** + * \var SHA256::BLOCK_SIZE + * \brief Constant for the block size of SHA256. + */ + +/** + * \brief Constructs a SHA-256 hash object. + */ +SHA256::SHA256() +{ + reset(); +} + +/** + * \brief Destroys this SHA-256 hash object after clearing + * sensitive information. + */ +SHA256::~SHA256() +{ + clean(state); +} + +size_t SHA256::hashSize() const +{ + return 32; +} + +size_t SHA256::blockSize() const +{ + return 64; +} + +void SHA256::reset() +{ + state.h[0] = 0x6a09e667; + state.h[1] = 0xbb67ae85; + state.h[2] = 0x3c6ef372; + state.h[3] = 0xa54ff53a, + state.h[4] = 0x510e527f; + state.h[5] = 0x9b05688c; + state.h[6] = 0x1f83d9ab; + state.h[7] = 0x5be0cd19; + state.chunkSize = 0; + state.length = 0; +} + +void SHA256::update(const void *data, size_t len) +{ + // Update the total length (in bits, not bytes). + state.length += ((uint64_t)len) << 3; + + // Break the input up into 512-bit chunks and process each in turn. + const uint8_t *d = (const uint8_t *)data; + while (len > 0) { + uint8_t size = 64 - state.chunkSize; + if (size > len) + size = len; + memcpy(((uint8_t *)state.w) + state.chunkSize, d, size); + state.chunkSize += size; + len -= size; + d += size; + if (state.chunkSize == 64) { + processChunk(); + state.chunkSize = 0; + } + } +} + +void SHA256::finalize(void *hash, size_t len) +{ + // Pad the last chunk. We may need two padding chunks if there + // isn't enough room in the first for the padding and length. + uint8_t *wbytes = (uint8_t *)state.w; + if (state.chunkSize <= (64 - 9)) { + wbytes[state.chunkSize] = 0x80; + memset(wbytes + state.chunkSize + 1, 0x00, 64 - 8 - (state.chunkSize + 1)); + state.w[14] = htobe32((uint32_t)(state.length >> 32)); + state.w[15] = htobe32((uint32_t)state.length); + processChunk(); + } else { + wbytes[state.chunkSize] = 0x80; + memset(wbytes + state.chunkSize + 1, 0x00, 64 - (state.chunkSize + 1)); + processChunk(); + memset(wbytes, 0x00, 64 - 8); + state.w[14] = htobe32((uint32_t)(state.length >> 32)); + state.w[15] = htobe32((uint32_t)state.length); + processChunk(); + } + + // Convert the result into big endian and return it. + for (uint8_t posn = 0; posn < 8; ++posn) + state.w[posn] = htobe32(state.h[posn]); + + // Copy the hash to the caller's return buffer. + size_t maxHashSize = hashSize(); + if (len > maxHashSize) + len = maxHashSize; + memcpy(hash, state.w, len); +} + +void SHA256::clear() +{ + clean(state); + reset(); +} + +void SHA256::resetHMAC(const void *key, size_t keyLen) +{ + formatHMACKey(state.w, key, keyLen, 0x36); + state.length += 64 * 8; + processChunk(); +} + +void SHA256::finalizeHMAC(const void *key, size_t keyLen, void *hash, size_t hashLen) +{ + uint8_t temp[32]; + finalize(temp, sizeof(temp)); + formatHMACKey(state.w, key, keyLen, 0x5C); + state.length += 64 * 8; + processChunk(); + update(temp, hashSize()); + finalize(hash, hashLen); + clean(temp); +} + +/** + * \brief Processes a single 512-bit chunk with the core SHA-256 algorithm. + * + * Reference: http://en.wikipedia.org/wiki/SHA-2 + */ +void SHA256::processChunk() +{ + // Round constants for SHA-256. + static uint32_t const k[64] PROGMEM = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + }; + + // Convert the first 16 words from big endian to host byte order. + uint8_t index; + for (index = 0; index < 16; ++index) + state.w[index] = be32toh(state.w[index]); + + // Initialise working variables to the current hash value. + uint32_t a = state.h[0]; + uint32_t b = state.h[1]; + uint32_t c = state.h[2]; + uint32_t d = state.h[3]; + uint32_t e = state.h[4]; + uint32_t f = state.h[5]; + uint32_t g = state.h[6]; + uint32_t h = state.h[7]; + + // Perform the first 16 rounds of the compression function main loop. + uint32_t temp1, temp2; + for (index = 0; index < 16; ++index) { + temp1 = h + pgm_read_dword(k + index) + state.w[index] + + (rightRotate6(e) ^ rightRotate11(e) ^ rightRotate25(e)) + + ((e & f) ^ ((~e) & g)); + temp2 = (rightRotate2(a) ^ rightRotate13(a) ^ rightRotate22(a)) + + ((a & b) ^ (a & c) ^ (b & c)); + h = g; + g = f; + f = e; + e = d + temp1; + d = c; + c = b; + b = a; + a = temp1 + temp2; + } + + // Perform the 48 remaining rounds. We expand the first 16 words to + // 64 in-place in the "w" array. This saves 192 bytes of memory + // that would have otherwise need to be allocated to the "w" array. + for (; index < 64; ++index) { + // Expand the next word. + temp1 = state.w[(index - 15) & 0x0F]; + temp2 = state.w[(index - 2) & 0x0F]; + temp1 = state.w[index & 0x0F] = + state.w[(index - 16) & 0x0F] + state.w[(index - 7) & 0x0F] + + (rightRotate7(temp1) ^ rightRotate18(temp1) ^ (temp1 >> 3)) + + (rightRotate17(temp2) ^ rightRotate19(temp2) ^ (temp2 >> 10)); + + // Perform the round. + temp1 = h + pgm_read_dword(k + index) + temp1 + + (rightRotate6(e) ^ rightRotate11(e) ^ rightRotate25(e)) + + ((e & f) ^ ((~e) & g)); + temp2 = (rightRotate2(a) ^ rightRotate13(a) ^ rightRotate22(a)) + + ((a & b) ^ (a & c) ^ (b & c)); + h = g; + g = f; + f = e; + e = d + temp1; + d = c; + c = b; + b = a; + a = temp1 + temp2; + } + + // Add the compressed chunk to the current hash value. + state.h[0] += a; + state.h[1] += b; + state.h[2] += c; + state.h[3] += d; + state.h[4] += e; + state.h[5] += f; + state.h[6] += g; + state.h[7] += h; + + // Attempt to clean up the stack. + a = b = c = d = e = f = g = h = temp1 = temp2 = 0; +} diff --git a/lib/rweather_crypto/SHA256.h b/lib/rweather_crypto/SHA256.h new file mode 100644 index 000000000..ffc681c3f --- /dev/null +++ b/lib/rweather_crypto/SHA256.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2015 Southern Storm Software, Pty Ltd. + * + * 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 CRYPTO_SHA256_h +#define CRYPTO_SHA256_h + +#include "Hash.h" + +class SHA256 : public Hash +{ +public: + SHA256(); + virtual ~SHA256(); + + size_t hashSize() const; + size_t blockSize() const; + + void reset(); + void update(const void *data, size_t len); + void finalize(void *hash, size_t len); + + void clear(); + + void resetHMAC(const void *key, size_t keyLen); + void finalizeHMAC(const void *key, size_t keyLen, void *hash, size_t hashLen); + + static const size_t HASH_SIZE = 32; + static const size_t BLOCK_SIZE = 64; + +protected: + struct { + uint32_t h[8]; + uint32_t w[16]; + uint64_t length; + uint8_t chunkSize; + } state; + + void processChunk(); +}; + +#endif diff --git a/lib/rweather_crypto/library.json b/lib/rweather_crypto/library.json new file mode 100644 index 000000000..878b1cbf3 --- /dev/null +++ b/lib/rweather_crypto/library.json @@ -0,0 +1,19 @@ +{ + "name": "Crypto", + "version": "0.4.0", + "description": "Minimal subset of rweather/arduinolibs Crypto library (AES128 + SHA256 only)", + "authors": [ + { + "name": "Rhys Weatherley", + "email": "rhys.weatherley@gmail.com", + "url": "https://rweather.github.io/arduinolibs/crypto.html" + } + ], + "repository": { + "type": "git", + "url": "https://github.com/rweather/arduinolibs.git" + }, + "license": "MIT", + "frameworks": ["arduino"], + "platforms": ["*"] +} diff --git a/lib/rweather_crypto/utility/EndianUtil.h b/lib/rweather_crypto/utility/EndianUtil.h new file mode 100644 index 000000000..b4e31aa7e --- /dev/null +++ b/lib/rweather_crypto/utility/EndianUtil.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2015 Southern Storm Software, Pty Ltd. + * + * 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 CRYPTO_ENDIANUTIL_H +#define CRYPTO_ENDIANUTIL_H + +#include + +#if !defined(HOST_BUILD) + +// CPU is assumed to be little endian. Edit this file if you +// need to port this library to a big endian CPU. + +#define CRYPTO_LITTLE_ENDIAN 1 + +#define htole16(x) (x) +#define le16toh(x) (x) +#define htobe16(x) \ + (__extension__ ({ \ + uint16_t _temp = (x); \ + ((_temp >> 8) & 0x00FF) | \ + ((_temp << 8) & 0xFF00); \ + })) +#define be16toh(x) (htobe16((x))) + +#define htole32(x) (x) +#define le32toh(x) (x) +#define htobe32(x) \ + (__extension__ ({ \ + uint32_t _temp = (x); \ + ((_temp >> 24) & 0x000000FF) | \ + ((_temp >> 8) & 0x0000FF00) | \ + ((_temp << 8) & 0x00FF0000) | \ + ((_temp << 24) & 0xFF000000); \ + })) +#define be32toh(x) (htobe32((x))) + +#define htole64(x) (x) +#define le64toh(x) (x) +#define htobe64(x) \ + (__extension__ ({ \ + uint64_t __temp = (x); \ + uint32_t __low = htobe32((uint32_t)__temp); \ + uint32_t __high = htobe32((uint32_t)(__temp >> 32)); \ + (((uint64_t)__low) << 32) | __high; \ + })) +#define be64toh(x) (htobe64((x))) + +#else // HOST_BUILD + +#include +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define CRYPTO_LITTLE_ENDIAN 1 +#endif + +#endif // HOST_BUILD + +#endif diff --git a/lib/rweather_crypto/utility/ProgMemUtil.h b/lib/rweather_crypto/utility/ProgMemUtil.h new file mode 100644 index 000000000..023154b7c --- /dev/null +++ b/lib/rweather_crypto/utility/ProgMemUtil.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 Southern Storm Software, Pty Ltd. + * + * 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 CRYPTO_PROGMEMUTIL_H +#define CRYPTO_PROGMEMUTIL_H + +#if defined(__AVR__) +#include +#define pgm_read_qword(x) \ + (__extension__ ({ \ + const uint32_t *_temp = (const uint32_t *)(x); \ + ((uint64_t)pgm_read_dword(_temp)) | \ + (((uint64_t)pgm_read_dword(_temp + 1)) << 32); \ + })) +#elif defined(ESP8266) || defined(ESP32) +#include +#define pgm_read_qword(x) \ + (__extension__ ({ \ + const uint32_t *_temp = (const uint32_t *)(x); \ + ((uint64_t)pgm_read_dword(_temp)) | \ + (((uint64_t)pgm_read_dword(_temp + 1)) << 32); \ + })) +#else +#include +#define PROGMEM +#ifndef pgm_read_byte +# define pgm_read_byte(x) (*(x)) +#endif +#ifndef pgm_read_word +# define pgm_read_word(x) (*(x)) +#endif +#ifndef pgm_read_dword +# define pgm_read_dword(x) (*(x)) +#endif +#ifndef pgm_read_qword +# define pgm_read_qword(x) (*(x)) +#endif +#ifndef memcpy_P +# define memcpy_P(d,s,l) memcpy((d), (s), (l)) +#endif +#endif + +#endif diff --git a/lib/rweather_crypto/utility/RotateUtil.h b/lib/rweather_crypto/utility/RotateUtil.h new file mode 100644 index 000000000..fcabc824a --- /dev/null +++ b/lib/rweather_crypto/utility/RotateUtil.h @@ -0,0 +1,696 @@ +/* + * Copyright (C) 2015 Southern Storm Software, Pty Ltd. + * + * 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 CRYPTO_ROTATEUTIL_H +#define CRYPTO_ROTATEUTIL_H + +#include + +// Rotation functions that are optimised for best performance on AVR. +// The most efficient rotations are where the number of bits is 1 or a +// multiple of 8, so we compose the efficient rotations to produce all +// other rotation counts of interest. + +#if defined(__AVR__) +#define CRYPTO_ROTATE32_COMPOSED 1 +#define CRYPTO_ROTATE64_COMPOSED 0 +#else +#define CRYPTO_ROTATE32_COMPOSED 0 +#define CRYPTO_ROTATE64_COMPOSED 0 +#endif + +#if CRYPTO_ROTATE32_COMPOSED + +// Rotation macros for 32-bit arguments. + +// Generic left rotate - best performance when "bits" is 1 or a multiple of 8. +#define leftRotate(a, bits) \ + (__extension__ ({ \ + uint32_t _temp = (a); \ + (_temp << (bits)) | (_temp >> (32 - (bits))); \ + })) + +// Generic right rotate - best performance when "bits" is 1 or a multiple of 8. +#define rightRotate(a, bits) \ + (__extension__ ({ \ + uint32_t _temp = (a); \ + (_temp >> (bits)) | (_temp << (32 - (bits))); \ + })) + +// Left rotate by 1. +#define leftRotate1(a) (leftRotate((a), 1)) + +// Left rotate by 2. +#define leftRotate2(a) (leftRotate(leftRotate((a), 1), 1)) + +// Left rotate by 3. +#define leftRotate3(a) (leftRotate(leftRotate(leftRotate((a), 1), 1), 1)) + +// Left rotate by 4. +#define leftRotate4(a) (leftRotate(leftRotate(leftRotate(leftRotate((a), 1), 1), 1), 1)) + +// Left rotate by 5: Rotate left by 8, then right by 3. +#define leftRotate5(a) (rightRotate(rightRotate(rightRotate(leftRotate((a), 8), 1), 1), 1)) + +// Left rotate by 6: Rotate left by 8, then right by 2. +#define leftRotate6(a) (rightRotate(rightRotate(leftRotate((a), 8), 1), 1)) + +// Left rotate by 7: Rotate left by 8, then right by 1. +#define leftRotate7(a) (rightRotate(leftRotate((a), 8), 1)) + +// Left rotate by 8. +#define leftRotate8(a) (leftRotate((a), 8)) + +// Left rotate by 9: Rotate left by 8, then left by 1. +#define leftRotate9(a) (leftRotate(leftRotate((a), 8), 1)) + +// Left rotate by 10: Rotate left by 8, then left by 2. +#define leftRotate10(a) (leftRotate(leftRotate(leftRotate((a), 8), 1), 1)) + +// Left rotate by 11: Rotate left by 8, then left by 3. +#define leftRotate11(a) (leftRotate(leftRotate(leftRotate(leftRotate((a), 8), 1), 1), 1)) + +// Left rotate by 12: Rotate left by 16, then right by 4. +#define leftRotate12(a) (rightRotate(rightRotate(rightRotate(rightRotate(leftRotate((a), 16), 1), 1), 1), 1)) + +// Left rotate by 13: Rotate left by 16, then right by 3. +#define leftRotate13(a) (rightRotate(rightRotate(rightRotate(leftRotate((a), 16), 1), 1), 1)) + +// Left rotate by 14: Rotate left by 16, then right by 2. +#define leftRotate14(a) (rightRotate(rightRotate(leftRotate((a), 16), 1), 1)) + +// Left rotate by 15: Rotate left by 16, then right by 1. +#define leftRotate15(a) (rightRotate(leftRotate((a), 16), 1)) + +// Left rotate by 16. +#define leftRotate16(a) (leftRotate((a), 16)) + +// Left rotate by 17: Rotate left by 16, then left by 1. +#define leftRotate17(a) (leftRotate(leftRotate((a), 16), 1)) + +// Left rotate by 18: Rotate left by 16, then left by 2. +#define leftRotate18(a) (leftRotate(leftRotate(leftRotate((a), 16), 1), 1)) + +// Left rotate by 19: Rotate left by 16, then left by 3. +#define leftRotate19(a) (leftRotate(leftRotate(leftRotate(leftRotate((a), 16), 1), 1), 1)) + +// Left rotate by 20: Rotate left by 16, then left by 4. +#define leftRotate20(a) (leftRotate(leftRotate(leftRotate(leftRotate(leftRotate((a), 16), 1), 1), 1), 1)) + +// Left rotate by 21: Rotate left by 24, then right by 3. +#define leftRotate21(a) (rightRotate(rightRotate(rightRotate(leftRotate((a), 24), 1), 1), 1)) + +// Left rotate by 22: Rotate left by 24, then right by 2. +#define leftRotate22(a) (rightRotate(rightRotate(leftRotate((a), 24), 1), 1)) + +// Left rotate by 23: Rotate left by 24, then right by 1. +#define leftRotate23(a) (rightRotate(leftRotate((a), 24), 1)) + +// Left rotate by 24. +#define leftRotate24(a) (leftRotate((a), 24)) + +// Left rotate by 25: Rotate left by 24, then left by 1. +#define leftRotate25(a) (leftRotate(leftRotate((a), 24), 1)) + +// Left rotate by 26: Rotate left by 24, then left by 2. +#define leftRotate26(a) (leftRotate(leftRotate(leftRotate((a), 24), 1), 1)) + +// Left rotate by 27: Rotate left by 24, then left by 3. +#define leftRotate27(a) (leftRotate(leftRotate(leftRotate(leftRotate((a), 24), 1), 1), 1)) + +// Left rotate by 28: Rotate right by 4. +#define leftRotate28(a) (rightRotate(rightRotate(rightRotate(rightRotate((a), 1), 1), 1), 1)) + +// Left rotate by 29: Rotate right by 3. +#define leftRotate29(a) (rightRotate(rightRotate(rightRotate((a), 1), 1), 1)) + +// Left rotate by 30: Rotate right by 2. +#define leftRotate30(a) (rightRotate(rightRotate((a), 1), 1)) + +// Left rotate by 31: Rotate right by 1. +#define leftRotate31(a) (rightRotate((a), 1)) + +// Define the 32-bit right rotations in terms of left rotations. +#define rightRotate1(a) (leftRotate31((a))) +#define rightRotate2(a) (leftRotate30((a))) +#define rightRotate3(a) (leftRotate29((a))) +#define rightRotate4(a) (leftRotate28((a))) +#define rightRotate5(a) (leftRotate27((a))) +#define rightRotate6(a) (leftRotate26((a))) +#define rightRotate7(a) (leftRotate25((a))) +#define rightRotate8(a) (leftRotate24((a))) +#define rightRotate9(a) (leftRotate23((a))) +#define rightRotate10(a) (leftRotate22((a))) +#define rightRotate11(a) (leftRotate21((a))) +#define rightRotate12(a) (leftRotate20((a))) +#define rightRotate13(a) (leftRotate19((a))) +#define rightRotate14(a) (leftRotate18((a))) +#define rightRotate15(a) (leftRotate17((a))) +#define rightRotate16(a) (leftRotate16((a))) +#define rightRotate17(a) (leftRotate15((a))) +#define rightRotate18(a) (leftRotate14((a))) +#define rightRotate19(a) (leftRotate13((a))) +#define rightRotate20(a) (leftRotate12((a))) +#define rightRotate21(a) (leftRotate11((a))) +#define rightRotate22(a) (leftRotate10((a))) +#define rightRotate23(a) (leftRotate9((a))) +#define rightRotate24(a) (leftRotate8((a))) +#define rightRotate25(a) (leftRotate7((a))) +#define rightRotate26(a) (leftRotate6((a))) +#define rightRotate27(a) (leftRotate5((a))) +#define rightRotate28(a) (leftRotate4((a))) +#define rightRotate29(a) (leftRotate3((a))) +#define rightRotate30(a) (leftRotate2((a))) +#define rightRotate31(a) (leftRotate1((a))) + +#else // !CRYPTO_ROTATE32_COMPOSED + +// Generic rotation functions. All bit shifts are considered to have +// similar performance. Usually true of 32-bit and higher platforms. + +// Rotation macros for 32-bit arguments. + +// Generic left rotate. +#define leftRotate(a, bits) \ + (__extension__ ({ \ + uint32_t _temp = (a); \ + (_temp << (bits)) | (_temp >> (32 - (bits))); \ + })) + +// Generic right rotate. +#define rightRotate(a, bits) \ + (__extension__ ({ \ + uint32_t _temp = (a); \ + (_temp >> (bits)) | (_temp << (32 - (bits))); \ + })) + +// Left rotate by a specific number of bits. +#define leftRotate1(a) (leftRotate((a), 1)) +#define leftRotate2(a) (leftRotate((a), 2)) +#define leftRotate3(a) (leftRotate((a), 3)) +#define leftRotate4(a) (leftRotate((a), 4)) +#define leftRotate5(a) (leftRotate((a), 5)) +#define leftRotate6(a) (leftRotate((a), 6)) +#define leftRotate7(a) (leftRotate((a), 7)) +#define leftRotate8(a) (leftRotate((a), 8)) +#define leftRotate9(a) (leftRotate((a), 9)) +#define leftRotate10(a) (leftRotate((a), 10)) +#define leftRotate11(a) (leftRotate((a), 11)) +#define leftRotate12(a) (leftRotate((a), 12)) +#define leftRotate13(a) (leftRotate((a), 13)) +#define leftRotate14(a) (leftRotate((a), 14)) +#define leftRotate15(a) (leftRotate((a), 15)) +#define leftRotate16(a) (leftRotate((a), 16)) +#define leftRotate17(a) (leftRotate((a), 17)) +#define leftRotate18(a) (leftRotate((a), 18)) +#define leftRotate19(a) (leftRotate((a), 19)) +#define leftRotate20(a) (leftRotate((a), 20)) +#define leftRotate21(a) (leftRotate((a), 21)) +#define leftRotate22(a) (leftRotate((a), 22)) +#define leftRotate23(a) (leftRotate((a), 23)) +#define leftRotate24(a) (leftRotate((a), 24)) +#define leftRotate25(a) (leftRotate((a), 25)) +#define leftRotate26(a) (leftRotate((a), 26)) +#define leftRotate27(a) (leftRotate((a), 27)) +#define leftRotate28(a) (leftRotate((a), 28)) +#define leftRotate29(a) (leftRotate((a), 29)) +#define leftRotate30(a) (leftRotate((a), 30)) +#define leftRotate31(a) (leftRotate((a), 31)) + +// Right rotate by a specific number of bits. +#define rightRotate1(a) (rightRotate((a), 1)) +#define rightRotate2(a) (rightRotate((a), 2)) +#define rightRotate3(a) (rightRotate((a), 3)) +#define rightRotate4(a) (rightRotate((a), 4)) +#define rightRotate5(a) (rightRotate((a), 5)) +#define rightRotate6(a) (rightRotate((a), 6)) +#define rightRotate7(a) (rightRotate((a), 7)) +#define rightRotate8(a) (rightRotate((a), 8)) +#define rightRotate9(a) (rightRotate((a), 9)) +#define rightRotate10(a) (rightRotate((a), 10)) +#define rightRotate11(a) (rightRotate((a), 11)) +#define rightRotate12(a) (rightRotate((a), 12)) +#define rightRotate13(a) (rightRotate((a), 13)) +#define rightRotate14(a) (rightRotate((a), 14)) +#define rightRotate15(a) (rightRotate((a), 15)) +#define rightRotate16(a) (rightRotate((a), 16)) +#define rightRotate17(a) (rightRotate((a), 17)) +#define rightRotate18(a) (rightRotate((a), 18)) +#define rightRotate19(a) (rightRotate((a), 19)) +#define rightRotate20(a) (rightRotate((a), 20)) +#define rightRotate21(a) (rightRotate((a), 21)) +#define rightRotate22(a) (rightRotate((a), 22)) +#define rightRotate23(a) (rightRotate((a), 23)) +#define rightRotate24(a) (rightRotate((a), 24)) +#define rightRotate25(a) (rightRotate((a), 25)) +#define rightRotate26(a) (rightRotate((a), 26)) +#define rightRotate27(a) (rightRotate((a), 27)) +#define rightRotate28(a) (rightRotate((a), 28)) +#define rightRotate29(a) (rightRotate((a), 29)) +#define rightRotate30(a) (rightRotate((a), 30)) +#define rightRotate31(a) (rightRotate((a), 31)) + +#endif // !CRYPTO_ROTATE32_COMPOSED + +#if CRYPTO_ROTATE64_COMPOSED + +// Rotation macros for 64-bit arguments. + +// Generic left rotate - best performance when "bits" is 1 or a multiple of 8. +#define leftRotate_64(a, bits) \ + (__extension__ ({ \ + uint64_t _temp = (a); \ + (_temp << (bits)) | (_temp >> (64 - (bits))); \ + })) + +// Generic right rotate - best performance when "bits" is 1 or a multiple of 8. +#define rightRotate_64(a, bits) \ + (__extension__ ({ \ + uint64_t _temp = (a); \ + (_temp >> (bits)) | (_temp << (64 - (bits))); \ + })) + +// Left rotate by 1. +#define leftRotate1_64(a) (leftRotate_64((a), 1)) + +// Left rotate by 2. +#define leftRotate2_64(a) (leftRotate_64(leftRotate_64((a), 1), 1)) + +// Left rotate by 3. +#define leftRotate3_64(a) (leftRotate_64(leftRotate_64(leftRotate_64((a), 1), 1), 1)) + +// Left rotate by 4. +#define leftRotate4_64(a) (leftRotate_64(leftRotate_64(leftRotate_64(leftRotate_64((a), 1), 1), 1), 1)) + +// Left rotate by 5: Rotate left by 8, then right by 3. +#define leftRotate5_64(a) (rightRotate_64(rightRotate_64(rightRotate_64(leftRotate_64((a), 8), 1), 1), 1)) + +// Left rotate by 6: Rotate left by 8, then right by 2. +#define leftRotate6_64(a) (rightRotate_64(rightRotate_64(leftRotate_64((a), 8), 1), 1)) + +// Left rotate by 7: Rotate left by 8, then right by 1. +#define leftRotate7_64(a) (rightRotate_64(leftRotate_64((a), 8), 1)) + +// Left rotate by 8. +#define leftRotate8_64(a) (leftRotate_64((a), 8)) + +// Left rotate by 9: Rotate left by 8, then left by 1. +#define leftRotate9_64(a) (leftRotate_64(leftRotate_64((a), 8), 1)) + +// Left rotate by 10: Rotate left by 8, then left by 2. +#define leftRotate10_64(a) (leftRotate_64(leftRotate_64(leftRotate_64((a), 8), 1), 1)) + +// Left rotate by 11: Rotate left by 8, then left by 3. +#define leftRotate11_64(a) (leftRotate_64(leftRotate_64(leftRotate_64(leftRotate_64((a), 8), 1), 1), 1)) + +// Left rotate by 12: Rotate left by 16, then right by 4. +#define leftRotate12_64(a) (rightRotate_64(rightRotate_64(rightRotate_64(rightRotate_64(leftRotate_64((a), 16), 1), 1), 1), 1)) + +// Left rotate by 13: Rotate left by 16, then right by 3. +#define leftRotate13_64(a) (rightRotate_64(rightRotate_64(rightRotate_64(leftRotate_64((a), 16), 1), 1), 1)) + +// Left rotate by 14: Rotate left by 16, then right by 2. +#define leftRotate14_64(a) (rightRotate_64(rightRotate_64(leftRotate_64((a), 16), 1), 1)) + +// Left rotate by 15: Rotate left by 16, then right by 1. +#define leftRotate15_64(a) (rightRotate_64(leftRotate_64((a), 16), 1)) + +// Left rotate by 16. +#define leftRotate16_64(a) (leftRotate_64((a), 16)) + +// Left rotate by 17: Rotate left by 16, then left by 1. +#define leftRotate17_64(a) (leftRotate_64(leftRotate_64((a), 16), 1)) + +// Left rotate by 18: Rotate left by 16, then left by 2. +#define leftRotate18_64(a) (leftRotate_64(leftRotate_64(leftRotate_64((a), 16), 1), 1)) + +// Left rotate by 19: Rotate left by 16, then left by 3. +#define leftRotate19_64(a) (leftRotate_64(leftRotate_64(leftRotate_64(leftRotate_64((a), 16), 1), 1), 1)) + +// Left rotate by 20: Rotate left by 16, then left by 4. +#define leftRotate20_64(a) (leftRotate_64(leftRotate_64(leftRotate_64(leftRotate_64(leftRotate_64((a), 16), 1), 1), 1), 1)) + +// Left rotate by 21: Rotate left by 24, then right by 3. +#define leftRotate21_64(a) (rightRotate_64(rightRotate_64(rightRotate_64(leftRotate_64((a), 24), 1), 1), 1)) + +// Left rotate by 22: Rotate left by 24, then right by 2. +#define leftRotate22_64(a) (rightRotate_64(rightRotate_64(leftRotate_64((a), 24), 1), 1)) + +// Left rotate by 23: Rotate left by 24, then right by 1. +#define leftRotate23_64(a) (rightRotate_64(leftRotate_64((a), 24), 1)) + +// Left rotate by 24. +#define leftRotate24_64(a) (leftRotate_64((a), 24)) + +// Left rotate by 25: Rotate left by 24, then left by 1. +#define leftRotate25_64(a) (leftRotate_64(leftRotate_64((a), 24), 1)) + +// Left rotate by 26: Rotate left by 24, then left by 2. +#define leftRotate26_64(a) (leftRotate_64(leftRotate_64(leftRotate_64((a), 24), 1), 1)) + +// Left rotate by 27: Rotate left by 24, then left by 3. +#define leftRotate27_64(a) (leftRotate_64(leftRotate_64(leftRotate_64(leftRotate_64((a), 24), 1), 1), 1)) + +// Left rotate by 28: Rotate left by 24, then left by 4. +#define leftRotate28_64(a) (leftRotate_64(leftRotate_64(leftRotate_64(leftRotate_64(leftRotate_64((a), 24), 1), 1), 1), 1)) + +// Left rotate by 29: Rotate left by 32, then right by 3. +#define leftRotate29_64(a) (rightRotate_64(rightRotate_64(rightRotate_64(leftRotate_64((a), 32), 1), 1), 1)) + +// Left rotate by 30: Rotate left by 32, then right by 2. +#define leftRotate30_64(a) (rightRotate_64(rightRotate_64(leftRotate_64((a), 32), 1), 1)) + +// Left rotate by 31: Rotate left by 32, then right by 1. +#define leftRotate31_64(a) (rightRotate_64(leftRotate_64((a), 32), 1)) + +// Left rotate by 32. +#define leftRotate32_64(a) (leftRotate_64((a), 32)) + +// Left rotate by 33: Rotate left by 32, then left by 1. +#define leftRotate33_64(a) (leftRotate_64(leftRotate_64((a), 32), 1)) + +// Left rotate by 34: Rotate left by 32, then left by 2. +#define leftRotate34_64(a) (leftRotate_64(leftRotate_64(leftRotate_64((a), 32), 1), 1)) + +// Left rotate by 35: Rotate left by 32, then left by 3. +#define leftRotate35_64(a) (leftRotate_64(leftRotate_64(leftRotate_64(leftRotate_64((a), 32), 1), 1), 1)) + +// Left rotate by 36: Rotate left by 32, then left by 4. +#define leftRotate36_64(a) (leftRotate_64(leftRotate_64(leftRotate_64(leftRotate_64(leftRotate_64((a), 32), 1), 1), 1), 1)) + +// Left rotate by 37: Rotate left by 40, then right by 3. +#define leftRotate37_64(a) (rightRotate_64(rightRotate_64(rightRotate_64(leftRotate_64((a), 40), 1), 1), 1)) + +// Left rotate by 38: Rotate left by 40, then right by 2. +#define leftRotate38_64(a) (rightRotate_64(rightRotate_64(leftRotate_64((a), 40), 1), 1)) + +// Left rotate by 39: Rotate left by 40, then right by 1. +#define leftRotate39_64(a) (rightRotate_64(leftRotate_64((a), 40), 1)) + +// Left rotate by 40. +#define leftRotate40_64(a) (leftRotate_64((a), 40)) + +// Left rotate by 41: Rotate left by 40, then left by 1. +#define leftRotate41_64(a) (leftRotate_64(leftRotate_64((a), 40), 1)) + +// Left rotate by 42: Rotate left by 40, then left by 2. +#define leftRotate42_64(a) (leftRotate_64(leftRotate_64(leftRotate_64((a), 40), 1), 1)) + +// Left rotate by 43: Rotate left by 40, then left by 3. +#define leftRotate43_64(a) (leftRotate_64(leftRotate_64(leftRotate_64(leftRotate_64((a), 40), 1), 1), 1)) + +// Left rotate by 44: Rotate left by 40, then left by 4. +#define leftRotate44_64(a) (leftRotate_64(leftRotate_64(leftRotate_64(leftRotate_64(leftRotate_64((a), 40), 1), 1), 1), 1)) + +// Left rotate by 45: Rotate left by 48, then right by 3. +#define leftRotate45_64(a) (rightRotate_64(rightRotate_64(rightRotate_64(leftRotate_64((a), 48), 1), 1), 1)) + +// Left rotate by 46: Rotate left by 48, then right by 2. +#define leftRotate46_64(a) (rightRotate_64(rightRotate_64(leftRotate_64((a), 48), 1), 1)) + +// Left rotate by 47: Rotate left by 48, then right by 1. +#define leftRotate47_64(a) (rightRotate_64(leftRotate_64((a), 48), 1)) + +// Left rotate by 48. +#define leftRotate48_64(a) (leftRotate_64((a), 48)) + +// Left rotate by 49: Rotate left by 48, then left by 1. +#define leftRotate49_64(a) (leftRotate_64(leftRotate_64((a), 48), 1)) + +// Left rotate by 50: Rotate left by 48, then left by 2. +#define leftRotate50_64(a) (leftRotate_64(leftRotate_64(leftRotate_64((a), 48), 1), 1)) + +// Left rotate by 51: Rotate left by 48, then left by 3. +#define leftRotate51_64(a) (leftRotate_64(leftRotate_64(leftRotate_64(leftRotate_64((a), 48), 1), 1), 1)) + +// Left rotate by 52: Rotate left by 48, then left by 4. +#define leftRotate52_64(a) (leftRotate_64(leftRotate_64(leftRotate_64(leftRotate_64(leftRotate_64((a), 48), 1), 1), 1), 1)) + +// Left rotate by 53: Rotate left by 56, then right by 3. +#define leftRotate53_64(a) (rightRotate_64(rightRotate_64(rightRotate_64(leftRotate_64((a), 56), 1), 1), 1)) + +// Left rotate by 54: Rotate left by 56, then right by 2. +#define leftRotate54_64(a) (rightRotate_64(rightRotate_64(leftRotate_64((a), 56), 1), 1)) + +// Left rotate by 55: Rotate left by 56, then right by 1. +#define leftRotate55_64(a) (rightRotate_64(leftRotate_64((a), 56), 1)) + +// Left rotate by 56. +#define leftRotate56_64(a) (leftRotate_64((a), 56)) + +// Left rotate by 57: Rotate left by 56, then left by 1. +#define leftRotate57_64(a) (leftRotate_64(leftRotate_64((a), 56), 1)) + +// Left rotate by 58: Rotate left by 56, then left by 2. +#define leftRotate58_64(a) (leftRotate_64(leftRotate_64(leftRotate_64((a), 56), 1), 1)) + +// Left rotate by 59: Rotate left by 56, then left by 3. +#define leftRotate59_64(a) (leftRotate_64(leftRotate_64(leftRotate_64(leftRotate_64((a), 56), 1), 1), 1)) + +// Left rotate by 60: Rotate left by 60, then left by 4. +#define leftRotate60_64(a) (leftRotate_64(leftRotate_64(leftRotate_64(leftRotate_64(leftRotate_64((a), 56), 1), 1), 1), 1)) + +// Left rotate by 61: Rotate right by 3. +#define leftRotate61_64(a) (rightRotate_64(rightRotate_64(rightRotate_64((a), 1), 1), 1)) + +// Left rotate by 62: Rotate right by 2. +#define leftRotate62_64(a) (rightRotate_64(rightRotate_64((a), 1), 1)) + +// Left rotate by 63: Rotate right by 1. +#define leftRotate63_64(a) (rightRotate_64((a), 1)) + +// Define the 64-bit right rotations in terms of left rotations. +#define rightRotate1_64(a) (leftRotate63_64((a))) +#define rightRotate2_64(a) (leftRotate62_64((a))) +#define rightRotate3_64(a) (leftRotate61_64((a))) +#define rightRotate4_64(a) (leftRotate60_64((a))) +#define rightRotate5_64(a) (leftRotate59_64((a))) +#define rightRotate6_64(a) (leftRotate58_64((a))) +#define rightRotate7_64(a) (leftRotate57_64((a))) +#define rightRotate8_64(a) (leftRotate56_64((a))) +#define rightRotate9_64(a) (leftRotate55_64((a))) +#define rightRotate10_64(a) (leftRotate54_64((a))) +#define rightRotate11_64(a) (leftRotate53_64((a))) +#define rightRotate12_64(a) (leftRotate52_64((a))) +#define rightRotate13_64(a) (leftRotate51_64((a))) +#define rightRotate14_64(a) (leftRotate50_64((a))) +#define rightRotate15_64(a) (leftRotate49_64((a))) +#define rightRotate16_64(a) (leftRotate48_64((a))) +#define rightRotate17_64(a) (leftRotate47_64((a))) +#define rightRotate18_64(a) (leftRotate46_64((a))) +#define rightRotate19_64(a) (leftRotate45_64((a))) +#define rightRotate20_64(a) (leftRotate44_64((a))) +#define rightRotate21_64(a) (leftRotate43_64((a))) +#define rightRotate22_64(a) (leftRotate42_64((a))) +#define rightRotate23_64(a) (leftRotate41_64((a))) +#define rightRotate24_64(a) (leftRotate40_64((a))) +#define rightRotate25_64(a) (leftRotate39_64((a))) +#define rightRotate26_64(a) (leftRotate38_64((a))) +#define rightRotate27_64(a) (leftRotate37_64((a))) +#define rightRotate28_64(a) (leftRotate36_64((a))) +#define rightRotate29_64(a) (leftRotate35_64((a))) +#define rightRotate30_64(a) (leftRotate34_64((a))) +#define rightRotate31_64(a) (leftRotate33_64((a))) +#define rightRotate32_64(a) (leftRotate32_64((a))) +#define rightRotate33_64(a) (leftRotate31_64((a))) +#define rightRotate34_64(a) (leftRotate30_64((a))) +#define rightRotate35_64(a) (leftRotate29_64((a))) +#define rightRotate36_64(a) (leftRotate28_64((a))) +#define rightRotate37_64(a) (leftRotate27_64((a))) +#define rightRotate38_64(a) (leftRotate26_64((a))) +#define rightRotate39_64(a) (leftRotate25_64((a))) +#define rightRotate40_64(a) (leftRotate24_64((a))) +#define rightRotate41_64(a) (leftRotate23_64((a))) +#define rightRotate42_64(a) (leftRotate22_64((a))) +#define rightRotate43_64(a) (leftRotate21_64((a))) +#define rightRotate44_64(a) (leftRotate20_64((a))) +#define rightRotate45_64(a) (leftRotate19_64((a))) +#define rightRotate46_64(a) (leftRotate18_64((a))) +#define rightRotate47_64(a) (leftRotate17_64((a))) +#define rightRotate48_64(a) (leftRotate16_64((a))) +#define rightRotate49_64(a) (leftRotate15_64((a))) +#define rightRotate50_64(a) (leftRotate14_64((a))) +#define rightRotate51_64(a) (leftRotate13_64((a))) +#define rightRotate52_64(a) (leftRotate12_64((a))) +#define rightRotate53_64(a) (leftRotate11_64((a))) +#define rightRotate54_64(a) (leftRotate10_64((a))) +#define rightRotate55_64(a) (leftRotate9_64((a))) +#define rightRotate56_64(a) (leftRotate8_64((a))) +#define rightRotate57_64(a) (leftRotate7_64((a))) +#define rightRotate58_64(a) (leftRotate6_64((a))) +#define rightRotate59_64(a) (leftRotate5_64((a))) +#define rightRotate60_64(a) (leftRotate4_64((a))) +#define rightRotate61_64(a) (leftRotate3_64((a))) +#define rightRotate62_64(a) (leftRotate2_64((a))) +#define rightRotate63_64(a) (leftRotate1_64((a))) + +#else // !CRYPTO_ROTATE64_COMPOSED + +// Rotation macros for 64-bit arguments. + +// Generic left rotate. +#define leftRotate_64(a, bits) \ + (__extension__ ({ \ + uint64_t _temp = (a); \ + (_temp << (bits)) | (_temp >> (64 - (bits))); \ + })) + +// Generic right rotate. +#define rightRotate_64(a, bits) \ + (__extension__ ({ \ + uint64_t _temp = (a); \ + (_temp >> (bits)) | (_temp << (64 - (bits))); \ + })) + +// Left rotate by a specific number of bits. +#define leftRotate1_64(a) (leftRotate_64((a), 1)) +#define leftRotate2_64(a) (leftRotate_64((a), 2)) +#define leftRotate3_64(a) (leftRotate_64((a), 3)) +#define leftRotate4_64(a) (leftRotate_64((a), 4)) +#define leftRotate5_64(a) (leftRotate_64((a), 5)) +#define leftRotate6_64(a) (leftRotate_64((a), 6)) +#define leftRotate7_64(a) (leftRotate_64((a), 7)) +#define leftRotate8_64(a) (leftRotate_64((a), 8)) +#define leftRotate9_64(a) (leftRotate_64((a), 9)) +#define leftRotate10_64(a) (leftRotate_64((a), 10)) +#define leftRotate11_64(a) (leftRotate_64((a), 11)) +#define leftRotate12_64(a) (leftRotate_64((a), 12)) +#define leftRotate13_64(a) (leftRotate_64((a), 13)) +#define leftRotate14_64(a) (leftRotate_64((a), 14)) +#define leftRotate15_64(a) (leftRotate_64((a), 15)) +#define leftRotate16_64(a) (leftRotate_64((a), 16)) +#define leftRotate17_64(a) (leftRotate_64((a), 17)) +#define leftRotate18_64(a) (leftRotate_64((a), 18)) +#define leftRotate19_64(a) (leftRotate_64((a), 19)) +#define leftRotate20_64(a) (leftRotate_64((a), 20)) +#define leftRotate21_64(a) (leftRotate_64((a), 21)) +#define leftRotate22_64(a) (leftRotate_64((a), 22)) +#define leftRotate23_64(a) (leftRotate_64((a), 23)) +#define leftRotate24_64(a) (leftRotate_64((a), 24)) +#define leftRotate25_64(a) (leftRotate_64((a), 25)) +#define leftRotate26_64(a) (leftRotate_64((a), 26)) +#define leftRotate27_64(a) (leftRotate_64((a), 27)) +#define leftRotate28_64(a) (leftRotate_64((a), 28)) +#define leftRotate29_64(a) (leftRotate_64((a), 29)) +#define leftRotate30_64(a) (leftRotate_64((a), 30)) +#define leftRotate31_64(a) (leftRotate_64((a), 31)) +#define leftRotate32_64(a) (leftRotate_64((a), 32)) +#define leftRotate33_64(a) (leftRotate_64((a), 33)) +#define leftRotate34_64(a) (leftRotate_64((a), 34)) +#define leftRotate35_64(a) (leftRotate_64((a), 35)) +#define leftRotate36_64(a) (leftRotate_64((a), 36)) +#define leftRotate37_64(a) (leftRotate_64((a), 37)) +#define leftRotate38_64(a) (leftRotate_64((a), 38)) +#define leftRotate39_64(a) (leftRotate_64((a), 39)) +#define leftRotate40_64(a) (leftRotate_64((a), 40)) +#define leftRotate41_64(a) (leftRotate_64((a), 41)) +#define leftRotate42_64(a) (leftRotate_64((a), 42)) +#define leftRotate43_64(a) (leftRotate_64((a), 43)) +#define leftRotate44_64(a) (leftRotate_64((a), 44)) +#define leftRotate45_64(a) (leftRotate_64((a), 45)) +#define leftRotate46_64(a) (leftRotate_64((a), 46)) +#define leftRotate47_64(a) (leftRotate_64((a), 47)) +#define leftRotate48_64(a) (leftRotate_64((a), 48)) +#define leftRotate49_64(a) (leftRotate_64((a), 49)) +#define leftRotate50_64(a) (leftRotate_64((a), 50)) +#define leftRotate51_64(a) (leftRotate_64((a), 51)) +#define leftRotate52_64(a) (leftRotate_64((a), 52)) +#define leftRotate53_64(a) (leftRotate_64((a), 53)) +#define leftRotate54_64(a) (leftRotate_64((a), 54)) +#define leftRotate55_64(a) (leftRotate_64((a), 55)) +#define leftRotate56_64(a) (leftRotate_64((a), 56)) +#define leftRotate57_64(a) (leftRotate_64((a), 57)) +#define leftRotate58_64(a) (leftRotate_64((a), 58)) +#define leftRotate59_64(a) (leftRotate_64((a), 59)) +#define leftRotate60_64(a) (leftRotate_64((a), 60)) +#define leftRotate61_64(a) (leftRotate_64((a), 61)) +#define leftRotate62_64(a) (leftRotate_64((a), 62)) +#define leftRotate63_64(a) (leftRotate_64((a), 63)) + +// Right rotate by a specific number of bits. +#define rightRotate1_64(a) (rightRotate_64((a), 1)) +#define rightRotate2_64(a) (rightRotate_64((a), 2)) +#define rightRotate3_64(a) (rightRotate_64((a), 3)) +#define rightRotate4_64(a) (rightRotate_64((a), 4)) +#define rightRotate5_64(a) (rightRotate_64((a), 5)) +#define rightRotate6_64(a) (rightRotate_64((a), 6)) +#define rightRotate7_64(a) (rightRotate_64((a), 7)) +#define rightRotate8_64(a) (rightRotate_64((a), 8)) +#define rightRotate9_64(a) (rightRotate_64((a), 9)) +#define rightRotate10_64(a) (rightRotate_64((a), 10)) +#define rightRotate11_64(a) (rightRotate_64((a), 11)) +#define rightRotate12_64(a) (rightRotate_64((a), 12)) +#define rightRotate13_64(a) (rightRotate_64((a), 13)) +#define rightRotate14_64(a) (rightRotate_64((a), 14)) +#define rightRotate15_64(a) (rightRotate_64((a), 15)) +#define rightRotate16_64(a) (rightRotate_64((a), 16)) +#define rightRotate17_64(a) (rightRotate_64((a), 17)) +#define rightRotate18_64(a) (rightRotate_64((a), 18)) +#define rightRotate19_64(a) (rightRotate_64((a), 19)) +#define rightRotate20_64(a) (rightRotate_64((a), 20)) +#define rightRotate21_64(a) (rightRotate_64((a), 21)) +#define rightRotate22_64(a) (rightRotate_64((a), 22)) +#define rightRotate23_64(a) (rightRotate_64((a), 23)) +#define rightRotate24_64(a) (rightRotate_64((a), 24)) +#define rightRotate25_64(a) (rightRotate_64((a), 25)) +#define rightRotate26_64(a) (rightRotate_64((a), 26)) +#define rightRotate27_64(a) (rightRotate_64((a), 27)) +#define rightRotate28_64(a) (rightRotate_64((a), 28)) +#define rightRotate29_64(a) (rightRotate_64((a), 29)) +#define rightRotate30_64(a) (rightRotate_64((a), 30)) +#define rightRotate31_64(a) (rightRotate_64((a), 31)) +#define rightRotate32_64(a) (rightRotate_64((a), 32)) +#define rightRotate33_64(a) (rightRotate_64((a), 33)) +#define rightRotate34_64(a) (rightRotate_64((a), 34)) +#define rightRotate35_64(a) (rightRotate_64((a), 35)) +#define rightRotate36_64(a) (rightRotate_64((a), 36)) +#define rightRotate37_64(a) (rightRotate_64((a), 37)) +#define rightRotate38_64(a) (rightRotate_64((a), 38)) +#define rightRotate39_64(a) (rightRotate_64((a), 39)) +#define rightRotate40_64(a) (rightRotate_64((a), 40)) +#define rightRotate41_64(a) (rightRotate_64((a), 41)) +#define rightRotate42_64(a) (rightRotate_64((a), 42)) +#define rightRotate43_64(a) (rightRotate_64((a), 43)) +#define rightRotate44_64(a) (rightRotate_64((a), 44)) +#define rightRotate45_64(a) (rightRotate_64((a), 45)) +#define rightRotate46_64(a) (rightRotate_64((a), 46)) +#define rightRotate47_64(a) (rightRotate_64((a), 47)) +#define rightRotate48_64(a) (rightRotate_64((a), 48)) +#define rightRotate49_64(a) (rightRotate_64((a), 49)) +#define rightRotate50_64(a) (rightRotate_64((a), 50)) +#define rightRotate51_64(a) (rightRotate_64((a), 51)) +#define rightRotate52_64(a) (rightRotate_64((a), 52)) +#define rightRotate53_64(a) (rightRotate_64((a), 53)) +#define rightRotate54_64(a) (rightRotate_64((a), 54)) +#define rightRotate55_64(a) (rightRotate_64((a), 55)) +#define rightRotate56_64(a) (rightRotate_64((a), 56)) +#define rightRotate57_64(a) (rightRotate_64((a), 57)) +#define rightRotate58_64(a) (rightRotate_64((a), 58)) +#define rightRotate59_64(a) (rightRotate_64((a), 59)) +#define rightRotate60_64(a) (rightRotate_64((a), 60)) +#define rightRotate61_64(a) (rightRotate_64((a), 61)) +#define rightRotate62_64(a) (rightRotate_64((a), 62)) +#define rightRotate63_64(a) (rightRotate_64((a), 63)) + +#endif // !CRYPTO_ROTATE64_COMPOSED + +#endif diff --git a/library.json b/library.json index aa37cb6ed..3b879799d 100644 --- a/library.json +++ b/library.json @@ -5,7 +5,6 @@ "SPI": "*", "Wire": "*", "jgromes/RadioLib": "^7.3.0", - "rweather/Crypto": "^0.4.0", "adafruit/RTClib": "^2.1.3", "melopero/Melopero RV3028": "^1.1.0", "electroniccats/CayenneLPP": "1.6.1" diff --git a/platformio.ini b/platformio.ini index 743e357af..b460e16a5 100644 --- a/platformio.ini +++ b/platformio.ini @@ -19,7 +19,6 @@ lib_deps = SPI Wire jgromes/RadioLib @ ^7.3.0 - rweather/Crypto @ ^0.4.0 adafruit/RTClib @ ^2.1.3 melopero/Melopero RV3028 @ ^1.1.0 electroniccats/CayenneLPP @ 1.6.1 diff --git a/src/Identity.cpp b/src/Identity.cpp index ea546274d..3bcd2fa1c 100644 --- a/src/Identity.cpp +++ b/src/Identity.cpp @@ -2,7 +2,6 @@ #include #define ED25519_NO_SEED 1 #include -#include namespace mesh { @@ -15,12 +14,7 @@ Identity::Identity(const char* pub_hex) { } bool Identity::verify(const uint8_t* sig, const uint8_t* message, int msg_len) const { -#if 0 - // NOTE: memory corruption bug was found in this function!! return ed25519_verify(sig, message, msg_len, pub_key); -#else - return Ed25519::verify(sig, this->pub_key, message, msg_len); -#endif } bool Identity::readFrom(Stream& s) { diff --git a/src/helpers/RegionMap.cpp b/src/helpers/RegionMap.cpp index 2cc47e1d5..330b3cbb9 100644 --- a/src/helpers/RegionMap.cpp +++ b/src/helpers/RegionMap.cpp @@ -1,6 +1,5 @@ #include "RegionMap.h" #include -#include // helper class for region map exporter, we emulate Stream with a safe buffer writer. diff --git a/variants/heltec_mesh_solar/platformio.ini b/variants/heltec_mesh_solar/platformio.ini index 7bfbac85a..ab655fdfa 100644 --- a/variants/heltec_mesh_solar/platformio.ini +++ b/variants/heltec_mesh_solar/platformio.ini @@ -19,7 +19,6 @@ build_src_filter = ${nrf52_base.build_src_filter} +<../variants/heltec_mesh_solar> lib_deps = ${nrf52_base.lib_deps} - rweather/Crypto @ ^0.4.0 stevemarple/MicroNMEA @ ^2.0.6 adafruit/Adafruit NeoPixel@^1.10.0 https://github.com/NMIoT/meshsolar/archive/dfc5330dad443982e6cdd37a61d33fc7252f468b.zip diff --git a/variants/mesh_pocket/platformio.ini b/variants/mesh_pocket/platformio.ini index 015c2ca4b..52151f3ad 100644 --- a/variants/mesh_pocket/platformio.ini +++ b/variants/mesh_pocket/platformio.ini @@ -28,7 +28,6 @@ build_src_filter = ${nrf52_base.build_src_filter} lib_deps = ${nrf52_base.lib_deps} adafruit/Adafruit EPD @ 4.6.1 - rweather/Crypto @ ^0.4.0 stevemarple/MicroNMEA @ ^2.0.6 zinggjm/GxEPD2 @ 1.6.2 bakercp/CRC32 @ ^2.0.0 diff --git a/variants/minewsemi_me25ls01/platformio.ini b/variants/minewsemi_me25ls01/platformio.ini index fd9c3819f..4f454ae7d 100644 --- a/variants/minewsemi_me25ls01/platformio.ini +++ b/variants/minewsemi_me25ls01/platformio.ini @@ -11,7 +11,6 @@ lib_ignore = lib5b4 lib_deps = ${nrf52_base.lib_deps} - rweather/Crypto @ ^0.4.0 [me25ls01] extends = nrf52840_me25ls01 diff --git a/variants/nano_g2_ultra/platformio.ini b/variants/nano_g2_ultra/platformio.ini index 116a1f259..db5bbf2a8 100644 --- a/variants/nano_g2_ultra/platformio.ini +++ b/variants/nano_g2_ultra/platformio.ini @@ -7,7 +7,6 @@ build_flags = ${nrf52_base.build_flags} -I lib/nrf52/s140_nrf52_6.1.1_API/include/nrf52 lib_deps = ${nrf52_base.lib_deps} - rweather/Crypto @ ^0.4.0 lewisxhe/PCF8563_Library@^1.0.1 [Nano_G2_Ultra] From 48019bfb512a0aaceb238a305dbf4534f3f5fcc1 Mon Sep 17 00:00:00 2001 From: ViezeVingertjes Date: Sun, 8 Feb 2026 20:36:59 +0100 Subject: [PATCH 2/2] Add reentrancy guard to ed25519_verify in Identity class --- src/Identity.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Identity.cpp b/src/Identity.cpp index 3bcd2fa1c..3c7d0f7d2 100644 --- a/src/Identity.cpp +++ b/src/Identity.cpp @@ -1,5 +1,6 @@ #include "Identity.h" #include +#include #define ED25519_NO_SEED 1 #include @@ -14,7 +15,14 @@ Identity::Identity(const char* pub_hex) { } bool Identity::verify(const uint8_t* sig, const uint8_t* message, int msg_len) const { - return ed25519_verify(sig, message, msg_len, pub_key); + // ed25519_verify uses static buffers internally (ge.c) and is NOT reentrant. + // This guard catches concurrent calls (e.g. from multiple FreeRTOS tasks). + static volatile bool in_verify = false; + assert(!in_verify && "ed25519_verify is not reentrant - concurrent call detected"); + in_verify = true; + bool result = ed25519_verify(sig, message, msg_len, pub_key); + in_verify = false; + return result; } bool Identity::readFrom(Stream& s) {