| /* |
| * blake2b.c - definitions for the blake2b hash function |
| * |
| * This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| #ifdef FREEBL_NO_DEPEND |
| #include "stubs.h" |
| #endif |
| |
| #include "secerr.h" |
| #include "blapi.h" |
| #include "blake2b.h" |
| #include "crypto_primitives.h" |
| |
| /** |
| * This contains the BLAKE2b initialization vectors. |
| */ |
| static const uint64_t iv[8] = { |
| 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, 0x3c6ef372fe94f82bULL, |
| 0xa54ff53a5f1d36f1ULL, 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, |
| 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL |
| }; |
| |
| /** |
| * This contains the table of permutations for blake2b compression function. |
| */ |
| static const uint8_t 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 } |
| }; |
| |
| /** |
| * This function increments the blake2b ctx counter. |
| */ |
| void |
| blake2b_IncrementCounter(BLAKE2BContext* ctx, const uint64_t inc) |
| { |
| ctx->t[0] += inc; |
| ctx->t[1] += ctx->t[0] < inc; |
| } |
| |
| /** |
| * This macro implements the blake2b mixing function which mixes two 8-byte |
| * words from the message into the hash. |
| */ |
| #define G(a, b, c, d, x, y) \ |
| 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) |
| |
| #define ROUND(i) \ |
| G(v[0], v[4], v[8], v[12], m[sigma[i][0]], m[sigma[i][1]]); \ |
| G(v[1], v[5], v[9], v[13], m[sigma[i][2]], m[sigma[i][3]]); \ |
| G(v[2], v[6], v[10], v[14], m[sigma[i][4]], m[sigma[i][5]]); \ |
| G(v[3], v[7], v[11], v[15], m[sigma[i][6]], m[sigma[i][7]]); \ |
| G(v[0], v[5], v[10], v[15], m[sigma[i][8]], m[sigma[i][9]]); \ |
| G(v[1], v[6], v[11], v[12], m[sigma[i][10]], m[sigma[i][11]]); \ |
| G(v[2], v[7], v[8], v[13], m[sigma[i][12]], m[sigma[i][13]]); \ |
| G(v[3], v[4], v[9], v[14], m[sigma[i][14]], m[sigma[i][15]]) |
| |
| /** |
| * The blake2b compression function which takes a full 128-byte chunk of the |
| * input message and mixes it into the ongoing ctx array, i.e., permute the |
| * ctx while xoring in the block of data. |
| */ |
| void |
| blake2b_Compress(BLAKE2BContext* ctx, const uint8_t* block) |
| { |
| size_t i; |
| uint64_t v[16], m[16]; |
| |
| PORT_Memcpy(m, block, BLAKE2B_BLOCK_LENGTH); |
| #if !defined(IS_LITTLE_ENDIAN) |
| for (i = 0; i < 16; ++i) { |
| m[i] = FREEBL_HTONLL(m[i]); |
| } |
| #endif |
| |
| PORT_Memcpy(v, ctx->h, 8 * 8); |
| PORT_Memcpy(v + 8, iv, 8 * 8); |
| |
| v[12] ^= ctx->t[0]; |
| v[13] ^= ctx->t[1]; |
| v[14] ^= ctx->f; |
| |
| ROUND(0); |
| ROUND(1); |
| ROUND(2); |
| ROUND(3); |
| ROUND(4); |
| ROUND(5); |
| ROUND(6); |
| ROUND(7); |
| ROUND(8); |
| ROUND(9); |
| ROUND(10); |
| ROUND(11); |
| |
| for (i = 0; i < 8; i++) { |
| ctx->h[i] ^= v[i] ^ v[i + 8]; |
| } |
| } |
| |
| /** |
| * This function can be used for both keyed and unkeyed version. |
| */ |
| BLAKE2BContext* |
| BLAKE2B_NewContext() |
| { |
| return PORT_ZNew(BLAKE2BContext); |
| } |
| |
| /** |
| * Zero and free the context and can be used for both keyed and unkeyed version. |
| */ |
| void |
| BLAKE2B_DestroyContext(BLAKE2BContext* ctx, PRBool freeit) |
| { |
| PORT_Memset(ctx, 0, sizeof(*ctx)); |
| if (freeit) { |
| PORT_Free(ctx); |
| } |
| } |
| |
| /** |
| * This function initializes blake2b ctx and can be used for both keyed and |
| * unkeyed version. It also checks ctx and sets error states. |
| */ |
| static SECStatus |
| blake2b_Begin(BLAKE2BContext* ctx, uint8_t outlen, const uint8_t* key, |
| size_t keylen) |
| { |
| if (!ctx) { |
| goto failure_noclean; |
| } |
| if (outlen == 0 || outlen > BLAKE2B512_LENGTH) { |
| goto failure; |
| } |
| if (key && keylen > BLAKE2B_KEY_SIZE) { |
| goto failure; |
| } |
| /* Note: key can be null if it's unkeyed. */ |
| if ((key == NULL && keylen > 0) || keylen > BLAKE2B_KEY_SIZE || |
| (key != NULL && keylen == 0)) { |
| goto failure; |
| } |
| |
| /* Mix key size(keylen) and desired hash length(outlen) into h0 */ |
| uint64_t param = outlen ^ (keylen << 8) ^ (1 << 16) ^ (1 << 24); |
| PORT_Memcpy(ctx->h, iv, 8 * 8); |
| ctx->h[0] ^= param; |
| ctx->outlen = outlen; |
| |
| /* This updates the context for only the keyed version */ |
| if (keylen > 0 && keylen <= BLAKE2B_KEY_SIZE && key) { |
| uint8_t block[BLAKE2B_BLOCK_LENGTH] = { 0 }; |
| PORT_Memcpy(block, key, keylen); |
| BLAKE2B_Update(ctx, block, BLAKE2B_BLOCK_LENGTH); |
| PORT_Memset(block, 0, BLAKE2B_BLOCK_LENGTH); |
| } |
| |
| return SECSuccess; |
| |
| failure: |
| PORT_Memset(ctx, 0, sizeof(*ctx)); |
| failure_noclean: |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| return SECFailure; |
| } |
| |
| SECStatus |
| BLAKE2B_Begin(BLAKE2BContext* ctx) |
| { |
| return blake2b_Begin(ctx, BLAKE2B512_LENGTH, NULL, 0); |
| } |
| |
| SECStatus |
| BLAKE2B_MAC_Begin(BLAKE2BContext* ctx, const PRUint8* key, const size_t keylen) |
| { |
| PORT_Assert(key != NULL); |
| if (!key) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| return SECFailure; |
| } |
| return blake2b_Begin(ctx, BLAKE2B512_LENGTH, (const uint8_t*)key, keylen); |
| } |
| |
| static void |
| blake2b_IncrementCompress(BLAKE2BContext* ctx, size_t blockLength, |
| const unsigned char* input) |
| { |
| blake2b_IncrementCounter(ctx, blockLength); |
| blake2b_Compress(ctx, input); |
| } |
| |
| /** |
| * This function updates blake2b ctx and can be used for both keyed and unkeyed |
| * version. |
| */ |
| SECStatus |
| BLAKE2B_Update(BLAKE2BContext* ctx, const unsigned char* in, |
| unsigned int inlen) |
| { |
| /* Nothing to do if there's nothing. */ |
| if (inlen == 0) { |
| return SECSuccess; |
| } |
| |
| if (!ctx || !in) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| return SECFailure; |
| } |
| |
| /* Is this a reused context? */ |
| if (ctx->f) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| return SECFailure; |
| } |
| |
| size_t left = ctx->buflen; |
| PORT_Assert(left <= BLAKE2B_BLOCK_LENGTH); |
| size_t fill = BLAKE2B_BLOCK_LENGTH - left; |
| |
| if (inlen > fill) { |
| if (ctx->buflen) { |
| /* There's some remaining data in ctx->buf that we have to prepend |
| * to in. */ |
| PORT_Memcpy(ctx->buf + left, in, fill); |
| ctx->buflen = 0; |
| blake2b_IncrementCompress(ctx, BLAKE2B_BLOCK_LENGTH, ctx->buf); |
| in += fill; |
| inlen -= fill; |
| } |
| while (inlen > BLAKE2B_BLOCK_LENGTH) { |
| blake2b_IncrementCompress(ctx, BLAKE2B_BLOCK_LENGTH, in); |
| in += BLAKE2B_BLOCK_LENGTH; |
| inlen -= BLAKE2B_BLOCK_LENGTH; |
| } |
| } |
| |
| /* Store the remaining data from in in ctx->buf to process later. |
| * Note that ctx->buflen can be BLAKE2B_BLOCK_LENGTH. We can't process that |
| * here because we have to update ctx->f before compressing the last block. |
| */ |
| PORT_Assert(inlen <= BLAKE2B_BLOCK_LENGTH); |
| PORT_Memcpy(ctx->buf + ctx->buflen, in, inlen); |
| ctx->buflen += inlen; |
| |
| return SECSuccess; |
| } |
| |
| /** |
| * This function finalizes ctx, pads final block and stores hash. |
| * It can be used for both keyed and unkeyed version. |
| */ |
| SECStatus |
| BLAKE2B_End(BLAKE2BContext* ctx, unsigned char* out, |
| unsigned int* digestLen, size_t maxDigestLen) |
| { |
| size_t i; |
| unsigned int outlen = PR_MIN(BLAKE2B512_LENGTH, maxDigestLen); |
| |
| /* Argument checks */ |
| if (!ctx || !out) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| return SECFailure; |
| } |
| |
| /* Sanity check against outlen in context. */ |
| if (ctx->outlen < outlen) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| return SECFailure; |
| } |
| |
| /* Is this a reused context? */ |
| if (ctx->f != 0) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| return SECFailure; |
| } |
| |
| /* Process the remaining data from ctx->buf (padded with 0). */ |
| blake2b_IncrementCounter(ctx, ctx->buflen); |
| /* BLAKE2B_BLOCK_LENGTH - ctx->buflen can be 0. */ |
| PORT_Memset(ctx->buf + ctx->buflen, 0, BLAKE2B_BLOCK_LENGTH - ctx->buflen); |
| ctx->f = UINT64_MAX; |
| blake2b_Compress(ctx, ctx->buf); |
| |
| /* Write out the blake2b context(ctx). */ |
| for (i = 0; i < outlen; ++i) { |
| out[i] = ctx->h[i / 8] >> ((i % 8) * 8); |
| } |
| |
| if (digestLen) { |
| *digestLen = outlen; |
| } |
| |
| return SECSuccess; |
| } |
| |
| SECStatus |
| blake2b_HashBuf(uint8_t* output, const uint8_t* input, uint8_t outlen, |
| size_t inlen, const uint8_t* key, size_t keylen) |
| { |
| SECStatus rv = SECFailure; |
| BLAKE2BContext ctx = { { 0 } }; |
| |
| if (inlen != 0) { |
| PORT_Assert(input != NULL); |
| if (input == NULL) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| goto done; |
| } |
| } |
| |
| PORT_Assert(output != NULL); |
| if (output == NULL) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| goto done; |
| } |
| |
| if (blake2b_Begin(&ctx, outlen, key, keylen) != SECSuccess) { |
| goto done; |
| } |
| |
| if (BLAKE2B_Update(&ctx, input, inlen) != SECSuccess) { |
| goto done; |
| } |
| |
| if (BLAKE2B_End(&ctx, output, NULL, outlen) != SECSuccess) { |
| goto done; |
| } |
| rv = SECSuccess; |
| |
| done: |
| PORT_Memset(&ctx, 0, sizeof ctx); |
| return rv; |
| } |
| |
| SECStatus |
| BLAKE2B_Hash(unsigned char* dest, const char* src) |
| { |
| return blake2b_HashBuf(dest, (const unsigned char*)src, BLAKE2B512_LENGTH, |
| PORT_Strlen(src), NULL, 0); |
| } |
| |
| SECStatus |
| BLAKE2B_HashBuf(unsigned char* output, const unsigned char* input, PRUint32 inlen) |
| { |
| return blake2b_HashBuf(output, input, BLAKE2B512_LENGTH, inlen, NULL, 0); |
| } |
| |
| SECStatus |
| BLAKE2B_MAC_HashBuf(unsigned char* output, const unsigned char* input, |
| unsigned int inlen, const unsigned char* key, |
| unsigned int keylen) |
| { |
| PORT_Assert(key != NULL); |
| if (!key && keylen <= BLAKE2B_KEY_SIZE) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| return SECFailure; |
| } |
| return blake2b_HashBuf(output, input, BLAKE2B512_LENGTH, inlen, key, keylen); |
| } |
| |
| unsigned int |
| BLAKE2B_FlattenSize(BLAKE2BContext* ctx) |
| { |
| return sizeof(BLAKE2BContext); |
| } |
| |
| SECStatus |
| BLAKE2B_Flatten(BLAKE2BContext* ctx, unsigned char* space) |
| { |
| PORT_Assert(space != NULL); |
| if (!space) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| return SECFailure; |
| } |
| PORT_Memcpy(space, ctx, sizeof(BLAKE2BContext)); |
| return SECSuccess; |
| } |
| |
| BLAKE2BContext* |
| BLAKE2B_Resurrect(unsigned char* space, void* arg) |
| { |
| PORT_Assert(space != NULL); |
| if (!space) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| return NULL; |
| } |
| BLAKE2BContext* ctx = BLAKE2B_NewContext(); |
| if (ctx == NULL) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| return NULL; |
| } |
| |
| PORT_Memcpy(ctx, space, sizeof(BLAKE2BContext)); |
| return ctx; |
| } |
| |
| void |
| BLAKE2B_Clone(BLAKE2BContext* dest, BLAKE2BContext* src) |
| { |
| PORT_Assert(dest != NULL); |
| PORT_Assert(src != NULL); |
| if (!dest || !src) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| return; |
| } |
| PORT_Memcpy(dest, src, sizeof(BLAKE2BContext)); |
| } |