diff --git a/examples/AES128_Basics/AES128_Basics.ino b/examples/AES128_Basics/AES128_Basics.ino new file mode 100644 index 00000000..c9e40ab5 --- /dev/null +++ b/examples/AES128_Basics/AES128_Basics.ino @@ -0,0 +1,73 @@ +/* + * 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 explains basic AES128 implementation. +In Arduino serial monitor text appears to be non readable characters, but if you use any other serial terminals you can see the hex values +Example contributor: Aswin +*/ + +#include +#include +#include + +//key[16] cotain 16 byte key(128 bit) for encryption +byte key[16]={0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}; +//plaintext[16] contain the text we need to encrypt +byte plaintext[16]={0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; +//cypher[16] stores the encrypted text +byte cypher[16]; +//decryptedtext[16] stores decrypted text after decryption +byte decryptedtext[16]; +//creating an object of AES128 class +AES128 aes128; + + +void setup() { + Serial.begin(9600); + aes128.setKey(key,16);// Setting Key for AES + + Serial.print("Before Encryption:"); + for(int i=0; ioutput block and plaintext->input block + Serial.println(); + Serial.print("After Encryption:"); + for(int j=0;j +#include +#include +#include + +typedef struct +{ + const char *name; + const unsigned char *key; + size_t key_len; + const unsigned char *salt; + size_t salt_len; + const unsigned char *info; + size_t info_len; + const unsigned char *out; + size_t out_len; + +} TestHKDFVector; + +/* Test cases from RFC-5869 */ +static unsigned char const key_1[] = { + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b +}; +static unsigned char const salt_1[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c +}; +static unsigned char const info_1[] = { + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9 +}; +static unsigned char const out_1[] = { + 0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, + 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a, + 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, + 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf, + 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, + 0x58, 0x65 +}; +static TestHKDFVector const testVectorHKDF_1 = { + "Test Vector 1", + key_1, + 22, + salt_1, + 13, + info_1, + 10, + out_1, + 42 +}; + +HKDF hkdf_context; + +uint8_t buffer[128]; + +bool testHKDF_N(HKDFCommon *hkdf, const TestHKDFVector *test, size_t inc) +{ + size_t size = test->out_len; + size_t posn, len; + + hkdf->setKey(test->key, test->key_len, test->salt, test->salt_len); + for (posn = 0; posn < size; posn += inc) { + len = size - posn; + if (len > inc) + len = inc; + hkdf->extract(buffer + posn, len, test->info, test->info_len); + } + if (memcmp(buffer, test->out, test->out_len) != 0) + return false; + + return true; +} + +void testHKDF(HKDFCommon *hkdf, const TestHKDFVector *test) +{ + bool ok; + + Serial.print(test->name); + Serial.print(" ... "); + + ok = testHKDF_N(hkdf, test, test->out_len); + ok &= testHKDF_N(hkdf, test, 1); + ok &= testHKDF_N(hkdf, test, 2); + ok &= testHKDF_N(hkdf, test, 5); + ok &= testHKDF_N(hkdf, test, 8); + ok &= testHKDF_N(hkdf, test, 13); + ok &= testHKDF_N(hkdf, test, 16); + ok &= testHKDF_N(hkdf, test, 24); + ok &= testHKDF_N(hkdf, test, 63); + ok &= testHKDF_N(hkdf, test, 64); + + if (ok) + Serial.println("Passed"); + else + Serial.println("Failed"); +} + +void setup() +{ + Serial.begin(9600); + + Serial.println(); + + Serial.print("State Size ... "); + Serial.println(sizeof(HKDF)); + Serial.print("Size Without Hash State ... "); + Serial.println(sizeof(HKDF) - sizeof(SHA256)); + Serial.println(); + + Serial.println("Test Vectors:"); + testHKDF(&hkdf_context, &testVectorHKDF_1); + Serial.println(); +} + +void loop() +{ +} diff --git a/examples/TestSHA224/TestSHA224.ino b/examples/TestSHA224/TestSHA224.ino new file mode 100644 index 00000000..c148becc --- /dev/null +++ b/examples/TestSHA224/TestSHA224.ino @@ -0,0 +1,323 @@ +/* + * 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 SHA224 implementation to verify correct behaviour. +*/ + +#include +#include +#include + +#define HASH_SIZE 28 +#define BLOCK_SIZE 64 + +struct TestHashVector +{ + const char *name; + const char *key; + const char *data; + uint8_t hash[HASH_SIZE]; +}; + +static TestHashVector const testVectorSHA224_1 = { + "SHA-224 #1", + 0, + "abc", + {0x23, 0x09, 0x7d, 0x22, 0x34, 0x05, 0xd8, 0x22, + 0x86, 0x42, 0xa4, 0x77, 0xbd, 0xa2, 0x55, 0xb3, + 0x2a, 0xad, 0xbc, 0xe4, 0xbd, 0xa0, 0xb3, 0xf7, + 0xe3, 0x6c, 0x9d, 0xa7} +}; +static TestHashVector const testVectorSHA224_2 = { + "SHA-224 #2", + 0, + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + {0x75, 0x38, 0x8b, 0x16, 0x51, 0x27, 0x76, 0xcc, + 0x5d, 0xba, 0x5d, 0xa1, 0xfd, 0x89, 0x01, 0x50, + 0xb0, 0xc6, 0x45, 0x5c, 0xb4, 0xf5, 0x8b, 0x19, + 0x52, 0x52, 0x25, 0x25} +}; +static TestHashVector const testVectorHMAC_SHA224_1 = { + "HMAC-SHA-224 #1", + "", + "", + {0x5c, 0xe1, 0x4f, 0x72, 0x89, 0x46, 0x62, 0x21, + 0x3e, 0x27, 0x48, 0xd2, 0xa6, 0xba, 0x23, 0x4b, + 0x74, 0x26, 0x39, 0x10, 0xce, 0xdd, 0xe2, 0xf5, + 0xa9, 0x27, 0x15, 0x24} +}; +static TestHashVector const testVectorHMAC_SHA224_2 = { + "HMAC-SHA-224 #2", + "key", + "The quick brown fox jumps over the lazy dog", + {0x88, 0xff, 0x8b, 0x54, 0x67, 0x5d, 0x39, 0xb8, + 0xf7, 0x23, 0x22, 0xe6, 0x5f, 0xf9, 0x45, 0xc5, + 0x2d, 0x96, 0x37, 0x99, 0x88, 0xad, 0xa2, 0x56, + 0x39, 0x74, 0x7e, 0x69} +}; + +SHA224 sha224; + +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]; + + hash->reset(); + 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"); +} + +// Very simple method for hashing a HMAC inner or outer key. +void hashKey(Hash *hash, const uint8_t *key, size_t keyLen, uint8_t pad) +{ + size_t posn; + uint8_t buf; + uint8_t result[HASH_SIZE]; + if (keyLen <= BLOCK_SIZE) { + hash->reset(); + for (posn = 0; posn < BLOCK_SIZE; ++posn) { + if (posn < keyLen) + buf = key[posn] ^ pad; + else + buf = pad; + hash->update(&buf, 1); + } + } else { + hash->reset(); + hash->update(key, keyLen); + hash->finalize(result, HASH_SIZE); + hash->reset(); + for (posn = 0; posn < BLOCK_SIZE; ++posn) { + if (posn < HASH_SIZE) + buf = result[posn] ^ pad; + else + buf = pad; + hash->update(&buf, 1); + } + } +} + +void testHMAC(Hash *hash, size_t keyLen) +{ + uint8_t result[HASH_SIZE]; + + Serial.print("HMAC-SHA-224 keysize="); + Serial.print(keyLen); + Serial.print(" ... "); + + // Construct the expected result with a simple HMAC implementation. + memset(buffer, (uint8_t)keyLen, keyLen); + hashKey(hash, buffer, keyLen, 0x36); + memset(buffer, 0xBA, sizeof(buffer)); + hash->update(buffer, sizeof(buffer)); + hash->finalize(result, HASH_SIZE); + memset(buffer, (uint8_t)keyLen, keyLen); + hashKey(hash, buffer, keyLen, 0x5C); + hash->update(result, HASH_SIZE); + hash->finalize(result, HASH_SIZE); + + // Now use the library to compute the HMAC. + hash->resetHMAC(buffer, keyLen); + memset(buffer, 0xBA, sizeof(buffer)); + hash->update(buffer, sizeof(buffer)); + memset(buffer, (uint8_t)keyLen, keyLen); + hash->finalizeHMAC(buffer, keyLen, buffer, HASH_SIZE); + + // Check the result. + if (!memcmp(result, buffer, HASH_SIZE)) + Serial.println("Passed"); + else + Serial.println("Failed"); +} + +void testHMAC(Hash *hash, const struct TestHashVector *test) +{ + uint8_t result[HASH_SIZE]; + + Serial.print(test->name); + Serial.print(" ... "); + + hash->resetHMAC(test->key, strlen(test->key)); + hash->update(test->data, strlen(test->data)); + hash->finalizeHMAC(test->key, strlen(test->key), result, sizeof(result)); + + if (!memcmp(result, test->hash, HASH_SIZE)) + 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 perfFinalize(Hash *hash) +{ + unsigned long start; + unsigned long elapsed; + int count; + + Serial.print("Finalizing ... "); + + hash->reset(); + hash->update("abc", 3); + start = micros(); + for (count = 0; count < 1000; ++count) { + hash->finalize(buffer, hash->hashSize()); + } + elapsed = micros() - start; + + Serial.print(elapsed / 1000.0); + Serial.print("us per op, "); + Serial.print((1000.0 * 1000000.0) / elapsed); + Serial.println(" ops per second"); +} + +void perfHMAC(Hash *hash) +{ + unsigned long start; + unsigned long elapsed; + int count; + + Serial.print("HMAC Reset ... "); + + for (size_t posn = 0; posn < sizeof(buffer); ++posn) + buffer[posn] = (uint8_t)posn; + + start = micros(); + for (count = 0; count < 1000; ++count) { + hash->resetHMAC(buffer, hash->hashSize()); + } + elapsed = micros() - start; + + Serial.print(elapsed / 1000.0); + Serial.print("us per op, "); + Serial.print((1000.0 * 1000000.0) / elapsed); + Serial.println(" ops per second"); + + Serial.print("HMAC Finalize ... "); + + hash->resetHMAC(buffer, hash->hashSize()); + hash->update("abc", 3); + start = micros(); + for (count = 0; count < 1000; ++count) { + hash->finalizeHMAC(buffer, hash->hashSize(), buffer, hash->hashSize()); + } + elapsed = micros() - start; + + Serial.print(elapsed / 1000.0); + Serial.print("us per op, "); + Serial.print((1000.0 * 1000000.0) / elapsed); + Serial.println(" ops per second"); +} + +void setup() +{ + Serial.begin(9600); + + Serial.println(); + + Serial.print("State Size ..."); + Serial.println(sizeof(SHA224)); + Serial.println(); + + Serial.println("Test Vectors:"); + testHash(&sha224, &testVectorSHA224_1); + testHash(&sha224, &testVectorSHA224_2); + testHMAC(&sha224, &testVectorHMAC_SHA224_1); + testHMAC(&sha224, &testVectorHMAC_SHA224_2); + testHMAC(&sha224, (size_t)0); + testHMAC(&sha224, 1); + testHMAC(&sha224, HASH_SIZE); + testHMAC(&sha224, BLOCK_SIZE); + testHMAC(&sha224, BLOCK_SIZE + 1); + testHMAC(&sha224, sizeof(buffer)); + + Serial.println(); + + Serial.println("Performance Tests:"); + perfHash(&sha224); + perfFinalize(&sha224); + perfHMAC(&sha224); +} + +void loop() +{ +} diff --git a/examples/TestSHA256/TestSHA256.ino b/examples/TestSHA256/TestSHA256.ino index 87f78cd7..7a11a420 100644 --- a/examples/TestSHA256/TestSHA256.ino +++ b/examples/TestSHA256/TestSHA256.ino @@ -198,6 +198,13 @@ void testHMAC(Hash *hash, const struct TestHashVector *test) hash->update(test->data, strlen(test->data)); hash->finalizeHMAC(test->key, strlen(test->key), result, sizeof(result)); + // If the first test passed, then try the all-in-one function too. + if (!memcmp(result, test->hash, HASH_SIZE)) { + memset(result, 0xAA, sizeof(result)); + hmac(result, HASH_SIZE, test->key, strlen(test->key), + test->data, strlen(test->data)); + } + if (!memcmp(result, test->hash, HASH_SIZE)) Serial.println("Passed"); else diff --git a/examples/TestSHA384/TestSHA384.ino b/examples/TestSHA384/TestSHA384.ino new file mode 100644 index 00000000..dff79d8d --- /dev/null +++ b/examples/TestSHA384/TestSHA384.ino @@ -0,0 +1,260 @@ +/* + * 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 SHA384 implementation to verify correct behaviour. +*/ + +#include +#include +#include + +#define HASH_SIZE 48 +#define BLOCK_SIZE 128 + +struct TestHashVector +{ + const char *name; + const char *data; + uint8_t hash[HASH_SIZE]; +}; + +static TestHashVector const testVectorSHA384_1 = { + "SHA-384 #1", + "", + {0x38, 0xb0, 0x60, 0xa7, 0x51, 0xac, 0x96, 0x38, + 0x4c, 0xd9, 0x32, 0x7e, 0xb1, 0xb1, 0xe3, 0x6a, + 0x21, 0xfd, 0xb7, 0x11, 0x14, 0xbe, 0x07, 0x43, + 0x4c, 0x0c, 0xc7, 0xbf, 0x63, 0xf6, 0xe1, 0xda, + 0x27, 0x4e, 0xde, 0xbf, 0xe7, 0x6f, 0x65, 0xfb, + 0xd5, 0x1a, 0xd2, 0xf1, 0x48, 0x98, 0xb9, 0x5b} +}; +static TestHashVector const testVectorSHA384_2 = { + "SHA-384 #2", + "abc", + {0xcb, 0x00, 0x75, 0x3f, 0x45, 0xa3, 0x5e, 0x8b, + 0xb5, 0xa0, 0x3d, 0x69, 0x9a, 0xc6, 0x50, 0x07, + 0x27, 0x2c, 0x32, 0xab, 0x0e, 0xde, 0xd1, 0x63, + 0x1a, 0x8b, 0x60, 0x5a, 0x43, 0xff, 0x5b, 0xed, + 0x80, 0x86, 0x07, 0x2b, 0xa1, 0xe7, 0xcc, 0x23, + 0x58, 0xba, 0xec, 0xa1, 0x34, 0xc8, 0x25, 0xa7} +}; +static TestHashVector const testVectorSHA384_3 = { + "SHA-384 #3", + "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" + "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + {0x09, 0x33, 0x0c, 0x33, 0xf7, 0x11, 0x47, 0xe8, + 0x3d, 0x19, 0x2f, 0xc7, 0x82, 0xcd, 0x1b, 0x47, + 0x53, 0x11, 0x1b, 0x17, 0x3b, 0x3b, 0x05, 0xd2, + 0x2f, 0xa0, 0x80, 0x86, 0xe3, 0xb0, 0xf7, 0x12, + 0xfc, 0xc7, 0xc7, 0x1a, 0x55, 0x7e, 0x2d, 0xb9, + 0x66, 0xc3, 0xe9, 0xfa, 0x91, 0x74, 0x60, 0x39} +}; + +SHA384 sha384; + +byte buffer[BLOCK_SIZE + 2]; + +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]; + + hash->reset(); + 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 < 250; ++count) { + hash->update(buffer, sizeof(buffer)); + } + elapsed = micros() - start; + + Serial.print(elapsed / (sizeof(buffer) * 250.0)); + Serial.print("us per byte, "); + Serial.print((sizeof(buffer) * 250.0 * 1000000.0) / elapsed); + Serial.println(" bytes per second"); +} + +// Very simple method for hashing a HMAC inner or outer key. +void hashKey(Hash *hash, const uint8_t *key, size_t keyLen, uint8_t pad) +{ + size_t posn; + uint8_t buf; + uint8_t result[HASH_SIZE]; + if (keyLen <= BLOCK_SIZE) { + hash->reset(); + for (posn = 0; posn < BLOCK_SIZE; ++posn) { + if (posn < keyLen) + buf = key[posn] ^ pad; + else + buf = pad; + hash->update(&buf, 1); + } + } else { + hash->reset(); + hash->update(key, keyLen); + hash->finalize(result, HASH_SIZE); + hash->reset(); + for (posn = 0; posn < BLOCK_SIZE; ++posn) { + if (posn < HASH_SIZE) + buf = result[posn] ^ pad; + else + buf = pad; + hash->update(&buf, 1); + } + } +} + +void testHMAC(Hash *hash, size_t keyLen) +{ + uint8_t result[HASH_SIZE]; + + Serial.print("HMAC-SHA-384 keysize="); + Serial.print(keyLen); + Serial.print(" ... "); + + // Construct the expected result with a simple HMAC implementation. + memset(buffer, (uint8_t)keyLen, keyLen); + hashKey(hash, buffer, keyLen, 0x36); + memset(buffer, 0xBA, sizeof(buffer)); + hash->update(buffer, sizeof(buffer)); + hash->finalize(result, HASH_SIZE); + memset(buffer, (uint8_t)keyLen, keyLen); + hashKey(hash, buffer, keyLen, 0x5C); + hash->update(result, HASH_SIZE); + hash->finalize(result, HASH_SIZE); + + // Now use the library to compute the HMAC. + hash->resetHMAC(buffer, keyLen); + memset(buffer, 0xBA, sizeof(buffer)); + hash->update(buffer, sizeof(buffer)); + memset(buffer, (uint8_t)keyLen, keyLen); + hash->finalizeHMAC(buffer, keyLen, buffer, HASH_SIZE); + + // Check the result. + if (!memcmp(result, buffer, HASH_SIZE)) + Serial.println("Passed"); + else + Serial.println("Failed"); +} + +void perfFinalize(Hash *hash) +{ + unsigned long start; + unsigned long elapsed; + int count; + + Serial.print("Finalizing ... "); + + hash->reset(); + hash->update("abc", 3); + start = micros(); + for (count = 0; count < 1000; ++count) { + hash->finalize(buffer, hash->hashSize()); + } + elapsed = micros() - start; + + Serial.print(elapsed / 1000.0); + Serial.print("us per op, "); + Serial.print((1000.0 * 1000000.0) / elapsed); + Serial.println(" ops per second"); +} + +void setup() +{ + Serial.begin(9600); + + Serial.println(); + + Serial.print("State Size ..."); + Serial.println(sizeof(SHA384)); + Serial.println(); + + Serial.println("Test Vectors:"); + testHash(&sha384, &testVectorSHA384_1); + testHash(&sha384, &testVectorSHA384_2); + testHash(&sha384, &testVectorSHA384_3); + testHMAC(&sha384, (size_t)0); + testHMAC(&sha384, 1); + testHMAC(&sha384, HASH_SIZE); + testHMAC(&sha384, BLOCK_SIZE); + testHMAC(&sha384, BLOCK_SIZE + 1); + testHMAC(&sha384, BLOCK_SIZE + 2); + + Serial.println(); + + Serial.println("Performance Tests:"); + perfHash(&sha384); + perfFinalize(&sha384); +} + +void loop() +{ +} diff --git a/keywords.txt b/keywords.txt index 068542fc..45dfc210 100644 --- a/keywords.txt +++ b/keywords.txt @@ -10,7 +10,9 @@ ChaChaPoly KEYWORD1 BLAKE2b KEYWORD1 BLAKE2s KEYWORD1 +SHA224 KEYWORD1 SHA256 KEYWORD1 +SHA384 KEYWORD1 SHA512 KEYWORD1 SHA3_256 KEYWORD1 SHA3_512 KEYWORD1 @@ -30,6 +32,7 @@ CBC KEYWORD1 CFB KEYWORD1 CTR KEYWORD1 OFB KEYWORD1 +HKDF KEYWORD1 GCM KEYWORD1 EAX KEYWORD1 @@ -44,6 +47,7 @@ encrypt KEYWORD2 decrypt KEYWORD2 clear KEYWORD2 addAuthData KEYWORD2 +extract KEYWORD2 hashSize KEYWORD2 blockSize KEYWORD2 diff --git a/library.json b/library.json index 9c85f880..83057c0c 100644 --- a/library.json +++ b/library.json @@ -1,18 +1,21 @@ { "name": "Crypto", - "version": "0.2.0", - "keywords": "AES128,AES192,AES256,Speck,CTR,CFB,CBC,OFB,EAX,GCM,XTS,ChaCha,ChaChaPoly,EAX,GCM,SHA256,SHA512,SHA3_256,SHA3_512,BLAKE2s,BLAKE2b,SHAKE128,SHAKE256,Poly1305,GHASH,OMAC,Curve25519,Ed25519,P521,RNG,NOISE", - "description": "Rhys Weatherley's arduinolibs Crypto library - All cryptographic algorithms have been optimized for 8-bit Arduino platforms like the Uno", + "version": "0.4.0", + "keywords": "AES128,AES192,AES256,Speck,CTR,CFB,CBC,OFB,EAX,GCM,HKDF,XTS,ChaCha,ChaChaPoly,EAX,GCM,SHA224,SHA256,SHA384,SHA512,SHA3-256,SHA3-512,BLAKE2s,BLAKE2b,SHAKE128,SHAKE256,Poly1305,GHASH,OMAC,Curve25519,Ed25519,P521,RNG,NOISE", + "description": "Arduino CryptoLibs - All cryptographic algorithms have been optimized for 8-bit Arduino platforms like the Uno", "authors": { "name": "Rhys Weatherley", "email": "rhys.weatherley@gmail.com", "url": "https://rweather.github.io/arduinolibs/crypto.html" }, + "export": { + "include": "libraries/Crypto" + }, "repository": { "type": "git", - "url": "https://github.com/OperatorFoundation/Crypto.git" + "url": "https://github.com/rweather/arduinolibs.git" }, "frameworks": "arduino", "platforms": "*" diff --git a/library.properties b/library.properties index 2f2201f0..e4887a90 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Crypto -version=0.2.0 +version=0.4.0 author=Rhys Weatherley rhys.weatherley@gmail.com maintainer=Dr. Brandon Wiley brandon@operatorfoundation.org sentence=Rhys Weatherley's arduinolibs Crypto library. diff --git a/src/AES.h b/src/AES.h index 63adc6df..16c2ce0f 100644 --- a/src/AES.h +++ b/src/AES.h @@ -190,17 +190,30 @@ class AESSmall128 : public AESTiny128 #if defined(CRYPTO_AES_ESP32) -// "hwcrypto/aes.h" includes "rom/aes.h" which defines global enums for -// AES128, AES192, and AES256. The enum definitions interfere with the -// definition of the same-named classes below. The #define's and #undef's -// here work around the problem by defining the enums to different names. -#define AES128 AES128_enum -#define AES192 AES192_enum -#define AES256 AES256_enum -#include "hwcrypto/aes.h" +/** @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 { @@ -221,7 +234,7 @@ class AESCommon : public BlockCipher AESCommon(uint8_t keySize); private: - esp_aes_context ctx; + uint8_t ctx[CRYPTO_ESP32_CONTEXT_SIZE]; }; class AES128 : public AESCommon diff --git a/src/AESEsp32.cpp b/src/AESEsp32.cpp index 72a86469..d37761f5 100644 --- a/src/AESEsp32.cpp +++ b/src/AESEsp32.cpp @@ -28,14 +28,24 @@ #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) { - ctx.key_bytes = keySize; + memset(ctx, 0, sizeof(ctx)); + ctx[0] = keySize; } AESCommon::~AESCommon() { - clean(ctx.key, sizeof(ctx.key)); + clean(ctx, sizeof(ctx)); } size_t AESCommon::blockSize() const @@ -45,14 +55,13 @@ size_t AESCommon::blockSize() const size_t AESCommon::keySize() const { - return ctx.key_bytes; + return ctx[0]; } bool AESCommon::setKey(const uint8_t *key, size_t len) { - if (len == ctx.key_bytes) { - // Do the effect of esp_aes_setkey() which is just a memcpy(). - memcpy(ctx.key, key, len); + if (len == ctx[0]) { + esp_aes_setkey(ctx, key, len * 8); return true; } return false; @@ -60,17 +69,19 @@ bool AESCommon::setKey(const uint8_t *key, size_t len) void AESCommon::encryptBlock(uint8_t *output, const uint8_t *input) { - esp_aes_encrypt(&ctx, input, output); + esp_aes_crypt_ecb(ctx, 1, input, output); } void AESCommon::decryptBlock(uint8_t *output, const uint8_t *input) { - esp_aes_decrypt(&ctx, input, output); + esp_aes_crypt_ecb(ctx, 0, input, output); } void AESCommon::clear() { - clean(ctx.key, sizeof(ctx.key)); + uint8_t keySize = ctx[0]; + clean(ctx, sizeof(ctx)); + ctx[0] = keySize; } AES128::~AES128() diff --git a/src/BLAKE2b.cpp b/src/BLAKE2b.cpp index 7352ec06..5150e3fb 100644 --- a/src/BLAKE2b.cpp +++ b/src/BLAKE2b.cpp @@ -69,6 +69,16 @@ * \sa BLAKE2s, SHA512, SHA3_512 */ +/** + * \var BLAKE2b::HASH_SIZE + * \brief Constant for the size of the hash output of BLAKE2b. + */ + +/** + * \var BLAKE2b::BLOCK_SIZE + * \brief Constant for the block size of BLAKE2b. + */ + /** * \brief Constructs a BLAKE2b hash object. */ @@ -240,7 +250,7 @@ void BLAKE2b::resetHMAC(const void *key, size_t keyLen) { formatHMACKey(state.m, key, keyLen, 0x36); state.lengthLow += 128; - processChunk(0); + state.chunkSize = 128; } void BLAKE2b::finalizeHMAC(const void *key, size_t keyLen, void *hash, size_t hashLen) @@ -249,7 +259,7 @@ void BLAKE2b::finalizeHMAC(const void *key, size_t keyLen, void *hash, size_t ha finalize(temp, sizeof(temp)); formatHMACKey(state.m, key, keyLen, 0x5C); state.lengthLow += 128; - processChunk(0); + state.chunkSize = 128; update(temp, sizeof(temp)); finalize(hash, hashLen); clean(temp); diff --git a/src/BLAKE2b.h b/src/BLAKE2b.h index e3f78684..b2333ec7 100644 --- a/src/BLAKE2b.h +++ b/src/BLAKE2b.h @@ -46,6 +46,9 @@ class BLAKE2b : public Hash 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 = 64; + static const size_t BLOCK_SIZE = 128; + private: struct { uint64_t h[8]; diff --git a/src/BLAKE2s.cpp b/src/BLAKE2s.cpp index a707ff7e..d597f89e 100644 --- a/src/BLAKE2s.cpp +++ b/src/BLAKE2s.cpp @@ -69,6 +69,16 @@ * \sa BLAKE2b, SHA256, SHA3_256 */ +/** + * \var BLAKE2s::HASH_SIZE + * \brief Constant for the size of the hash output of BLAKE2s. + */ + +/** + * \var BLAKE2s::BLOCK_SIZE + * \brief Constant for the block size of BLAKE2s. + */ + /** * \brief Constructs a BLAKE2s hash object. */ @@ -234,7 +244,7 @@ void BLAKE2s::resetHMAC(const void *key, size_t keyLen) { formatHMACKey(state.m, key, keyLen, 0x36); state.length += 64; - processChunk(0); + state.chunkSize = 64; } void BLAKE2s::finalizeHMAC(const void *key, size_t keyLen, void *hash, size_t hashLen) @@ -243,7 +253,7 @@ void BLAKE2s::finalizeHMAC(const void *key, size_t keyLen, void *hash, size_t ha finalize(temp, sizeof(temp)); formatHMACKey(state.m, key, keyLen, 0x5C); state.length += 64; - processChunk(0); + state.chunkSize = 64; update(temp, sizeof(temp)); finalize(hash, hashLen); clean(temp); diff --git a/src/BLAKE2s.h b/src/BLAKE2s.h index 3ebae2e6..d14f5e42 100644 --- a/src/BLAKE2s.h +++ b/src/BLAKE2s.h @@ -46,6 +46,9 @@ class BLAKE2s : public Hash 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; + private: struct { uint32_t h[8]; diff --git a/src/GCM.cpp b/src/GCM.cpp index 3f9a45c0..0e247709 100644 --- a/src/GCM.cpp +++ b/src/GCM.cpp @@ -302,7 +302,7 @@ void GCMCommon::clear() * gcm.setKey(key, sizeof(key)); * gcm.setIV(iv, sizeof(iv)); * gcm.addAuthData(adata, sizeof(adata)); - * gcm.decrypt(ciphertext, plaintext, sizeof(plaintext)); + * gcm.decrypt(plaintext, ciphertext, sizeof(ciphertext)); * if (!gcm.checkTag(tag, sizeof(tag))) { * // The data was invalid - do not use it. * ... diff --git a/src/HKDF.cpp b/src/HKDF.cpp new file mode 100644 index 00000000..43053e00 --- /dev/null +++ b/src/HKDF.cpp @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2022 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 "HKDF.h" +#include + +/** + * \class HKDFCommon HKDF.h + * \brief Concrete base class to assist with implementing HKDF mode for + * hash algorithms. + * + * Reference: https://datatracker.ietf.org/doc/html/rfc5869 + * + * \sa HKDF + */ + +/** + * \brief Constructs a new HKDF instance. + * + * This constructor must be followed by a call to setHashAlgorithm(). + */ +HKDFCommon::HKDFCommon() + : hash(0) + , buf(0) + , counter(1) + , posn(255) +{ +} + +/** + * \brief Destroys this HKDF instance. + */ +HKDFCommon::~HKDFCommon() +{ +} + +/** + * \brief Sets the key and salt for a HKDF session. + * + * \param key Points to the key. + * \param keyLen Length of the \a key in bytes. + * \param salt Points to the salt. + * \param saltLen Length of the \a salt in bytes. + */ +void HKDFCommon::setKey(const void *key, size_t keyLen, const void *salt, size_t saltLen) +{ + // Initialise the HKDF context with the key and salt to generate the PRK. + size_t hashSize = hash->hashSize(); + if (salt && saltLen) { + hash->resetHMAC(salt, saltLen); + hash->update(key, keyLen); + hash->finalizeHMAC(salt, saltLen, buf + hashSize, hashSize); + } else { + // If no salt is provided, RFC 5869 says that a string of + // hashSize zeroes should be used instead. + memset(buf, 0, hashSize); + hash->resetHMAC(buf, hashSize); + hash->update(key, keyLen); + hash->finalizeHMAC(buf, hashSize, buf + hashSize, hashSize); + } + counter = 1; + posn = hashSize; +} + +/** + * \brief Extracts data from a HKDF session. + * + * \param out Points to the buffer to fill with extracted data. + * \param outLen Number of bytes to extract into the \a out buffer. + * \param info Points to the application-specific information string. + * \param infoLen Length of the \a info string in bytes. + * + * \note RFC 5869 specifies that a maximum of 255 * HashLen bytes + * should be extracted from a HKDF session. This maximum is not + * enforced by this function. + */ +void HKDFCommon::extract(void *out, size_t outLen, const void *info, size_t infoLen) +{ + size_t hashSize = hash->hashSize(); + uint8_t *outPtr = (uint8_t *)out; + while (outLen > 0) { + // Generate a new output block if necessary. + if (posn >= hashSize) { + hash->resetHMAC(buf + hashSize, hashSize); + if (counter != 1) + hash->update(buf, hashSize); + if (info && infoLen) + hash->update(info, infoLen); + hash->update(&counter, 1); + hash->finalizeHMAC(buf + hashSize, hashSize, buf, hashSize); + ++counter; + posn = 0; + } + + // Copy as much output data as we can for this block. + size_t len = hashSize - posn; + if (len > outLen) + len = outLen; + memcpy(outPtr, buf + posn, len); + posn += len; + outPtr += len; + outLen -= len; + } +} + +/** + * \brief Clears sensitive information from this HKDF instance. + */ +void HKDFCommon::clear() +{ + size_t hashSize = hash->hashSize(); + hash->clear(); + clean(buf, hashSize * 2); + counter = 1; + posn = hashSize; +} + +/** + * \fn void HKDFCommon::setHashAlgorithm(Hash *hashAlg, uint8_t *buffer) + * \brief Sets the hash algorithm to use for HKDF operations. + * + * \param hashAlg Points to the hash algorithm instance to use. + * \param buffer Points to a buffer that must be at least twice the + * size of the hash output from \a hashAlg. + */ + +/** + * \class HKDF HKDF.h + * \brief Implementation of the HKDF mode for hash algorithms. + * + * HKDF expands a key to a larger amount of material that can be used + * for cryptographic operations. It is based around a hash algorithm. + * + * The template parameter T must be a concrete subclass of Hash + * indicating the specific hash algorithm to use. + * + * The following example expands a 32-byte / 256-bit key into + * 128 bytes / 1024 bits of key material for use in a cryptographic session: + * + * \code + * uint8_t key[32] = {...}; + * uint8_t output[128]; + * HKDF hkdf; + * hkdf.setKey(key, sizeof(key)); + * hkdf.extract(output, sizeof(output)); + * \endcode + * + * Usually the key will be salted, which can be passed to the setKey() + * function: + * + * \code + * hkdf.setKey(key, sizeof(key), salt, sizeof(salt)); + * \endcode + * + * It is also possible to acheive the same effect with a single function call + * using the hkdf() templated function: + * + * \code + * hkdf(output, sizeof(output), key, sizeof(key), + * salt, sizeof(salt), info, sizeof(info)); + * \endcode + * + * Reference: https://datatracker.ietf.org/doc/html/rfc5869 + */ + +/** + * \fn HKDF::HKDF() + * \brief Constructs a new HKDF object for the hash algorithm T. + */ + +/** + * \fn HKDF::~HKDF() + * \brief Destroys a HKDF instance and all sensitive data within it. + */ + +/** + * \fn void hkdf(void *out, size_t outLen, const void *key, size_t keyLen, const void *salt, size_t saltLen, const void *info, size_t infoLen) + * \brief All-in-one implementation of HKDF using a hash algorithm. + * + * \param out Points to the buffer to fill with extracted data. + * \param outLen Number of bytes to extract into the \a out buffer. + * \param key Points to the key. + * \param keyLen Length of the \a key in bytes. + * \param salt Points to the salt. + * \param saltLen Length of the \a salt in bytes. + * \param info Points to the application-specific information string. + * \param infoLen Length of the \a info string in bytes. + * + * The template parameter T must be a subclass of Hash. + */ diff --git a/src/HKDF.h b/src/HKDF.h new file mode 100644 index 00000000..0e9ee7bd --- /dev/null +++ b/src/HKDF.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2022 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_HKDF_h +#define CRYPTO_HKDF_h + +#include "Hash.h" +#include "Crypto.h" + +class HKDFCommon +{ +public: + virtual ~HKDFCommon(); + + void setKey(const void *key, size_t keyLen, const void *salt = 0, size_t saltLen = 0); + + void extract(void *out, size_t outLen, const void *info = 0, size_t infoLen = 0); + + void clear(); + +protected: + HKDFCommon(); + void setHashAlgorithm(Hash *hashAlg, uint8_t *buffer) + { + hash = hashAlg; + buf = buffer; + } + +private: + Hash *hash; + uint8_t *buf; + uint8_t counter; + uint8_t posn; +}; + +template +class HKDF : public HKDFCommon +{ +public: + HKDF() { setHashAlgorithm(&hashAlg, buffer); } + ~HKDF() { ::clean(buffer, sizeof(buffer)); } + +private: + T hashAlg; + uint8_t buffer[T::HASH_SIZE * 2]; +}; + +template void hkdf + (void *out, size_t outLen, const void *key, size_t keyLen, + const void *salt, size_t saltLen, const void *info, size_t infoLen) +{ + HKDF context; + context.setKey(key, keyLen, salt, saltLen); + context.extract(out, outLen, info, infoLen); +} + +#endif diff --git a/src/Hash.cpp b/src/Hash.cpp index ef132572..5f03f8c8 100644 --- a/src/Hash.cpp +++ b/src/Hash.cpp @@ -27,7 +27,7 @@ * \class Hash Hash.h * \brief Abstract base class for cryptographic hash algorithms. * - * \sa SHA256, SHA3_256, BLAKE2s + * \sa SHA224, SHA256, SHA384, SHA3_256, BLAKE2s */ /** @@ -178,3 +178,25 @@ void Hash::formatHMACKey(void *block, const void *key, size_t len, uint8_t 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/src/Hash.h b/src/Hash.h index 2f7db16a..18c72fff 100644 --- a/src/Hash.h +++ b/src/Hash.h @@ -48,4 +48,14 @@ class Hash 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/src/RNG.cpp b/src/RNG.cpp index 11db3636..84bbc4ed 100644 --- a/src/RNG.cpp +++ b/src/RNG.cpp @@ -24,8 +24,8 @@ #include "NoiseSource.h" #include "ChaCha.h" #include "Crypto.h" -#include "utility/ProgMemUtil.h" #include +#include "utility/ProgMemUtil.h" #if defined (__arm__) && defined (__SAM3X8E__) // The Arduino Due does not have any EEPROM natively on the main chip. // However, it does have a TRNG and flash memory. @@ -571,7 +571,7 @@ void RNGClass::rand(uint8_t *data, size_t len) begin(0); // Decrease the amount of entropy in the pool. - if (len > (credits / 8u)) + if ( (uint16_t)len > (credits / 8)) credits = 0; else credits -= len * 8; @@ -662,7 +662,7 @@ bool RNGClass::available(size_t len) const if (len >= (RNG_MAX_CREDITS / 8)) return credits >= RNG_MAX_CREDITS; else - return len <= (credits / 8u); + return (uint16_t)len <= (credits / 8); } /** @@ -695,7 +695,7 @@ void RNGClass::stir(const uint8_t *data, size_t len, unsigned int credit) // Increase the entropy credit. if ((credit / 8) >= len && len) credit = len * 8; - if ((RNG_MAX_CREDITS - credits) > credit) + if ((uint16_t)(RNG_MAX_CREDITS - credits) > credit) credits += credit; else credits = RNG_MAX_CREDITS; diff --git a/src/SHA224.cpp b/src/SHA224.cpp new file mode 100644 index 00000000..32041440 --- /dev/null +++ b/src/SHA224.cpp @@ -0,0 +1,70 @@ +/* + * 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 "SHA224.h" +#include "Crypto.h" + +/** + * \class SHA224 SHA224.h + * \brief SHA-224 hash algorithm. + * + * Reference: http://en.wikipedia.org/wiki/SHA-2 + * + * \sa SHA256, SHA512, SHA3_256, BLAKE2s + */ + +/** + * \var SHA224::HASH_SIZE + * \brief Constant for the size of the hash output of SHA224. + */ + +/** + * \var SHA224::BLOCK_SIZE + * \brief Constant for the block size of SHA224. + */ + +/** + * \brief Constructs a SHA-224 hash object. + */ +SHA224::SHA224() +{ + reset(); +} + +size_t SHA224::hashSize() const +{ + return 28; +} + +void SHA224::reset() +{ + state.h[0] = 0xc1059ed8; + state.h[1] = 0x367cd507; + state.h[2] = 0x3070dd17; + state.h[3] = 0xf70e5939; + state.h[4] = 0xffc00b31; + state.h[5] = 0x68581511; + state.h[6] = 0x64f98fa7; + state.h[7] = 0xbefa4fa4; + state.chunkSize = 0; + state.length = 0; +} diff --git a/src/SHA224.h b/src/SHA224.h new file mode 100644 index 00000000..ce327631 --- /dev/null +++ b/src/SHA224.h @@ -0,0 +1,40 @@ +/* + * 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_SHA224_h +#define CRYPTO_SHA224_h + +#include "SHA256.h" + +class SHA224 : public SHA256 +{ +public: + SHA224(); + + size_t hashSize() const; + + void reset(); + + static const size_t HASH_SIZE = 28; +}; + +#endif diff --git a/src/SHA256.cpp b/src/SHA256.cpp index 098b2e7a..07832695 100644 --- a/src/SHA256.cpp +++ b/src/SHA256.cpp @@ -33,7 +33,17 @@ * * Reference: http://en.wikipedia.org/wiki/SHA-2 * - * \sa SHA512, SHA3_256, BLAKE2s + * \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. */ /** @@ -125,8 +135,9 @@ void SHA256::finalize(void *hash, size_t len) state.w[posn] = htobe32(state.h[posn]); // Copy the hash to the caller's return buffer. - if (len > 32) - len = 32; + size_t maxHashSize = hashSize(); + if (len > maxHashSize) + len = maxHashSize; memcpy(hash, state.w, len); } @@ -150,7 +161,7 @@ void SHA256::finalizeHMAC(const void *key, size_t keyLen, void *hash, size_t has formatHMACKey(state.w, key, keyLen, 0x5C); state.length += 64 * 8; processChunk(); - update(temp, sizeof(temp)); + update(temp, hashSize()); finalize(hash, hashLen); clean(temp); } diff --git a/src/SHA256.h b/src/SHA256.h index 246ea586..ffc681c3 100644 --- a/src/SHA256.h +++ b/src/SHA256.h @@ -43,7 +43,10 @@ class SHA256 : public Hash void resetHMAC(const void *key, size_t keyLen); void finalizeHMAC(const void *key, size_t keyLen, void *hash, size_t hashLen); -private: + static const size_t HASH_SIZE = 32; + static const size_t BLOCK_SIZE = 64; + +protected: struct { uint32_t h[8]; uint32_t w[16]; diff --git a/src/SHA3.cpp b/src/SHA3.cpp index 74353c44..3ff0cbdf 100644 --- a/src/SHA3.cpp +++ b/src/SHA3.cpp @@ -32,6 +32,16 @@ * \sa SHA3_512 */ +/** + * \var SHA3_256::HASH_SIZE + * \brief Constant for the size of the hash output of SHA3-256. + */ + +/** + * \var SHA3_256::BLOCK_SIZE + * \brief Constant for the block size of SHA3-256. + */ + /** * \brief Constructs a new SHA3-256 hash object. */ @@ -104,6 +114,16 @@ void SHA3_256::finalizeHMAC(const void *key, size_t keyLen, void *hash, size_t h * \sa SHA3_256 */ +/** + * \var SHA3_512::HASH_SIZE + * \brief Constant for the size of the hash output of SHA3-512. + */ + +/** + * \var SHA3_512::BLOCK_SIZE + * \brief Constant for the block size of SHA3-512. + */ + /** * \brief Constructs a new SHA3-512 hash object. */ diff --git a/src/SHA3.h b/src/SHA3.h index 76bb031c..7da407f6 100644 --- a/src/SHA3.h +++ b/src/SHA3.h @@ -44,6 +44,9 @@ class SHA3_256 : public Hash 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 = 136; + private: KeccakCore core; }; @@ -66,6 +69,9 @@ class SHA3_512 : public Hash 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 = 64; + static const size_t BLOCK_SIZE = 72; + private: KeccakCore core; }; diff --git a/src/SHA384.cpp b/src/SHA384.cpp new file mode 100644 index 00000000..8c954e27 --- /dev/null +++ b/src/SHA384.cpp @@ -0,0 +1,71 @@ +/* + * 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 "SHA384.h" +#include "Crypto.h" +#include "utility/ProgMemUtil.h" +#include + +/** + * \class SHA384 SHA384.h + * \brief SHA-384 hash algorithm. + * + * Reference: http://en.wikipedia.org/wiki/SHA-2 + * + * \sa SHA256, SHA512, SHA3_256, BLAKE2s + */ + +/** + * \var SHA384::HASH_SIZE + * \brief Constant for the size of the hash output of SHA384. + */ + +/** + * \var SHA384::BLOCK_SIZE + * \brief Constant for the block size of SHA384. + */ + +/** + * \brief Constructs a SHA-384 hash object. + */ +SHA384::SHA384() +{ + reset(); +} + +size_t SHA384::hashSize() const +{ + return 48; +} + +void SHA384::reset() +{ + static uint64_t const hashStart[8] PROGMEM = { + 0xcbbb9d5dc1059ed8ULL, 0x629a292a367cd507ULL, 0x9159015a3070dd17ULL, + 0x152fecd8f70e5939ULL, 0x67332667ffc00b31ULL, 0x8eb44a8768581511ULL, + 0xdb0c2e0d64f98fa7ULL, 0x47b5481dbefa4fa4ULL + }; + memcpy_P(state.h, hashStart, sizeof(hashStart)); + state.chunkSize = 0; + state.lengthLow = 0; + state.lengthHigh = 0; +} diff --git a/src/SHA384.h b/src/SHA384.h new file mode 100644 index 00000000..556d8386 --- /dev/null +++ b/src/SHA384.h @@ -0,0 +1,40 @@ +/* + * 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_SHA384_h +#define CRYPTO_SHA384_h + +#include "SHA512.h" + +class SHA384 : public SHA512 +{ +public: + SHA384(); + + size_t hashSize() const; + + void reset(); + + static const size_t HASH_SIZE = 48; +}; + +#endif diff --git a/src/SHA512.cpp b/src/SHA512.cpp index 71cf0197..aba1d81e 100644 --- a/src/SHA512.cpp +++ b/src/SHA512.cpp @@ -33,7 +33,17 @@ * * Reference: http://en.wikipedia.org/wiki/SHA-2 * - * \sa SHA256, SHA3_512, BLAKE2b + * \sa SHA224, SHA256, SHA3_512, BLAKE2b + */ + +/** + * \var SHA512::HASH_SIZE + * \brief Constant for the size of the hash output of SHA512. + */ + +/** + * \var SHA512::BLOCK_SIZE + * \brief Constant for the block size of SHA512. */ /** @@ -128,8 +138,9 @@ void SHA512::finalize(void *hash, size_t len) state.w[posn] = htobe64(state.h[posn]); // Copy the hash to the caller's return buffer. - if (len > 64) - len = 64; + size_t maxHashSize = hashSize(); + if (len > maxHashSize) + len = maxHashSize; memcpy(hash, state.w, len); } @@ -153,7 +164,7 @@ void SHA512::finalizeHMAC(const void *key, size_t keyLen, void *hash, size_t has formatHMACKey(state.w, key, keyLen, 0x5C); state.lengthLow += 128 * 8; processChunk(); - update(temp, sizeof(temp)); + update(temp, hashSize()); finalize(hash, hashLen); clean(temp); } diff --git a/src/SHA512.h b/src/SHA512.h index d1a069f7..30e958f1 100644 --- a/src/SHA512.h +++ b/src/SHA512.h @@ -45,7 +45,10 @@ class SHA512 : public Hash void resetHMAC(const void *key, size_t keyLen); void finalizeHMAC(const void *key, size_t keyLen, void *hash, size_t hashLen); -private: + static const size_t HASH_SIZE = 64; + static const size_t BLOCK_SIZE = 128; + +protected: struct { uint64_t h[8]; uint64_t w[16]; diff --git a/src/utility/ProgMemUtil.h b/src/utility/ProgMemUtil.h index d7588e8a..023154b7 100644 --- a/src/utility/ProgMemUtil.h +++ b/src/utility/ProgMemUtil.h @@ -42,11 +42,21 @@ #else #include #define PROGMEM -#define pgm_read_byte(x) (*(x)) -#define pgm_read_word(x) (*(x)) -#define pgm_read_dword(x) (*(x)) -#define pgm_read_qword(x) (*(x)) -#define memcpy_P(d,s,l) memcpy((d), (s), (l)) +#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