From 431f38f34a1bfff77a0698587bea740bf0fbbd38 Mon Sep 17 00:00:00 2001 From: Rhys Weatherley Date: Sat, 3 Jan 2015 07:13:35 +1000 Subject: [PATCH] SHA1 and SHA256 hash algorithms --- libraries/Crypto/AES256.cpp | 2 +- libraries/Crypto/Hash.cpp | 108 ++++++++ libraries/Crypto/Hash.h | 45 ++++ libraries/Crypto/SHA1.cpp | 231 ++++++++++++++++ libraries/Crypto/SHA1.h | 55 ++++ libraries/Crypto/SHA256.cpp | 247 ++++++++++++++++++ libraries/Crypto/SHA256.h | 55 ++++ .../Crypto/examples/TestSHA1/TestSHA1.ino | 155 +++++++++++ .../Crypto/examples/TestSHA256/TestSHA256.ino | 157 +++++++++++ 9 files changed, 1054 insertions(+), 1 deletion(-) create mode 100644 libraries/Crypto/Hash.cpp create mode 100644 libraries/Crypto/Hash.h create mode 100644 libraries/Crypto/SHA1.cpp create mode 100644 libraries/Crypto/SHA1.h create mode 100644 libraries/Crypto/SHA256.cpp create mode 100644 libraries/Crypto/SHA256.h create mode 100644 libraries/Crypto/examples/TestSHA1/TestSHA1.ino create mode 100644 libraries/Crypto/examples/TestSHA256/TestSHA256.ino diff --git a/libraries/Crypto/AES256.cpp b/libraries/Crypto/AES256.cpp index c85ba752..bca97ace 100644 --- a/libraries/Crypto/AES256.cpp +++ b/libraries/Crypto/AES256.cpp @@ -26,7 +26,7 @@ /** * \class AES256 AES.h - * \brief AES block cipher with 56-bit keys. + * \brief AES block cipher with 256-bit keys. * * \sa AES128, AES192 */ diff --git a/libraries/Crypto/Hash.cpp b/libraries/Crypto/Hash.cpp new file mode 100644 index 00000000..dc81ac7a --- /dev/null +++ b/libraries/Crypto/Hash.cpp @@ -0,0 +1,108 @@ +/* + * 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" + +/** + * \class Hash Hash.h + * \brief Abstract base class for cryptographic hash algorithms. + * + * \sa SHA1, SHA256 + */ + +/** + * \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() + */ + +/** + * \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 calling update() will + * reset() the hash and 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, the same hash value is returned again until + * the next call to reset() or update(). + * + * \sa reset(), update() + */ + +/** + * \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() + */ diff --git a/libraries/Crypto/Hash.h b/libraries/Crypto/Hash.h new file mode 100644 index 00000000..2d42f41c --- /dev/null +++ b/libraries/Crypto/Hash.h @@ -0,0 +1,45 @@ +/* + * 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; +}; + +#endif diff --git a/libraries/Crypto/SHA1.cpp b/libraries/Crypto/SHA1.cpp new file mode 100644 index 00000000..df6714c2 --- /dev/null +++ b/libraries/Crypto/SHA1.cpp @@ -0,0 +1,231 @@ +/* + * 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 "SHA1.h" +#include "Crypto.h" +#include "RotateUtil.h" +#include "EndianUtil.h" +#include + +/** + * \class SHA1 SHA1.h + * \brief SHA-1 hash algorithm. + * + * Reference: http://en.wikipedia.org/wiki/SHA-1 + * + * \sa SHA256 + */ + +/** + * \brief Constructs a SHA-1 hash object. + */ +SHA1::SHA1() +{ + reset(); +} + +/** + * \brief Destroys this SHA-1 hash object after clearing sensitive information. + */ +SHA1::~SHA1() +{ + clean(state); +} + +size_t SHA1::hashSize() const +{ + return 20; +} + +size_t SHA1::blockSize() const +{ + return 64; +} + +void SHA1::reset() +{ + state.h[0] = 0x67452301; + state.h[1] = 0xEFCDAB89; + state.h[2] = 0x98BADCFE; + state.h[3] = 0x10325476; + state.h[4] = 0xC3D2E1F0; + state.chunkSize = 0; + state.finalized = false; + state.length = 0; +} + +void SHA1::update(const void *data, size_t len) +{ + // Reset the hashing process if finalize() was called previously. + if (state.finalized) + reset(); + + // 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 SHA1::finalize(void *hash, size_t len) +{ + // Finalize the hash if necessary. + if (!state.finalized) { + // 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 < 5; ++posn) + state.w[posn] = htobe32(state.h[posn]); + state.finalized = true; + } + + // Copy the hash to the caller's return buffer. + if (len > 20) + len = 20; + memcpy(hash, state.w, len); +} + +void SHA1::clear() +{ + clean(state); + reset(); +} + +/** + * \brief Processes a single 512-bit chunk with the core SHA-1 algorithm. + * + * Reference: http://en.wikipedia.org/wiki/SHA-1 + */ +void SHA1::processChunk() +{ + uint8_t index; + + // Convert the first 16 words from big endian to host byte order. + for (index = 0; index < 16; ++index) + state.w[index] = be32toh(state.w[index]); + + // Initialize the hash value for this chunk. + 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]; + + // Perform the first 16 rounds of the compression function main loop. + uint32_t temp; + for (index = 0; index < 16; ++index) { + temp = leftRotate5(a) + ((b & c) | ((~b) & d)) + e + 0x5A827999 + state.w[index]; + e = d; + d = c; + c = leftRotate30(b); + b = a; + a = temp; + } + + // Perform the 64 remaining rounds. We expand the first 16 words to + // 80 in-place in the "w" array. This saves 256 bytes of memory + // that would have otherwise need to be allocated to the "w" array. + for (; index < 20; ++index) { + temp = state.w[index & 0x0F] = leftRotate1 + (state.w[(index - 3) & 0x0F] ^ state.w[(index - 8) & 0x0F] ^ + state.w[(index - 14) & 0x0F] ^ state.w[(index - 16) & 0x0F]); + temp = leftRotate5(a) + ((b & c) | ((~b) & d)) + e + 0x5A827999 + temp; + e = d; + d = c; + c = leftRotate30(b); + b = a; + a = temp; + } + for (; index < 40; ++index) { + temp = state.w[index & 0x0F] = leftRotate1 + (state.w[(index - 3) & 0x0F] ^ state.w[(index - 8) & 0x0F] ^ + state.w[(index - 14) & 0x0F] ^ state.w[(index - 16) & 0x0F]); + temp = leftRotate5(a) + (b ^ c ^ d) + e + 0x6ED9EBA1 + temp; + e = d; + d = c; + c = leftRotate30(b); + b = a; + a = temp; + } + for (; index < 60; ++index) { + temp = state.w[index & 0x0F] = leftRotate1 + (state.w[(index - 3) & 0x0F] ^ state.w[(index - 8) & 0x0F] ^ + state.w[(index - 14) & 0x0F] ^ state.w[(index - 16) & 0x0F]); + temp = leftRotate5(a) + ((b & c) | (b & d) | (c & d)) + e + 0x8F1BBCDC + temp; + e = d; + d = c; + c = leftRotate30(b); + b = a; + a = temp; + } + for (; index < 80; ++index) { + temp = state.w[index & 0x0F] = leftRotate1 + (state.w[(index - 3) & 0x0F] ^ state.w[(index - 8) & 0x0F] ^ + state.w[(index - 14) & 0x0F] ^ state.w[(index - 16) & 0x0F]); + temp = leftRotate5(a) + (b ^ c ^ d) + e + 0xCA62C1D6 + temp; + e = d; + d = c; + c = leftRotate30(b); + b = a; + a = temp; + } + + // Add this chunk's hash to the result so far. + state.h[0] += a; + state.h[1] += b; + state.h[2] += c; + state.h[3] += d; + state.h[4] += e; + + // Attempt to clean up the stack. + a = b = c = d = e = temp = 0; +} diff --git a/libraries/Crypto/SHA1.h b/libraries/Crypto/SHA1.h new file mode 100644 index 00000000..83ed362f --- /dev/null +++ b/libraries/Crypto/SHA1.h @@ -0,0 +1,55 @@ +/* + * 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_SHA1_h +#define CRYPTO_SHA1_h + +#include "Hash.h" + +class SHA1 : public Hash +{ +public: + SHA1(); + virtual ~SHA1(); + + 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(); + +private: + struct { + uint32_t h[5]; + uint32_t w[16]; + uint8_t chunkSize; + bool finalized; + uint64_t length; + } state; + + void processChunk(); +}; + +#endif diff --git a/libraries/Crypto/SHA256.cpp b/libraries/Crypto/SHA256.cpp new file mode 100644 index 00000000..9d3f929b --- /dev/null +++ b/libraries/Crypto/SHA256.cpp @@ -0,0 +1,247 @@ +/* + * 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 "RotateUtil.h" +#include "EndianUtil.h" +#include + +/** + * \class SHA256 SHA256.h + * \brief SHA-256 hash algorithm. + * + * Reference: http://en.wikipedia.org/wiki/SHA-2 + * + * \sa SHA1 + */ + +/** + * \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.finalized = false; + state.length = 0; +} + +void SHA256::update(const void *data, size_t len) +{ + // Reset the hashing process if finalize() was called previously. + if (state.finalized) + reset(); + + // 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) +{ + // Finalize the hash if necessary. + if (!state.finalized) { + // 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]); + state.finalized = true; + } + + // Copy the hash to the caller's return buffer. + if (len > 32) + len = 32; + memcpy(hash, state.w, len); +} + +void SHA256::clear() +{ + clean(state); + reset(); +} + +/** + * \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] = { + 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 + 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 + 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/libraries/Crypto/SHA256.h b/libraries/Crypto/SHA256.h new file mode 100644 index 00000000..f65a08cf --- /dev/null +++ b/libraries/Crypto/SHA256.h @@ -0,0 +1,55 @@ +/* + * 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(); + +private: + struct { + uint32_t h[8]; + uint32_t w[16]; + uint8_t chunkSize; + bool finalized; + uint64_t length; + } state; + + void processChunk(); +}; + +#endif diff --git a/libraries/Crypto/examples/TestSHA1/TestSHA1.ino b/libraries/Crypto/examples/TestSHA1/TestSHA1.ino new file mode 100644 index 00000000..033b2fdd --- /dev/null +++ b/libraries/Crypto/examples/TestSHA1/TestSHA1.ino @@ -0,0 +1,155 @@ +/* + * 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. + */ + +/* +This example runs tests on the SHA1 implementation to verify correct behaviour. +*/ + +#include +#include +#include + +#define HASH_SIZE 20 + +struct TestHashVector +{ + const char *name; + const char *data; + uint8_t hash[HASH_SIZE]; +}; + +static TestHashVector const testVectorSHA1_1 = { + "SHA-1 #1", + "abc", + {0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A, + 0xBA, 0x3E, 0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C, + 0x9C, 0xD0, 0xD8, 0x9D} +}; +static TestHashVector const testVectorSHA1_2 = { + "SHA-1 #2", + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + {0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, + 0xBA, 0xAE, 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5, + 0xE5, 0x46, 0x70, 0xF1} +}; + +SHA1 sha1; + +byte buffer[128]; + +bool testHash_N(Hash *hash, const struct TestHashVector *test, size_t inc) +{ + size_t size = strlen(test->data); + size_t posn, len; + uint8_t value[HASH_SIZE]; + + for (posn = 0; posn < size; posn += inc) { + len = size - posn; + if (len > inc) + len = inc; + hash->update(test->data + posn, len); + } + hash->finalize(value, sizeof(value)); + if (memcmp(value, test->hash, sizeof(value)) != 0) + return false; + + // Try again to make sure the hash resets. + for (posn = 0; posn < size; posn += inc) { + len = size - posn; + if (len > inc) + len = inc; + hash->update(test->data + posn, len); + } + hash->finalize(value, sizeof(value)); + if (memcmp(value, test->hash, sizeof(value)) != 0) + return false; + + return true; +} + +void testHash(Hash *hash, const struct TestHashVector *test) +{ + bool ok; + + Serial.print(test->name); + Serial.print(" ... "); + + ok = testHash_N(hash, test, strlen(test->data)); + ok &= testHash_N(hash, test, 1); + ok &= testHash_N(hash, test, 2); + ok &= testHash_N(hash, test, 5); + ok &= testHash_N(hash, test, 8); + ok &= testHash_N(hash, test, 13); + ok &= testHash_N(hash, test, 16); + ok &= testHash_N(hash, test, 24); + ok &= testHash_N(hash, test, 63); + ok &= testHash_N(hash, test, 64); + + if (ok) + Serial.println("Passed"); + else + Serial.println("Failed"); +} + +void perfHash(Hash *hash) +{ + unsigned long start; + unsigned long elapsed; + int count; + + Serial.print("Hashing ... "); + + for (size_t posn = 0; posn < sizeof(buffer); ++posn) + buffer[posn] = (uint8_t)posn; + + hash->reset(); + start = micros(); + for (count = 0; count < 1000; ++count) { + hash->update(buffer, sizeof(buffer)); + } + elapsed = micros() - start; + + Serial.print(elapsed / (sizeof(buffer) * 1000.0)); + Serial.print("us per byte, "); + Serial.print((sizeof(buffer) * 1000.0 * 1000000.0) / elapsed); + Serial.println(" bytes per second"); +} + +void setup() +{ + Serial.begin(9600); + + Serial.println(); + + Serial.println("Test Vectors:"); + testHash(&sha1, &testVectorSHA1_1); + testHash(&sha1, &testVectorSHA1_2); + + Serial.println(); + + Serial.println("Performance Tests:"); + perfHash(&sha1); +} + +void loop() +{ +} diff --git a/libraries/Crypto/examples/TestSHA256/TestSHA256.ino b/libraries/Crypto/examples/TestSHA256/TestSHA256.ino new file mode 100644 index 00000000..767acd5c --- /dev/null +++ b/libraries/Crypto/examples/TestSHA256/TestSHA256.ino @@ -0,0 +1,157 @@ +/* + * 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. + */ + +/* +This example runs tests on the SHA256 implementation to verify correct behaviour. +*/ + +#include +#include +#include + +#define HASH_SIZE 32 + +struct TestHashVector +{ + const char *name; + const char *data; + uint8_t hash[HASH_SIZE]; +}; + +static TestHashVector const testVectorSHA256_1 = { + "SHA-256 #1", + "abc", + {0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, + 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, + 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, + 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad} +}; +static TestHashVector const testVectorSHA256_2 = { + "SHA-256 #2", + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + {0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, + 0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39, + 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67, + 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1} +}; + +SHA256 sha256; + +byte buffer[128]; + +bool testHash_N(Hash *hash, const struct TestHashVector *test, size_t inc) +{ + size_t size = strlen(test->data); + size_t posn, len; + uint8_t value[HASH_SIZE]; + + for (posn = 0; posn < size; posn += inc) { + len = size - posn; + if (len > inc) + len = inc; + hash->update(test->data + posn, len); + } + hash->finalize(value, sizeof(value)); + if (memcmp(value, test->hash, sizeof(value)) != 0) + return false; + + // Try again to make sure the hash resets. + for (posn = 0; posn < size; posn += inc) { + len = size - posn; + if (len > inc) + len = inc; + hash->update(test->data + posn, len); + } + hash->finalize(value, sizeof(value)); + if (memcmp(value, test->hash, sizeof(value)) != 0) + return false; + + return true; +} + +void testHash(Hash *hash, const struct TestHashVector *test) +{ + bool ok; + + Serial.print(test->name); + Serial.print(" ... "); + + ok = testHash_N(hash, test, strlen(test->data)); + ok &= testHash_N(hash, test, 1); + ok &= testHash_N(hash, test, 2); + ok &= testHash_N(hash, test, 5); + ok &= testHash_N(hash, test, 8); + ok &= testHash_N(hash, test, 13); + ok &= testHash_N(hash, test, 16); + ok &= testHash_N(hash, test, 24); + ok &= testHash_N(hash, test, 63); + ok &= testHash_N(hash, test, 64); + + if (ok) + Serial.println("Passed"); + else + Serial.println("Failed"); +} + +void perfHash(Hash *hash) +{ + unsigned long start; + unsigned long elapsed; + int count; + + Serial.print("Hashing ... "); + + for (size_t posn = 0; posn < sizeof(buffer); ++posn) + buffer[posn] = (uint8_t)posn; + + hash->reset(); + start = micros(); + for (count = 0; count < 500; ++count) { + hash->update(buffer, sizeof(buffer)); + } + elapsed = micros() - start; + + Serial.print(elapsed / (sizeof(buffer) * 500.0)); + Serial.print("us per byte, "); + Serial.print((sizeof(buffer) * 500.0 * 1000000.0) / elapsed); + Serial.println(" bytes per second"); +} + +void setup() +{ + Serial.begin(9600); + + Serial.println(); + + Serial.println("Test Vectors:"); + testHash(&sha256, &testVectorSHA256_1); + testHash(&sha256, &testVectorSHA256_2); + + Serial.println(); + + Serial.println("Performance Tests:"); + perfHash(&sha256); +} + +void loop() +{ +}