1
0
mirror of https://github.com/taigrr/arduinolibs synced 2025-01-18 04:33:12 -08:00

BLAKE2b hash algorithm

This commit is contained in:
Rhys Weatherley 2015-03-14 08:47:03 +10:00
parent 72901a91f9
commit e10b398949
5 changed files with 505 additions and 7 deletions

View File

@ -29,14 +29,14 @@
\li Block ciphers: AES128, AES192, AES256 \li Block ciphers: AES128, AES192, AES256
\li Block cipher modes: CTR, CFB, CBC, OFB \li Block cipher modes: CTR, CFB, CBC, OFB
\li Stream ciphers: ChaCha \li Stream ciphers: ChaCha
\li Hash algorithms: SHA1, SHA256, SHA512, BLAKE2s \li Hash algorithms: SHA1, SHA256, SHA512, BLAKE2s, BLAKE2b
\li Public key algorithms: Curve25519 \li Public key algorithms: Curve25519
\li Random number generation: \link RNGClass RNG\endlink, TransistorNoiseSource \li Random number generation: \link RNGClass RNG\endlink, TransistorNoiseSource
All cryptographic algorithms have been optimized for 8-bit Arduino platforms All cryptographic algorithms have been optimized for 8-bit Arduino platforms
like the Uno. Memory usage is also reduced, particularly for SHA1, SHA256, like the Uno. Memory usage is also reduced, particularly for SHA1, SHA256,
and SHA512 which save 256, 192, and 512 bytes respectively over traditional and SHA512 which save 256, 192, and 512 bytes respectively over traditional
implementations. For other algorithms, static sbox tables and the like are implementations. For all algorithms, static sbox tables and the like are
placed into program memory to further reduce data memory usage. placed into program memory to further reduce data memory usage.
ChaCha with 20 rounds and 256-bit keys is the recommended ChaCha with 20 rounds and 256-bit keys is the recommended
@ -45,10 +45,10 @@ constant-time, and much more secure. AES128, AES192, and AES256 are
provided for use in applications where compatibility with other systems provided for use in applications where compatibility with other systems
is desirable. is desirable.
BLAKE2s is a variation on the ChaCha stream cipher, designed for hashing, BLAKE2s and BLAKE2b are variations on the ChaCha stream cipher, designed for
with a 256-bit hash output. It is intended as a high performance drop-in hashing, with 256-bit and 512-bit hash outputs respectively. They are
replacement for SHA256 for when speed is critical but exact SHA256 intended as high performance replacements for SHA256 and SHA512 for when
compatibility is not. speed is critical but exact bit-compatibility of hash values is not.
\section crypto_performance Performance \section crypto_performance Performance
@ -67,6 +67,7 @@ Ardunino Mega 2560 running at 16 MHz are similar:
<tr><td>SHA256</td><td align="right">43.85us</td><td> </td><td align="right"> </td><td align="right">106</td></tr> <tr><td>SHA256</td><td align="right">43.85us</td><td> </td><td align="right"> </td><td align="right">106</td></tr>
<tr><td>SHA512</td><td align="right">123.25us</td><td> </td><td align="right"> </td><td align="right">210</td></tr> <tr><td>SHA512</td><td align="right">123.25us</td><td> </td><td align="right"> </td><td align="right">210</td></tr>
<tr><td>BLAKE2s</td><td align="right">18.54us</td><td> </td><td align="right"> </td><td align="right">170</td></tr> <tr><td>BLAKE2s</td><td align="right">18.54us</td><td> </td><td align="right"> </td><td align="right">170</td></tr>
<tr><td>BLAKE2b</td><td align="right">50.59us</td><td> </td><td align="right"> </td><td align="right">338</td></tr>
</table> </table>
Where a cipher supports more than one key size (such as ChaCha), the values Where a cipher supports more than one key size (such as ChaCha), the values

View File

@ -93,7 +93,7 @@ realtime clock and the LCD library to implement an alarm clock.
\li Block ciphers: AES128, AES192, AES256 \li Block ciphers: AES128, AES192, AES256
\li Block cipher modes: CTR, CFB, CBC, OFB \li Block cipher modes: CTR, CFB, CBC, OFB
\li Stream ciphers: ChaCha \li Stream ciphers: ChaCha
\li Hash algorithms: SHA1, SHA256, SHA512, BLAKE2s \li Hash algorithms: SHA1, SHA256, SHA512, BLAKE2s, BLAKE2b
\li Public key algorithms: Curve25519 \li Public key algorithms: Curve25519
\li Random number generation: \link RNGClass RNG\endlink, TransistorNoiseSource \li Random number generation: \link RNGClass RNG\endlink, TransistorNoiseSource

View File

@ -0,0 +1,246 @@
/*
* 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 "BLAKE2b.h"
#include "Crypto.h"
#include "utility/EndianUtil.h"
#include "utility/RotateUtil.h"
#include "utility/ProgMemUtil.h"
#include <string.h>
/**
* \class BLAKE2b BLAKE2b.h <BLAKE2b.h>
* \brief BLAKE2b hash algorithm.
*
* BLAKE2b is a variation on the ChaCha stream cipher, designed for hashing,
* with a 512-bit hash output. It is intended as a high performance
* replacement for SHA512 for when speed is critical but exact SHA512
* compatibility is not.
*
* Reference: https://blake2.net/
*
* \sa BLAKE2s, SHA512
*/
/**
* \brief Constructs a BLAKE2b hash object.
*/
BLAKE2b::BLAKE2b()
{
reset();
}
/**
* \brief Destroys this BLAKE2b hash object after clearing
* sensitive information.
*/
BLAKE2b::~BLAKE2b()
{
clean(state);
}
size_t BLAKE2b::hashSize() const
{
return 64;
}
size_t BLAKE2b::blockSize() const
{
return 128;
}
// Initialization vectors for BLAKE2b.
#define BLAKE2b_IV0 0x6a09e667f3bcc908ULL
#define BLAKE2b_IV1 0xbb67ae8584caa73bULL
#define BLAKE2b_IV2 0x3c6ef372fe94f82bULL
#define BLAKE2b_IV3 0xa54ff53a5f1d36f1ULL
#define BLAKE2b_IV4 0x510e527fade682d1ULL
#define BLAKE2b_IV5 0x9b05688c2b3e6c1fULL
#define BLAKE2b_IV6 0x1f83d9abfb41bd6bULL
#define BLAKE2b_IV7 0x5be0cd19137e2179ULL
void BLAKE2b::reset()
{
state.h[0] = BLAKE2b_IV0 ^ 0x01010040; // Default output length of 64.
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;
state.chunkSize = 0;
state.finalized = false;
state.lengthLow = 0;
state.lengthHigh = 0;
}
/**
* \brief Resets the hash ready for a new hashing process with a specified
* output length.
*
* \param outputLength The output length to use for the final hash in bytes,
* between 1 and 64.
*/
void BLAKE2b::reset(uint8_t outputLength)
{
state.h[0] = BLAKE2b_IV0 ^ 0x01010000 ^ 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;
state.chunkSize = 0;
state.finalized = false;
state.lengthLow = 0;
state.lengthHigh = 0;
}
void BLAKE2b::update(const void *data, size_t len)
{
// Reset the hashing process if finalize() was called previously.
if (state.finalized)
reset();
// Break the input up into 1024-bit chunks and process each in turn.
const uint8_t *d = (const uint8_t *)data;
while (len > 0) {
if (state.chunkSize == 128) {
// Previous chunk was full and we know that it wasn't the
// last chunk, so we can process it now with f0 set to zero.
processChunk(0);
state.chunkSize = 0;
}
uint8_t size = 128 - state.chunkSize;
if (size > len)
size = len;
memcpy(((uint8_t *)state.m) + state.chunkSize, d, size);
state.chunkSize += size;
uint64_t temp = state.lengthLow;
state.lengthLow += size;
if (state.lengthLow < temp)
++state.lengthHigh;
len -= size;
d += size;
}
}
void BLAKE2b::finalize(void *hash, size_t len)
{
// Finalize the hash if necessary.
if (!state.finalized) {
// Pad the last chunk and hash it with f0 set to all-ones.
memset(((uint8_t *)state.m) + state.chunkSize, 0, 128 - state.chunkSize);
processChunk(0xFFFFFFFFFFFFFFFFULL);
// Convert the hash into little-endian in the message buffer.
for (uint8_t posn = 0; posn < 8; ++posn)
state.m[posn] = htole64(state.h[posn]);
state.finalized = true;
}
// Copy the hash to the caller's return buffer.
if (len > 64)
len = 64;
memcpy(hash, state.m, len);
}
void BLAKE2b::clear()
{
clean(state);
reset();
}
// Permutation on the message input state for BLAKE2b.
static const uint8_t sigma[12][16] PROGMEM = {
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
{14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
{11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4},
{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8},
{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13},
{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9},
{12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11},
{13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10},
{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5},
{10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0},
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
{14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
};
// Perform a BLAKE2b quarter round operation.
#define quarterRound(a, b, c, d, i) \
do { \
uint64_t _b = (b); \
uint64_t _a = (a) + _b + state.m[pgm_read_byte(&(sigma[index][2 * (i)]))]; \
uint64_t _d = rightRotate32_64((d) ^ _a); \
uint64_t _c = (c) + _d; \
_b = rightRotate24_64(_b ^ _c); \
_a += _b + state.m[pgm_read_byte(&(sigma[index][2 * (i) + 1]))]; \
(d) = _d = rightRotate16_64(_d ^ _a); \
_c += _d; \
(a) = _a; \
(b) = rightRotate63_64(_b ^ _c); \
(c) = _c; \
} while (0)
void BLAKE2b::processChunk(uint64_t f0)
{
uint8_t index;
// Byte-swap the message buffer into little-endian if necessary.
#if !defined(CRYPTO_LITTLE_ENDIAN)
for (index = 0; index < 16; ++index)
state.m[index] = le64toh(state.m[index]);
#endif
// Format the block to be hashed.
memcpy(state.v, state.h, sizeof(state.h));
state.v[8] = BLAKE2b_IV0;
state.v[9] = BLAKE2b_IV1;
state.v[10] = BLAKE2b_IV2;
state.v[11] = BLAKE2b_IV3;
state.v[12] = BLAKE2b_IV4 ^ state.lengthLow;
state.v[13] = BLAKE2b_IV5 ^ state.lengthHigh;
state.v[14] = BLAKE2b_IV6 ^ f0;
state.v[15] = BLAKE2b_IV7;
// Perform the 12 BLAKE2b rounds.
for (index = 0; index < 12; ++index) {
// Column round.
quarterRound(state.v[0], state.v[4], state.v[8], state.v[12], 0);
quarterRound(state.v[1], state.v[5], state.v[9], state.v[13], 1);
quarterRound(state.v[2], state.v[6], state.v[10], state.v[14], 2);
quarterRound(state.v[3], state.v[7], state.v[11], state.v[15], 3);
// Diagonal round.
quarterRound(state.v[0], state.v[5], state.v[10], state.v[15], 4);
quarterRound(state.v[1], state.v[6], state.v[11], state.v[12], 5);
quarterRound(state.v[2], state.v[7], state.v[8], state.v[13], 6);
quarterRound(state.v[3], state.v[4], state.v[9], state.v[14], 7);
}
// Combine the new and old hash values.
for (index = 0; index < 8; ++index)
state.h[index] ^= (state.v[index] ^ state.v[index + 8]);
}

View File

@ -0,0 +1,58 @@
/*
* 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_BLAKE2B_H
#define CRYPTO_BLAKE2B_H
#include "Hash.h"
class BLAKE2b : public Hash
{
public:
BLAKE2b();
virtual ~BLAKE2b();
size_t hashSize() const;
size_t blockSize() const;
void reset();
void reset(uint8_t outputLength);
void update(const void *data, size_t len);
void finalize(void *hash, size_t len);
void clear();
private:
struct {
uint64_t h[8];
uint64_t m[16];
uint64_t v[16];
uint8_t chunkSize;
bool finalized;
uint64_t lengthLow;
uint64_t lengthHigh;
} state;
void processChunk(uint64_t f0);
};
#endif

View File

@ -0,0 +1,193 @@
/*
* 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 BLAKE2b implementation to verify correct behaviour.
*/
#include <Crypto.h>
#include <BLAKE2b.h>
#include <string.h>
#define HASH_SIZE 64
struct TestHashVector
{
const char *name;
const char *data;
uint8_t hash[HASH_SIZE];
};
// Test vectors generated with the reference implementation of BLAKE2b.
static TestHashVector const testVectorBLAKE2b_1 = {
"BLAKE2b #1",
"",
{0x78, 0x6a, 0x02, 0xf7, 0x42, 0x01, 0x59, 0x03,
0xc6, 0xc6, 0xfd, 0x85, 0x25, 0x52, 0xd2, 0x72,
0x91, 0x2f, 0x47, 0x40, 0xe1, 0x58, 0x47, 0x61,
0x8a, 0x86, 0xe2, 0x17, 0xf7, 0x1f, 0x54, 0x19,
0xd2, 0x5e, 0x10, 0x31, 0xaf, 0xee, 0x58, 0x53,
0x13, 0x89, 0x64, 0x44, 0x93, 0x4e, 0xb0, 0x4b,
0x90, 0x3a, 0x68, 0x5b, 0x14, 0x48, 0xb7, 0x55,
0xd5, 0x6f, 0x70, 0x1a, 0xfe, 0x9b, 0xe2, 0xce}
};
static TestHashVector const testVectorBLAKE2b_2 = {
"BLAKE2b #2",
"abc",
{0xba, 0x80, 0xa5, 0x3f, 0x98, 0x1c, 0x4d, 0x0d,
0x6a, 0x27, 0x97, 0xb6, 0x9f, 0x12, 0xf6, 0xe9,
0x4c, 0x21, 0x2f, 0x14, 0x68, 0x5a, 0xc4, 0xb7,
0x4b, 0x12, 0xbb, 0x6f, 0xdb, 0xff, 0xa2, 0xd1,
0x7d, 0x87, 0xc5, 0x39, 0x2a, 0xab, 0x79, 0x2d,
0xc2, 0x52, 0xd5, 0xde, 0x45, 0x33, 0xcc, 0x95,
0x18, 0xd3, 0x8a, 0xa8, 0xdb, 0xf1, 0x92, 0x5a,
0xb9, 0x23, 0x86, 0xed, 0xd4, 0x00, 0x99, 0x23}
};
static TestHashVector const testVectorBLAKE2b_3 = {
"BLAKE2b #3",
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
{0x72, 0x85, 0xff, 0x3e, 0x8b, 0xd7, 0x68, 0xd6,
0x9b, 0xe6, 0x2b, 0x3b, 0xf1, 0x87, 0x65, 0xa3,
0x25, 0x91, 0x7f, 0xa9, 0x74, 0x4a, 0xc2, 0xf5,
0x82, 0xa2, 0x08, 0x50, 0xbc, 0x2b, 0x11, 0x41,
0xed, 0x1b, 0x3e, 0x45, 0x28, 0x59, 0x5a, 0xcc,
0x90, 0x77, 0x2b, 0xdf, 0x2d, 0x37, 0xdc, 0x8a,
0x47, 0x13, 0x0b, 0x44, 0xf3, 0x3a, 0x02, 0xe8,
0x73, 0x0e, 0x5a, 0xd8, 0xe1, 0x66, 0xe8, 0x88}
};
static TestHashVector const testVectorBLAKE2b_4 = {
"BLAKE2b #4",
"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn"
"hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
{0xce, 0x74, 0x1a, 0xc5, 0x93, 0x0f, 0xe3, 0x46,
0x81, 0x11, 0x75, 0xc5, 0x22, 0x7b, 0xb7, 0xbf,
0xcd, 0x47, 0xf4, 0x26, 0x12, 0xfa, 0xe4, 0x6c,
0x08, 0x09, 0x51, 0x4f, 0x9e, 0x0e, 0x3a, 0x11,
0xee, 0x17, 0x73, 0x28, 0x71, 0x47, 0xcd, 0xea,
0xee, 0xdf, 0xf5, 0x07, 0x09, 0xaa, 0x71, 0x63,
0x41, 0xfe, 0x65, 0x24, 0x0f, 0x4a, 0xd6, 0x77,
0x7d, 0x6b, 0xfa, 0xf9, 0x72, 0x6e, 0x5e, 0x52}
};
BLAKE2b blake2b;
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(&blake2b, &testVectorBLAKE2b_1);
testHash(&blake2b, &testVectorBLAKE2b_2);
testHash(&blake2b, &testVectorBLAKE2b_3);
testHash(&blake2b, &testVectorBLAKE2b_4);
Serial.println();
Serial.println("Performance Tests:");
perfHash(&blake2b);
}
void loop()
{
}