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

BLAKE2s hash function

This commit is contained in:
Rhys Weatherley 2015-01-04 15:49:16 +10:00
parent b7dc74b7c7
commit 91b3aa70e7
10 changed files with 490 additions and 17 deletions

View File

@ -29,7 +29,7 @@
\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, Arcfour \li Stream ciphers: ChaCha, Arcfour
\li Hash algorithms: SHA1, SHA256 \li Hash algorithms: SHA1, SHA256, BLAKE2s
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 and SHA256 like the Uno. Memory usage is also reduced, particularly for SHA1 and SHA256
@ -43,6 +43,11 @@ constant-time, and much more secure. AES128, AES192, AES256, and Arcfour
are provided for use in applications where compatibility with other systems are 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,
with a 256-bit hash output. It is intended as a high performance drop-in
replacement for SHA256 for when speed is critical but exact SHA256
compatibility is not.
\section crypto_examples Examples \section crypto_examples Examples
TBD TBD
@ -62,6 +67,7 @@ All figures are for the Arduino Uno running at 16 MHz:
<tr><td>ChaCha (8 rounds)</td><td align="right">8.13us</td><td align="right">8.14us</td><td align="right">43.74us</td><td align="right">130</td></tr> <tr><td>ChaCha (8 rounds)</td><td align="right">8.13us</td><td align="right">8.14us</td><td align="right">43.74us</td><td align="right">130</td></tr>
<tr><td>SHA1</td><td align="right">21.90us</td><td> </td><td align="right"> </td><td align="right">94</td></tr> <tr><td>SHA1</td><td align="right">21.90us</td><td> </td><td align="right"> </td><td align="right">94</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>SHA256</td><td align="right">43.85us</td><td> </td><td align="right"> </td><td align="right">106</td></tr>
<tr><td>BLAKE2s</td><td align="right">18.54us</td><td> </td><td align="right"> </td><td align="right">170</td></tr>
</table> </table>
Where a cipher supports more than one key size (such as ChaCha and Arcfour), Where a cipher supports more than one key size (such as ChaCha and Arcfour),

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, Arcfour \li Stream ciphers: ChaCha, Arcfour
\li Hash algorithms: SHA1, SHA256 \li Hash algorithms: SHA1, SHA256, BLAKE2s
More information can be found on the \ref crypto "Cryptographic Library" page. More information can be found on the \ref crypto "Cryptographic Library" page.

View File

@ -43,6 +43,8 @@
* \sa ChaCha, AES128, AES192, AES256 * \sa ChaCha, AES128, AES192, AES256
*/ */
/** @cond */
// AES S-box (http://en.wikipedia.org/wiki/Rijndael_S-box) // AES S-box (http://en.wikipedia.org/wiki/Rijndael_S-box)
static uint8_t const sbox[256] PROGMEM = { static uint8_t const sbox[256] PROGMEM = {
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, // 0x00 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, // 0x00
@ -115,6 +117,8 @@ static uint8_t const sbox_inverse[256] PROGMEM = {
0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
}; };
/** @endcond */
/** /**
* \brief Constructs an AES block cipher object. * \brief Constructs an AES block cipher object.
*/ */

View File

@ -0,0 +1,239 @@
/*
* 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 "BLAKE2s.h"
#include "Crypto.h"
#include "utility/EndianUtil.h"
#include "utility/RotateUtil.h"
#include "utility/ProgMemUtil.h"
#include <string.h>
/**
* \class BLAKE2s BLAKE2s.h <BLAKE2s.h>
* \brief BLAKE2s hash algorithm.
*
* BLAKE2s is a variation on the ChaCha stream cipher, designed for hashing,
* with a 256-bit hash output. It is intended as a high performance
* replacement for SHA256 for when speed is critical but exact SHA256
* compatibility is not.
*
* Reference: https://blake2.net/
*
* \sa SHA256
*/
/**
* \brief Constructs a BLAKE2s hash object.
*/
BLAKE2s::BLAKE2s()
{
reset();
}
/**
* \brief Destroys this BLAKE2s hash object after clearing
* sensitive information.
*/
BLAKE2s::~BLAKE2s()
{
clean(state);
}
size_t BLAKE2s::hashSize() const
{
return 32;
}
size_t BLAKE2s::blockSize() const
{
return 64;
}
// Initialization vectors for BLAKE2s.
#define BLAKE2s_IV0 0x6A09E667
#define BLAKE2s_IV1 0xBB67AE85
#define BLAKE2s_IV2 0x3C6EF372
#define BLAKE2s_IV3 0xA54FF53A
#define BLAKE2s_IV4 0x510E527F
#define BLAKE2s_IV5 0x9B05688C
#define BLAKE2s_IV6 0x1F83D9AB
#define BLAKE2s_IV7 0x5BE0CD19
void BLAKE2s::reset()
{
state.h[0] = BLAKE2s_IV0 ^ 0x01010020; // Default output length of 32.
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;
state.chunkSize = 0;
state.finalized = false;
state.length = 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 32.
*/
void BLAKE2s::reset(uint8_t outputLength)
{
state.h[0] = BLAKE2s_IV0 ^ 0x01010000 ^ 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;
state.chunkSize = 0;
state.finalized = false;
state.length = 0;
}
void BLAKE2s::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 512-bit chunks and process each in turn.
const uint8_t *d = (const uint8_t *)data;
while (len > 0) {
if (state.chunkSize == 64) {
// 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 = 64 - state.chunkSize;
if (size > len)
size = len;
memcpy(((uint8_t *)state.m) + state.chunkSize, d, size);
state.chunkSize += size;
state.length += size;
len -= size;
d += size;
}
}
void BLAKE2s::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, 64 - state.chunkSize);
processChunk(0xFFFFFFFF);
// Convert the hash into little-endian in the message buffer.
for (uint8_t posn = 0; posn < 8; ++posn)
state.m[posn] = htole32(state.h[posn]);
state.finalized = true;
}
// Copy the hash to the caller's return buffer.
if (len > 32)
len = 32;
memcpy(hash, state.m, len);
}
void BLAKE2s::clear()
{
clean(state);
reset();
}
// Permutation on the message input state for BLAKE2s.
static const uint8_t sigma[10][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}
};
// Perform a BLAKE2s quarter round operation.
#define quarterRound(a, b, c, d, i) \
do { \
uint32_t _b = (b); \
uint32_t _a = (a) + _b + state.m[pgm_read_byte(&(sigma[index][2 * (i)]))]; \
uint32_t _d = rightRotate16((d) ^ _a); \
uint32_t _c = (c) + _d; \
_b = rightRotate12(_b ^ _c); \
_a += _b + state.m[pgm_read_byte(&(sigma[index][2 * (i) + 1]))]; \
(d) = _d = rightRotate8(_d ^ _a); \
_c += _d; \
(a) = _a; \
(b) = rightRotate7(_b ^ _c); \
(c) = _c; \
} while (0)
void BLAKE2s::processChunk(uint32_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] = le32toh(state.m[index]);
#endif
// Format the block to be hashed.
memcpy(state.v, state.h, sizeof(state.h));
state.v[8] = BLAKE2s_IV0;
state.v[9] = BLAKE2s_IV1;
state.v[10] = BLAKE2s_IV2;
state.v[11] = BLAKE2s_IV3;
state.v[12] = BLAKE2s_IV4 ^ (uint32_t)(state.length);
state.v[13] = BLAKE2s_IV5 ^ (uint32_t)(state.length >> 32);
state.v[14] = BLAKE2s_IV6 ^ f0;
state.v[15] = BLAKE2s_IV7;
// Perform the 10 BLAKE2s rounds.
for (index = 0; index < 10; ++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,57 @@
/*
* 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_BLAKE2S_H
#define CRYPTO_BLAKE2S_H
#include "Hash.h"
class BLAKE2s : public Hash
{
public:
BLAKE2s();
virtual ~BLAKE2s();
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 {
uint32_t h[8];
uint32_t m[16];
uint32_t v[16];
uint8_t chunkSize;
bool finalized;
uint64_t length;
} state;
void processChunk(uint32_t f0);
};
#endif

View File

@ -199,18 +199,6 @@ void ChaCha::clear()
posn = 64; posn = 64;
} }
// On AVR it is faster to rotate left by 16 bits and then right by 4 bits
// one at a time than to rotate left by 12 bits in a single step.
#define leftRotate12(a) \
(__extension__ ({ \
uint32_t temp = (a); \
temp = (temp << 16) | (temp >> 16); \
temp = rightRotate(temp, 1); \
temp = rightRotate(temp, 1); \
temp = rightRotate(temp, 1); \
rightRotate(temp, 1); \
}))
// Perform a ChaCha quarter round operation. // Perform a ChaCha quarter round operation.
#define quarterRound(a, b, c, d) \ #define quarterRound(a, b, c, d) \
do { \ do { \

View File

@ -33,7 +33,7 @@
* *
* Reference: http://en.wikipedia.org/wiki/SHA-2 * Reference: http://en.wikipedia.org/wiki/SHA-2
* *
* \sa SHA1 * \sa SHA1, BLAKE2s
*/ */
/** /**

View File

@ -0,0 +1,177 @@
/*
* 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 BLAKE2s implementation to verify correct behaviour.
*/
#include <Crypto.h>
#include <BLAKE2s.h>
#include <string.h>
#define HASH_SIZE 32
struct TestHashVector
{
const char *name;
const char *data;
uint8_t hash[HASH_SIZE];
};
// Test vectors generated with the reference implementation of BLAKE2s.
static TestHashVector const testVectorBLAKE2s_1 = {
"BLAKE2s #1",
"",
{0x69, 0x21, 0x7a, 0x30, 0x79, 0x90, 0x80, 0x94,
0xe1, 0x11, 0x21, 0xd0, 0x42, 0x35, 0x4a, 0x7c,
0x1f, 0x55, 0xb6, 0x48, 0x2c, 0xa1, 0xa5, 0x1e,
0x1b, 0x25, 0x0d, 0xfd, 0x1e, 0xd0, 0xee, 0xf9}
};
static TestHashVector const testVectorBLAKE2s_2 = {
"BLAKE2s #2",
"abc",
{0x50, 0x8c, 0x5e, 0x8c, 0x32, 0x7c, 0x14, 0xe2,
0xe1, 0xa7, 0x2b, 0xa3, 0x4e, 0xeb, 0x45, 0x2f,
0x37, 0x45, 0x8b, 0x20, 0x9e, 0xd6, 0x3a, 0x29,
0x4d, 0x99, 0x9b, 0x4c, 0x86, 0x67, 0x59, 0x82}
};
static TestHashVector const testVectorBLAKE2s_3 = {
"BLAKE2s #3",
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
{0x6f, 0x4d, 0xf5, 0x11, 0x6a, 0x6f, 0x33, 0x2e,
0xda, 0xb1, 0xd9, 0xe1, 0x0e, 0xe8, 0x7d, 0xf6,
0x55, 0x7b, 0xea, 0xb6, 0x25, 0x9d, 0x76, 0x63,
0xf3, 0xbc, 0xd5, 0x72, 0x2c, 0x13, 0xf1, 0x89}
};
static TestHashVector const testVectorBLAKE2s_4 = {
"BLAKE2s #4",
"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn"
"hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
{0x35, 0x8d, 0xd2, 0xed, 0x07, 0x80, 0xd4, 0x05,
0x4e, 0x76, 0xcb, 0x6f, 0x3a, 0x5b, 0xce, 0x28,
0x41, 0xe8, 0xe2, 0xf5, 0x47, 0x43, 0x1d, 0x4d,
0x09, 0xdb, 0x21, 0xb6, 0x6d, 0x94, 0x1f, 0xc7}
};
BLAKE2s blake2s;
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(&blake2s, &testVectorBLAKE2s_1);
testHash(&blake2s, &testVectorBLAKE2s_2);
testHash(&blake2s, &testVectorBLAKE2s_3);
testHash(&blake2s, &testVectorBLAKE2s_4);
Serial.println();
Serial.println("Performance Tests:");
perfHash(&blake2s);
}
void loop()
{
}

View File

@ -28,6 +28,8 @@
// CPU is assumed to be little endian. Edit this file if you // CPU is assumed to be little endian. Edit this file if you
// need to port this library to a big endian CPU. // need to port this library to a big endian CPU.
#define CRYPTO_LITTLE_ENDIAN 1
#define htole32(x) (x) #define htole32(x) (x)
#define le32toh(x) (x) #define le32toh(x) (x)
#define htobe32(x) \ #define htobe32(x) \

View File

@ -78,8 +78,8 @@
// Left rotate by 11: Rotate left by 8, then left by 3. // Left rotate by 11: Rotate left by 8, then left by 3.
#define leftRotate11(a) (leftRotate(leftRotate(leftRotate(leftRotate((a), 8), 1), 1), 1)) #define leftRotate11(a) (leftRotate(leftRotate(leftRotate(leftRotate((a), 8), 1), 1), 1))
// Left rotate by 12: Rotate left by 8, then left by 4. // Left rotate by 12: Rotate left by 16, then right by 4.
#define leftRotate12(a) (leftRotate(leftRotate(leftRotate(leftRotate(leftRotate((a), 8), 1), 1), 1), 1)) #define leftRotate12(a) (rightRotate(rightRotate(rightRotate(rightRotate(leftRotate((a), 16), 1), 1), 1), 1))
// Left rotate by 13: Rotate left by 16, then right by 3. // Left rotate by 13: Rotate left by 16, then right by 3.
#define leftRotate13(a) (rightRotate(rightRotate(rightRotate(leftRotate((a), 16), 1), 1), 1)) #define leftRotate13(a) (rightRotate(rightRotate(rightRotate(leftRotate((a), 16), 1), 1), 1))