summaryrefslogtreecommitdiff
path: root/src/blake2.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/blake2.c')
-rw-r--r--src/blake2.c314
1 files changed, 314 insertions, 0 deletions
diff --git a/src/blake2.c b/src/blake2.c
new file mode 100644
index 0000000..cdd0384
--- /dev/null
+++ b/src/blake2.c
@@ -0,0 +1,314 @@
+/***********************************************************************/
+/* */
+/* The Cryptokit library */
+/* */
+/* Xavier Leroy, Collège de France and Inria */
+/* */
+/* Copyright 2020 Institut National de Recherche en Informatique et */
+/* en Automatique. All rights reserved. This file is distributed */
+/* under the terms of the GNU Library General Public License, with */
+/* the special exception on linking described in file LICENSE. */
+/* */
+/***********************************************************************/
+
+/* BLAKE2 hashing */
+
+#include <assert.h>
+#include <stdint.h>
+#include <string.h>
+#include "blake2.h"
+
+static const uint8_t BLAKE2_sigma[12][16] = {
+ { 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 }
+};
+
+/* BLAKE2b */
+
+static inline uint64_t U8TO64LE(unsigned char * src)
+{
+ return (uint64_t) src[0] | ((uint64_t) src[1] << 8)
+ | ((uint64_t) src[2] << 16) | ((uint64_t) src[3] << 24)
+ | ((uint64_t) src[4] << 32) | ((uint64_t) src[5] << 40)
+ | ((uint64_t) src[6] << 48) | ((uint64_t) src[7] << 56);
+}
+
+static inline uint64_t ROTR64(uint64_t x, int amount)
+{
+ return (x >> amount) | (x << (64 - amount));
+}
+
+static const uint64_t blake2b_iv[8] = {
+ UINT64_C(0x6a09e667f3bcc908),
+ UINT64_C(0xbb67ae8584caa73b),
+ UINT64_C(0x3c6ef372fe94f82b),
+ UINT64_C(0xa54ff53a5f1d36f1),
+ UINT64_C(0x510e527fade682d1),
+ UINT64_C(0x9b05688c2b3e6c1f),
+ UINT64_C(0x1f83d9abfb41bd6b),
+ UINT64_C(0x5be0cd19137e2179)
+};
+
+#define MIX2B(a,b,c,d,x,y) \
+ do { \
+ a += b + x; \
+ d = ROTR64(d ^ a, 32); \
+ c += d; \
+ b = ROTR64(b ^ c, 24); \
+ a += b + y; \
+ d = ROTR64(d ^ a, 16); \
+ c += d; \
+ b = ROTR64(b ^ c, 63); \
+ } while(0) \
+
+static void blake2b_compress(struct blake2b * s, unsigned char * data,
+ unsigned int numbytes, int is_last_block)
+{
+ uint64_t v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15;
+ uint64_t m[16];
+ int i;
+ const uint8_t * sigma;
+
+ /* Update the length */
+ s->len[0] += numbytes;
+ if (s->len[0] < numbytes) s->len[1]++; /* carry */
+ /* Initialize work space */
+ v0 = s->h[0]; v1 = s->h[1];
+ v2 = s->h[2]; v3 = s->h[3];
+ v4 = s->h[4]; v5 = s->h[5];
+ v6 = s->h[6]; v7 = s->h[7];
+ v8 = blake2b_iv[0]; v9 = blake2b_iv[1];
+ v10 = blake2b_iv[2]; v11 = blake2b_iv[3];
+ v12 = blake2b_iv[4] ^ s->len[0];
+ v13 = blake2b_iv[5] ^ s->len[1];
+ v14 = is_last_block ? ~ blake2b_iv[6] : blake2b_iv[6];
+ v15 = blake2b_iv[7];
+ /* Convert data to 16 64-bit words */
+ for (i = 0; i < 16; i++) {
+ m[i] = U8TO64LE(data + i * 8);
+ }
+ /* Twelve rounds of mixing */
+ for (i = 0; i < 12; i++) {
+ sigma = BLAKE2_sigma[i];
+ MIX2B(v0, v4, v8, v12, m[sigma[0]], m[sigma[1]]);
+ MIX2B(v1, v5, v9, v13, m[sigma[2]], m[sigma[3]]);
+ MIX2B(v2, v6, v10, v14, m[sigma[4]], m[sigma[5]]);
+ MIX2B(v3, v7, v11, v15, m[sigma[6]], m[sigma[7]]);
+ MIX2B(v0, v5, v10, v15, m[sigma[8]], m[sigma[9]]);
+ MIX2B(v1, v6, v11, v12, m[sigma[10]], m[sigma[11]]);
+ MIX2B(v2, v7, v8, v13, m[sigma[12]], m[sigma[13]]);
+ MIX2B(v3, v4, v9, v14, m[sigma[14]], m[sigma[15]]);
+ }
+ /* Update state */
+ s->h[0] ^= v0 ^ v8; s->h[1] ^= v1 ^ v9;
+ s->h[2] ^= v2 ^ v10; s->h[3] ^= v3 ^ v11;
+ s->h[4] ^= v4 ^ v12; s->h[5] ^= v5 ^ v13;
+ s->h[6] ^= v6 ^ v14; s->h[7] ^= v7 ^ v15;
+}
+
+void blake2b_init(struct blake2b * s,
+ int hashlen, int keylen, unsigned char * key)
+{
+ int i;
+ assert (0 < hashlen && hashlen <= 64);
+ assert (0 <= keylen && keylen <= 64);
+ for (i = 0; i < 8; i++) s->h[i] = blake2b_iv[i];
+ s->h[0] ^= 0x01010000 | (keylen << 8) | hashlen;
+ s->len[0] = s->len[1] = 0;
+ s->numbytes = 0;
+ /* If key was supplied, pad to 128 bytes and prepend to message */
+ if (keylen > 0) {
+ memset(s->buffer, 0, BLAKE2b_BLOCKSIZE);
+ memcpy(s->buffer, key, keylen);
+ s->numbytes = BLAKE2b_BLOCKSIZE;
+ }
+}
+
+void blake2b_add_data(struct blake2b * s,
+ unsigned char * data, size_t len)
+{
+ int n;
+ /* If data was left in buffer, pad it with fresh data and compress */
+ if (s->numbytes > 0) {
+ n = BLAKE2b_BLOCKSIZE - s->numbytes;
+ if (len <= n) {
+ /* Not enough fresh data to compress. Buffer the data. */
+ memcpy(s->buffer + s->numbytes, data, len);
+ s->numbytes += len;
+ return;
+ }
+ memcpy(s->buffer + s->numbytes, data, n);
+ blake2b_compress(s, s->buffer, BLAKE2b_BLOCKSIZE, 0);
+ data += n; len -= n;
+ }
+ /* Process data by blocks of BLAKE2b_BLOCKSIZE */
+ while (len > BLAKE2b_BLOCKSIZE) {
+ blake2b_compress(s, data, BLAKE2b_BLOCKSIZE, 0);
+ data += BLAKE2b_BLOCKSIZE; len -= BLAKE2b_BLOCKSIZE;
+ }
+ /* Save remaining data */
+ memcpy(s->buffer, data, len);
+ s->numbytes = len;
+}
+
+void blake2b_final(struct blake2b * s, int hashlen, unsigned char * hash)
+{
+ unsigned int i;
+ assert (0 < hashlen && hashlen <= 64);
+ /* The final block is composed of the remaining data padded with zeros. */
+ memset(s->buffer + s->numbytes, 0, BLAKE2b_BLOCKSIZE - s->numbytes);
+ blake2b_compress(s, s->buffer, s->numbytes, 1);
+ /* Extract the hash */
+ for (i = 0; i < hashlen; i++) {
+ hash[i] = s->h[i / 8] >> (8 * (i % 8));
+ }
+}
+
+/* BLAKE2s */
+
+static inline uint32_t U8TO32LE(unsigned char * src)
+{
+ return (uint32_t) src[0] | ((uint32_t) src[1] << 8)
+ | ((uint32_t) src[2] << 16) | ((uint32_t) src[3] << 24);
+}
+
+static inline uint32_t ROTR32(uint32_t x, int amount)
+{
+ return (x >> amount) | (x << (32 - amount));
+}
+
+static const uint32_t blake2s_iv[8] = {
+ UINT32_C(0x6A09E667),
+ UINT32_C(0xBB67AE85),
+ UINT32_C(0x3C6EF372),
+ UINT32_C(0xA54FF53A),
+ UINT32_C(0x510E527F),
+ UINT32_C(0x9B05688C),
+ UINT32_C(0x1F83D9AB),
+ UINT32_C(0x5BE0CD19)
+};
+
+#define MIX2S(a,b,c,d,x,y) \
+ do { \
+ a += b + x; \
+ d = ROTR32(d ^ a, 16); \
+ c += d; \
+ b = ROTR32(b ^ c, 12); \
+ a += b + y; \
+ d = ROTR32(d ^ a, 8); \
+ c += d; \
+ b = ROTR32(b ^ c, 7); \
+ } while(0) \
+
+static void blake2s_compress(struct blake2s * s, unsigned char * data,
+ unsigned int numbytes, int is_last_block)
+{
+ uint32_t v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15;
+ uint32_t m[16];
+ int i;
+ const uint8_t * sigma;
+
+ /* Update the length */
+ s->len[0] += numbytes;
+ if (s->len[0] < numbytes) s->len[1]++; /* carry */
+ /* Initialize work space */
+ v0 = s->h[0]; v1 = s->h[1];
+ v2 = s->h[2]; v3 = s->h[3];
+ v4 = s->h[4]; v5 = s->h[5];
+ v6 = s->h[6]; v7 = s->h[7];
+ v8 = blake2s_iv[0]; v9 = blake2s_iv[1];
+ v10 = blake2s_iv[2]; v11 = blake2s_iv[3];
+ v12 = blake2s_iv[4] ^ s->len[0];
+ v13 = blake2s_iv[5] ^ s->len[1];
+ v14 = is_last_block ? ~ blake2s_iv[6] : blake2s_iv[6];
+ v15 = blake2s_iv[7];
+ /* Convert data to 16 32-bit words */
+ for (i = 0; i < 16; i++) {
+ m[i] = U8TO32LE(data + i * 4);
+ }
+ /* Ten rounds of mixing */
+ for (i = 0; i < 10; i++) {
+ sigma = BLAKE2_sigma[i];
+ MIX2S(v0, v4, v8, v12, m[sigma[0]], m[sigma[1]]);
+ MIX2S(v1, v5, v9, v13, m[sigma[2]], m[sigma[3]]);
+ MIX2S(v2, v6, v10, v14, m[sigma[4]], m[sigma[5]]);
+ MIX2S(v3, v7, v11, v15, m[sigma[6]], m[sigma[7]]);
+ MIX2S(v0, v5, v10, v15, m[sigma[8]], m[sigma[9]]);
+ MIX2S(v1, v6, v11, v12, m[sigma[10]], m[sigma[11]]);
+ MIX2S(v2, v7, v8, v13, m[sigma[12]], m[sigma[13]]);
+ MIX2S(v3, v4, v9, v14, m[sigma[14]], m[sigma[15]]);
+ }
+ /* Update state */
+ s->h[0] ^= v0 ^ v8; s->h[1] ^= v1 ^ v9;
+ s->h[2] ^= v2 ^ v10; s->h[3] ^= v3 ^ v11;
+ s->h[4] ^= v4 ^ v12; s->h[5] ^= v5 ^ v13;
+ s->h[6] ^= v6 ^ v14; s->h[7] ^= v7 ^ v15;
+}
+
+void blake2s_init(struct blake2s * s,
+ int hashlen, int keylen, unsigned char * key)
+{
+ int i;
+ assert (0 < hashlen && hashlen <= 32);
+ assert (0 <= keylen && keylen <= 32);
+ for (i = 0; i < 8; i++) s->h[i] = blake2s_iv[i];
+ s->h[0] ^= 0x01010000 | (keylen << 8) | hashlen;
+ s->len[0] = s->len[1] = 0;
+ s->numbytes = 0;
+ /* If key was supplied, pad to 64 bytes and prepend to message */
+ if (keylen > 0) {
+ memset(s->buffer, 0, BLAKE2s_BLOCKSIZE);
+ memcpy(s->buffer, key, keylen);
+ s->numbytes = BLAKE2s_BLOCKSIZE;
+ }
+}
+
+void blake2s_add_data(struct blake2s * s,
+ unsigned char * data, size_t len)
+{
+ int n;
+ /* If data was left in buffer, pad it with fresh data and compress */
+ if (s->numbytes > 0) {
+ n = BLAKE2s_BLOCKSIZE - s->numbytes;
+ if (len <= n) {
+ /* Not enough fresh data to compress. Buffer the data. */
+ memcpy(s->buffer + s->numbytes, data, len);
+ s->numbytes += len;
+ return;
+ }
+ memcpy(s->buffer + s->numbytes, data, n);
+ blake2s_compress(s, s->buffer, BLAKE2s_BLOCKSIZE, 0);
+ data += n; len -= n;
+ }
+ /* Process data by blocks of BLAKE2s_BLOCKSIZE */
+ while (len > BLAKE2s_BLOCKSIZE) {
+ blake2s_compress(s, data, BLAKE2s_BLOCKSIZE, 0);
+ data += BLAKE2s_BLOCKSIZE; len -= BLAKE2s_BLOCKSIZE;
+ }
+ /* Save remaining data */
+ memcpy(s->buffer, data, len);
+ s->numbytes = len;
+}
+
+void blake2s_final(struct blake2s * s, int hashlen, unsigned char * hash)
+{
+ unsigned int i;
+ assert (0 < hashlen && hashlen <= 32);
+ /* The final block is composed of the remaining data padded with zeros. */
+ memset(s->buffer + s->numbytes, 0, BLAKE2s_BLOCKSIZE - s->numbytes);
+ blake2s_compress(s, s->buffer, s->numbytes, 1);
+ /* Extract the hash */
+ for (i = 0; i < hashlen; i++) {
+ hash[i] = s->h[i / 4] >> (8 * (i % 4));
+ }
+}