diff --git a/doc/crypto.dox b/doc/crypto.dox
index e9a3aa82..07af41c1 100644
--- a/doc/crypto.dox
+++ b/doc/crypto.dox
@@ -58,6 +58,8 @@ BLAKE2s and BLAKE2b are variations on the ChaCha stream cipher, designed for
hashing, with 256-bit and 512-bit hash outputs respectively. They are
intended as high performance replacements for SHA256 and SHA512 for when
speed is critical but exact bit-compatibility of hash values is not.
+BLAKE2s and BLAKE2b support regular hashing, BLAKE2 keyed hashing,
+and HMAC modes.
\section crypto_other Examples and other topics
@@ -71,7 +73,7 @@ All figures are for the Arduino Uno running at 16 MHz. Figures for the
Ardunino Mega 2560 running at 16 MHz are similar:
-Encryption Algorithm | Encryption (per byte) | Decryption (per byte) | Key Setup | State Size (bytes) |
+Encryption Algorithm | Encryption (per byte) | Decryption (per byte) | Key Setup | State Size (bytes) |
AES128 (ECB mode) | 33.28us | 63.18us | 160.00us | 181 |
AES192 (ECB mode) | 39.94us | 76.48us | 166.54us | 213 |
AES256 (ECB mode) | 46.61us | 89.78us | 227.97us | 245 |
@@ -88,7 +90,7 @@ Ardunino Mega 2560 running at 16 MHz are similar:
SpeckTiny (192-bit key, ECB mode) | 36.56us | | 13.62us | 35 |
SpeckTiny (256-bit key, ECB mode) | 37.87us | | 16.89us | 35 |
|
-AEAD Algorithm | Encryption (per byte) | Decryption (per byte) | Key Setup | State Size (bytes) |
+AEAD Algorithm | Encryption (per byte) | Decryption (per byte) | Key Setup | State Size (bytes) |
ChaChaPoly | 41.20us | 41.19us | 902.36us | 221 |
GCM<AES128> | 109.71us | 109.26us | 1265.69us | 284 |
GCM<AES192> | 116.38us | 115.92us | 1485.56us | 316 |
@@ -106,11 +108,13 @@ Ardunino Mega 2560 running at 16 MHz are similar:
SHA3_256 | 60.69us | 8180.24us | | 205 |
SHA3_512 | 113.88us | 8196.34us | | 205 |
BLAKE2s | 20.65us | 1335.25us | | 107 |
-BLAKE2b | 65.22us | 8375.36us | | 211 |
+BLAKE2b | 65.22us | 8375.34us | | 211 |
|
-Authentication Algorithm | Hashing (per byte) | Finalization | Key Setup | State Size (bytes) |
+Authentication Algorithm | Hashing (per byte) | Finalization | Key Setup | State Size (bytes) |
SHA256 (HMAC mode) | 43.85us | 8552.61us | 2836.49us | 107 |
+BLAKE2s (Keyed mode) | 20.65us | 1335.25us | 1339.51us | 107 |
BLAKE2s (HMAC mode) | 20.65us | 4055.56us | 1350.00us | 107 |
+BLAKE2b (Keyed mode) | 65.22us | 8375.34us | 8357.25us | 211 |
Poly1305 | 26.26us | 489.11us | 17.06us | 53 |
GHASH | 74.59us | 15.91us | 14.79us | 33 |
|
@@ -136,7 +140,7 @@ maximum is shown above.
All figures are for the Arduino Due running at 84 MHz:
-Encryption Algorithm | Encryption (per byte) | Decryption (per byte) | Key Setup | State Size (bytes) |
+Encryption Algorithm | Encryption (per byte) | Decryption (per byte) | Key Setup | State Size (bytes) |
AES128 (ECB mode) | 5.71us | 10.41us | 34.73us | 188 |
AES192 (ECB mode) | 6.87us | 12.57us | 36.51us | 220 |
AES256 (ECB mode) | 8.04us | 14.72 | 49.96us | 252 |
@@ -153,7 +157,7 @@ All figures are for the Arduino Due running at 84 MHz:
SpeckTiny (192-bit key, ECB mode) | 2.81us | | 1.54us | 48 |
SpeckTiny (256-bit key, ECB mode) | 2.90us | | 1.83us | 48 |
|
-AEAD Algorithm | Encryption (per byte) | Decryption (per byte) | Key Setup | State Size (bytes) |
+AEAD Algorithm | Encryption (per byte) | Decryption (per byte) | Key Setup | State Size (bytes) |
ChaChaPoly | 1.71us | 1.71us | 45.08us | 240 |
GCM<AES128> | 10.90us | 10.90us | 248.83us | 312 |
GCM<AES192> | 12.30us | 12.31us | 296.83us | 344 |
@@ -170,12 +174,14 @@ All figures are for the Arduino Due running at 84 MHz:
SHA512 | 2.87us | 370.37us | | 224 |
SHA3_256 | 5.64us | 735.29us | | 224 |
SHA3_512 | 10.42us | 735.49us | | 224 |
-BLAKE2s | 0.72us | 48.24us | | 120 |
-BLAKE2b | 1.29us | 165.28us | | 224 |
+BLAKE2s | 0.80us | 53.39us | | 120 |
+BLAKE2b | 1.28us | 164.66us | | 224 |
|
-Authentication Algorithm | Hashing (per byte) | Finalization | Key Setup | State Size (bytes) |
+Authentication Algorithm | Hashing (per byte) | Finalization | Key Setup | State Size (bytes) |
SHA256 (HMAC mode) | 1.15us | 238.98us | 80.44us | 120 |
-BLAKE2s (HMAC mode) | 0.72us | 157.75us | 57.18us | 120 |
+BLAKE2s (Keyed mode) | 0.80us | 53.39us | 55.10us | 120 |
+BLAKE2s (HMAC mode) | 0.80us | 168.20us | 57.60us | 120 |
+BLAKE2b (Keyed mode) | 1.28us | 164.66us | 166.68us | 224 |
Poly1305 | 0.81us | 19.01us | 2.57us | 60 |
GHASH | 4.47us | 1.52us | 2.60us | 36 |
|
diff --git a/libraries/Crypto/BLAKE2b.cpp b/libraries/Crypto/BLAKE2b.cpp
index 8cd8a880..7352ec06 100644
--- a/libraries/Crypto/BLAKE2b.cpp
+++ b/libraries/Crypto/BLAKE2b.cpp
@@ -36,7 +36,35 @@
* replacement for SHA512 for when speed is critical but exact SHA512
* compatibility is not.
*
- * Reference: https://blake2.net/
+ * This class supports two types of keyed hash. The BLAKE2 keyed hash and
+ * traditional HMAC. The BLAKE2 keyed hash is recommended unless there is
+ * some higher-level application need to be compatible with the HMAC
+ * construction. The keyed hash is computed as follows:
+ *
+ * \code
+ * BLAKE2b blake;
+ * blake.reset(key, sizeof(key), outputLength);
+ * blake.update(data1, sizeof(data1));
+ * blake.update(data2, sizeof(data2));
+ * ...
+ * blake.update(dataN, sizeof(dataN));
+ * blake.finalize(hash, outputLength);
+ * \endcode
+ *
+ * The HMAC is computed as follows (the output length is always 64):
+ *
+ * \code
+ * BLAKE2b blake;
+ * blake.resetHMAC(key, sizeof(key));
+ * blake.update(data1, sizeof(data1));
+ * blake.update(data2, sizeof(data2));
+ * ...
+ * blake.update(dataN, sizeof(dataN));
+ * blake.finalizeHMAC(key, sizeof(key), hash, 32);
+ * \endcode
+ *
+ * References: https://blake2.net/,
+ * RFC 7693
*
* \sa BLAKE2s, SHA512, SHA3_512
*/
@@ -102,6 +130,10 @@ void BLAKE2b::reset()
*/
void BLAKE2b::reset(uint8_t outputLength)
{
+ if (outputLength < 1)
+ outputLength = 1;
+ else if (outputLength > 64)
+ outputLength = 64;
state.h[0] = BLAKE2b_IV0 ^ 0x01010000 ^ outputLength;
state.h[1] = BLAKE2b_IV1;
state.h[2] = BLAKE2b_IV2;
@@ -115,6 +147,48 @@ void BLAKE2b::reset(uint8_t outputLength)
state.lengthHigh = 0;
}
+/**
+ * \brief Resets the hash ready for a new hashing process with a specified
+ * key and output length.
+ *
+ * \param key Points to the key.
+ * \param keyLen The length of the key in bytes, between 0 and 64.
+ * \param outputLength The output length to use for the final hash in bytes,
+ * between 1 and 64.
+ *
+ * If \a keyLen is greater than 64, then the \a key will be truncated to
+ * the first 64 bytes.
+ */
+void BLAKE2b::reset(const void *key, size_t keyLen, uint8_t outputLength)
+{
+ if (keyLen > 64)
+ keyLen = 64;
+ if (outputLength < 1)
+ outputLength = 1;
+ else if (outputLength > 64)
+ outputLength = 64;
+ state.h[0] = BLAKE2b_IV0 ^ 0x01010000 ^ (keyLen << 8) ^ outputLength;
+ state.h[1] = BLAKE2b_IV1;
+ state.h[2] = BLAKE2b_IV2;
+ state.h[3] = BLAKE2b_IV3;
+ state.h[4] = BLAKE2b_IV4;
+ state.h[5] = BLAKE2b_IV5;
+ state.h[6] = BLAKE2b_IV6;
+ state.h[7] = BLAKE2b_IV7;
+ if (keyLen > 0) {
+ // Set the first block to the key and pad with zeroes.
+ memcpy(state.m, key, keyLen);
+ memset(((uint8_t *)state.m) + keyLen, 0, 128 - keyLen);
+ state.chunkSize = 128;
+ state.lengthLow = 128;
+ } else {
+ // No key. The first data block is the first hashed block.
+ state.chunkSize = 0;
+ state.lengthLow = 0;
+ }
+ state.lengthHigh = 0;
+}
+
void BLAKE2b::update(const void *data, size_t len)
{
// Break the input up into 1024-bit chunks and process each in turn.
diff --git a/libraries/Crypto/BLAKE2b.h b/libraries/Crypto/BLAKE2b.h
index 7f876647..e3f78684 100644
--- a/libraries/Crypto/BLAKE2b.h
+++ b/libraries/Crypto/BLAKE2b.h
@@ -36,6 +36,8 @@ public:
void reset();
void reset(uint8_t outputLength);
+ void reset(const void *key, size_t keyLen, uint8_t outputLength = 64);
+
void update(const void *data, size_t len);
void finalize(void *hash, size_t len);
diff --git a/libraries/Crypto/BLAKE2s.cpp b/libraries/Crypto/BLAKE2s.cpp
index e99117b3..a707ff7e 100644
--- a/libraries/Crypto/BLAKE2s.cpp
+++ b/libraries/Crypto/BLAKE2s.cpp
@@ -36,7 +36,35 @@
* replacement for SHA256 for when speed is critical but exact SHA256
* compatibility is not.
*
- * Reference: https://blake2.net/
+ * This class supports two types of keyed hash. The BLAKE2 keyed hash and
+ * traditional HMAC. The BLAKE2 keyed hash is recommended unless there is
+ * some higher-level application need to be compatible with the HMAC
+ * construction. The keyed hash is computed as follows:
+ *
+ * \code
+ * BLAKE2s blake;
+ * blake.reset(key, sizeof(key), outputLength);
+ * blake.update(data1, sizeof(data1));
+ * blake.update(data2, sizeof(data2));
+ * ...
+ * blake.update(dataN, sizeof(dataN));
+ * blake.finalize(hash, outputLength);
+ * \endcode
+ *
+ * The HMAC is computed as follows (the output length is always 32):
+ *
+ * \code
+ * BLAKE2s blake;
+ * blake.resetHMAC(key, sizeof(key));
+ * blake.update(data1, sizeof(data1));
+ * blake.update(data2, sizeof(data2));
+ * ...
+ * blake.update(dataN, sizeof(dataN));
+ * blake.finalizeHMAC(key, sizeof(key), hash, 32);
+ * \endcode
+ *
+ * References: https://blake2.net/,
+ * RFC 7693
*
* \sa BLAKE2b, SHA256, SHA3_256
*/
@@ -101,6 +129,10 @@ void BLAKE2s::reset()
*/
void BLAKE2s::reset(uint8_t outputLength)
{
+ if (outputLength < 1)
+ outputLength = 1;
+ else if (outputLength > 32)
+ outputLength = 32;
state.h[0] = BLAKE2s_IV0 ^ 0x01010000 ^ outputLength;
state.h[1] = BLAKE2s_IV1;
state.h[2] = BLAKE2s_IV2;
@@ -113,6 +145,47 @@ void BLAKE2s::reset(uint8_t outputLength)
state.length = 0;
}
+/**
+ * \brief Resets the hash ready for a new hashing process with a specified
+ * key and output length.
+ *
+ * \param key Points to the key.
+ * \param keyLen The length of the key in bytes, between 0 and 32.
+ * \param outputLength The output length to use for the final hash in bytes,
+ * between 1 and 32.
+ *
+ * If \a keyLen is greater than 32, then the \a key will be truncated to
+ * the first 32 bytes.
+ */
+void BLAKE2s::reset(const void *key, size_t keyLen, uint8_t outputLength)
+{
+ if (keyLen > 32)
+ keyLen = 32;
+ if (outputLength < 1)
+ outputLength = 1;
+ else if (outputLength > 32)
+ outputLength = 32;
+ state.h[0] = BLAKE2s_IV0 ^ 0x01010000 ^ (keyLen << 8) ^ outputLength;
+ state.h[1] = BLAKE2s_IV1;
+ state.h[2] = BLAKE2s_IV2;
+ state.h[3] = BLAKE2s_IV3;
+ state.h[4] = BLAKE2s_IV4;
+ state.h[5] = BLAKE2s_IV5;
+ state.h[6] = BLAKE2s_IV6;
+ state.h[7] = BLAKE2s_IV7;
+ if (keyLen > 0) {
+ // Set the first block to the key and pad with zeroes.
+ memcpy(state.m, key, keyLen);
+ memset(((uint8_t *)state.m) + keyLen, 0, 64 - keyLen);
+ state.chunkSize = 64;
+ state.length = 64;
+ } else {
+ // No key. The first data block is the first hashed block.
+ state.chunkSize = 0;
+ state.length = 0;
+ }
+}
+
void BLAKE2s::update(const void *data, size_t len)
{
// Break the input up into 512-bit chunks and process each in turn.
diff --git a/libraries/Crypto/BLAKE2s.h b/libraries/Crypto/BLAKE2s.h
index 1131e282..3ebae2e6 100644
--- a/libraries/Crypto/BLAKE2s.h
+++ b/libraries/Crypto/BLAKE2s.h
@@ -36,6 +36,8 @@ public:
void reset();
void reset(uint8_t outputLength);
+ void reset(const void *key, size_t keyLen, uint8_t outputLength = 32);
+
void update(const void *data, size_t len);
void finalize(void *hash, size_t len);
diff --git a/libraries/Crypto/examples/TestBLAKE2b/TestBLAKE2b.ino b/libraries/Crypto/examples/TestBLAKE2b/TestBLAKE2b.ino
index a21b4a1c..683bbc6a 100644
--- a/libraries/Crypto/examples/TestBLAKE2b/TestBLAKE2b.ino
+++ b/libraries/Crypto/examples/TestBLAKE2b/TestBLAKE2b.ino
@@ -27,6 +27,7 @@ This example runs tests on the BLAKE2b implementation to verify correct behaviou
#include
#include
#include
+#include
#define HASH_SIZE 64
#define BLOCK_SIZE 128
@@ -39,7 +40,7 @@ struct TestHashVector
};
// Test vectors generated with the reference implementation of BLAKE2b.
-static TestHashVector const testVectorBLAKE2b_1 = {
+static TestHashVector const testVectorBLAKE2b_1 PROGMEM = {
"BLAKE2b #1",
"",
{0x78, 0x6a, 0x02, 0xf7, 0x42, 0x01, 0x59, 0x03,
@@ -51,7 +52,7 @@ static TestHashVector const testVectorBLAKE2b_1 = {
0x90, 0x3a, 0x68, 0x5b, 0x14, 0x48, 0xb7, 0x55,
0xd5, 0x6f, 0x70, 0x1a, 0xfe, 0x9b, 0xe2, 0xce}
};
-static TestHashVector const testVectorBLAKE2b_2 = {
+static TestHashVector const testVectorBLAKE2b_2 PROGMEM = {
"BLAKE2b #2",
"abc",
{0xba, 0x80, 0xa5, 0x3f, 0x98, 0x1c, 0x4d, 0x0d,
@@ -63,7 +64,7 @@ static TestHashVector const testVectorBLAKE2b_2 = {
0x18, 0xd3, 0x8a, 0xa8, 0xdb, 0xf1, 0x92, 0x5a,
0xb9, 0x23, 0x86, 0xed, 0xd4, 0x00, 0x99, 0x23}
};
-static TestHashVector const testVectorBLAKE2b_3 = {
+static TestHashVector const testVectorBLAKE2b_3 PROGMEM = {
"BLAKE2b #3",
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
{0x72, 0x85, 0xff, 0x3e, 0x8b, 0xd7, 0x68, 0xd6,
@@ -75,7 +76,7 @@ static TestHashVector const testVectorBLAKE2b_3 = {
0x47, 0x13, 0x0b, 0x44, 0xf3, 0x3a, 0x02, 0xe8,
0x73, 0x0e, 0x5a, 0xd8, 0xe1, 0x66, 0xe8, 0x88}
};
-static TestHashVector const testVectorBLAKE2b_4 = {
+static TestHashVector const testVectorBLAKE2b_4 PROGMEM = {
"BLAKE2b #4",
"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn"
"hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
@@ -116,6 +117,10 @@ bool testHash_N(Hash *hash, const struct TestHashVector *test, size_t inc)
void testHash(Hash *hash, const struct TestHashVector *test)
{
bool ok;
+ TestHashVector vec;
+
+ memcpy_P(&vec, test, sizeof(vec));
+ test = &vec;
Serial.print(test->name);
Serial.print(" ... ");
@@ -224,6 +229,125 @@ void testHMAC(Hash *hash, size_t keyLen)
Serial.println("Failed");
}
+// Deterministic sequences (Fibonacci generator). From RFC 7693.
+static void selftest_seq(uint8_t *out, size_t len, uint32_t seed)
+{
+ size_t i;
+ uint32_t t, a , b;
+
+ a = 0xDEAD4BAD * seed; // prime
+ b = 1;
+
+ for (i = 0; i < len; i++) { // fill the buf
+ t = a + b;
+ a = b;
+ b = t;
+ out[i] = (t >> 24) & 0xFF;
+ }
+}
+
+// Incremental version of above to save memory.
+static void selftest_seq_incremental(BLAKE2b *blake, size_t len, uint32_t seed)
+{
+ size_t i;
+ uint32_t t, a , b;
+
+ a = 0xDEAD4BAD * seed; // prime
+ b = 1;
+
+ for (i = 0; i < len; i++) { // fill the buf
+ t = a + b;
+ a = b;
+ b = t;
+ buffer[i % 128] = (t >> 24) & 0xFF;
+ if ((i % 128) == 127)
+ blake->update(buffer, 128);
+ }
+
+ blake->update(buffer, len % 128);
+}
+
+// Run the self-test from Appendix E of RFC 7693. Most of this code
+// is from RFC 7693, with modifications to use the Crypto library.
+void testRFC7693()
+{
+ // Grand hash of hash results.
+ static const uint8_t blake2b_res[32] PROGMEM = {
+ 0xC2, 0x3A, 0x78, 0x00, 0xD9, 0x81, 0x23, 0xBD,
+ 0x10, 0xF5, 0x06, 0xC6, 0x1E, 0x29, 0xDA, 0x56,
+ 0x03, 0xD7, 0x63, 0xB8, 0xBB, 0xAD, 0x2E, 0x73,
+ 0x7F, 0x5E, 0x76, 0x5A, 0x7B, 0xCC, 0xD4, 0x75
+ };
+ // Parameter sets.
+ static const uint8_t b2b_md_len[4] PROGMEM = { 20, 32, 48, 64 };
+ static const uint16_t b2b_in_len[6] PROGMEM = { 0, 3, 128, 129, 255, 1024 };
+
+ size_t i, j, outlen, inlen;
+ uint8_t md[64], key[64];
+ BLAKE2b inner;
+
+ Serial.print("BLAKE2b RFC 7693 ... ");
+
+ // 256-bit hash for testing.
+ blake2b.reset(32);
+
+ for (i = 0; i < 4; i++) {
+ outlen = pgm_read_byte(&(b2b_md_len[i]));
+ for (j = 0; j < 6; j++) {
+ inlen = pgm_read_word(&(b2b_in_len[j]));
+
+ inner.reset(outlen); // unkeyed hash
+ selftest_seq_incremental(&inner, inlen, inlen);
+ inner.finalize(md, outlen);
+ blake2b.update(md, outlen); // hash the hash
+
+ selftest_seq(key, outlen, outlen); // keyed hash
+ inner.reset(key, outlen, outlen);
+ selftest_seq_incremental(&inner, inlen, inlen);
+ inner.finalize(md, outlen);
+ blake2b.update(md, outlen); // hash the hash
+ }
+ }
+
+ // Compute and compare the hash of hashes.
+ bool ok = true;
+ blake2b.finalize(md, 32);
+ for (i = 0; i < 32; i++) {
+ if (md[i] != pgm_read_byte(&(blake2b_res[i])))
+ ok = false;
+ }
+
+ // Report the results.
+ if (ok)
+ Serial.println("Passed");
+ else
+ Serial.println("Failed");
+}
+
+void perfKeyed(BLAKE2b *hash)
+{
+ unsigned long start;
+ unsigned long elapsed;
+ int count;
+
+ Serial.print("Keyed Reset ... ");
+
+ for (size_t posn = 0; posn < sizeof(buffer); ++posn)
+ buffer[posn] = (uint8_t)posn;
+
+ start = micros();
+ for (count = 0; count < 1000; ++count) {
+ hash->reset(buffer, hash->hashSize());
+ hash->update(buffer, 1); // To flush the key chunk.
+ }
+ 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 perfFinalize(Hash *hash)
{
unsigned long start;
@@ -267,11 +391,13 @@ void setup()
testHMAC(&blake2b, BLOCK_SIZE);
testHMAC(&blake2b, BLOCK_SIZE + 1);
testHMAC(&blake2b, BLOCK_SIZE + 2);
+ testRFC7693();
Serial.println();
Serial.println("Performance Tests:");
perfHash(&blake2b);
+ perfKeyed(&blake2b);
perfFinalize(&blake2b);
}
diff --git a/libraries/Crypto/examples/TestBLAKE2s/TestBLAKE2s.ino b/libraries/Crypto/examples/TestBLAKE2s/TestBLAKE2s.ino
index 1c06d7cb..153e9b35 100644
--- a/libraries/Crypto/examples/TestBLAKE2s/TestBLAKE2s.ino
+++ b/libraries/Crypto/examples/TestBLAKE2s/TestBLAKE2s.ino
@@ -27,6 +27,7 @@ This example runs tests on the BLAKE2s implementation to verify correct behaviou
#include
#include
#include
+#include
#define HASH_SIZE 32
#define BLOCK_SIZE 64
@@ -208,6 +209,101 @@ void testHMAC(Hash *hash, size_t keyLen)
Serial.println("Failed");
}
+// Deterministic sequences (Fibonacci generator). From RFC 7693.
+static void selftest_seq(uint8_t *out, size_t len, uint32_t seed)
+{
+ size_t i;
+ uint32_t t, a , b;
+
+ a = 0xDEAD4BAD * seed; // prime
+ b = 1;
+
+ for (i = 0; i < len; i++) { // fill the buf
+ t = a + b;
+ a = b;
+ b = t;
+ out[i] = (t >> 24) & 0xFF;
+ }
+}
+
+// Incremental version of above to save memory.
+static void selftest_seq_incremental(BLAKE2s *blake, size_t len, uint32_t seed)
+{
+ size_t i;
+ uint32_t t, a , b;
+
+ a = 0xDEAD4BAD * seed; // prime
+ b = 1;
+
+ for (i = 0; i < len; i++) { // fill the buf
+ t = a + b;
+ a = b;
+ b = t;
+ buffer[i % 128] = (t >> 24) & 0xFF;
+ if ((i % 128) == 127)
+ blake->update(buffer, sizeof(buffer));
+ }
+
+ blake->update(buffer, len % 128);
+}
+
+// Run the self-test from Appendix E of RFC 7693. Most of this code
+// is from RFC 7693, with modifications to use the Crypto library.
+void testRFC7693()
+{
+ // Grand hash of hash results.
+ static const uint8_t blake2s_res[32] PROGMEM = {
+ 0x6A, 0x41, 0x1F, 0x08, 0xCE, 0x25, 0xAD, 0xCD,
+ 0xFB, 0x02, 0xAB, 0xA6, 0x41, 0x45, 0x1C, 0xEC,
+ 0x53, 0xC5, 0x98, 0xB2, 0x4F, 0x4F, 0xC7, 0x87,
+ 0xFB, 0xDC, 0x88, 0x79, 0x7F, 0x4C, 0x1D, 0xFE
+ };
+ // Parameter sets.
+ static const uint8_t b2s_md_len[4] PROGMEM = { 16, 20, 28, 32 };
+ static const uint16_t b2s_in_len[6] PROGMEM = { 0, 3, 64, 65, 255, 1024 };
+
+ size_t i, j, outlen, inlen;
+ uint8_t md[32], key[32];
+ BLAKE2s inner;
+
+ Serial.print("BLAKE2s RFC 7693 ... ");
+
+ // 256-bit hash for testing.
+ blake2s.reset(32);
+
+ for (i = 0; i < 4; i++) {
+ outlen = pgm_read_byte(&(b2s_md_len[i]));
+ for (j = 0; j < 6; j++) {
+ inlen = pgm_read_word(&(b2s_in_len[j]));
+
+ inner.reset(outlen); // unkeyed hash
+ selftest_seq_incremental(&inner, inlen, inlen);
+ inner.finalize(md, outlen);
+ blake2s.update(md, outlen); // hash the hash
+
+ selftest_seq(key, outlen, outlen); // keyed hash
+ inner.reset(key, outlen, outlen);
+ selftest_seq_incremental(&inner, inlen, inlen);
+ inner.finalize(md, outlen);
+ blake2s.update(md, outlen); // hash the hash
+ }
+ }
+
+ // Compute and compare the hash of hashes.
+ bool ok = true;
+ blake2s.finalize(md, 32);
+ for (i = 0; i < 32; i++) {
+ if (md[i] != pgm_read_byte(&(blake2s_res[i])))
+ ok = false;
+ }
+
+ // Report the results.
+ if (ok)
+ Serial.println("Passed");
+ else
+ Serial.println("Failed");
+}
+
void perfFinalize(Hash *hash)
{
unsigned long start;
@@ -230,6 +326,30 @@ void perfFinalize(Hash *hash)
Serial.println(" ops per second");
}
+void perfKeyed(BLAKE2s *hash)
+{
+ unsigned long start;
+ unsigned long elapsed;
+ int count;
+
+ Serial.print("Keyed Reset ... ");
+
+ for (size_t posn = 0; posn < sizeof(buffer); ++posn)
+ buffer[posn] = (uint8_t)posn;
+
+ start = micros();
+ for (count = 0; count < 1000; ++count) {
+ hash->reset(buffer, hash->hashSize());
+ hash->update(buffer, 1); // To flush the key chunk.
+ }
+ 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;
@@ -289,12 +409,14 @@ void setup()
testHMAC(&blake2s, BLOCK_SIZE);
testHMAC(&blake2s, BLOCK_SIZE + 1);
testHMAC(&blake2s, sizeof(buffer));
+ testRFC7693();
Serial.println();
Serial.println("Performance Tests:");
perfHash(&blake2s);
perfFinalize(&blake2s);
+ perfKeyed(&blake2s);
perfHMAC(&blake2s);
}