diff --git a/cipher/blake2.c b/cipher/blake2.c index a5926b95..d7f9a7e4 100644 --- a/cipher/blake2.c +++ b/cipher/blake2.c @@ -1,1039 +1,1045 @@ /* blake2.c - BLAKE2b and BLAKE2s hash functions (RFC 7693) * Copyright (C) 2017 Jussi Kivilinna * * This file is part of Libgcrypt. * * Libgcrypt is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser general Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * Libgcrypt is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . */ /* The code is based on public-domain/CC0 BLAKE2 reference implementation * by Samual Neves, at https://github.com/BLAKE2/BLAKE2/tree/master/ref * Copyright 2012, Samuel Neves */ #include #include #include "g10lib.h" #include "bithelp.h" #include "bufhelp.h" #include "cipher.h" #include "hash-common.h" /* USE_AVX indicates whether to compile with Intel AVX code. */ #undef USE_AVX #if defined(__x86_64__) && defined(HAVE_GCC_INLINE_ASM_AVX) && \ (defined(HAVE_COMPATIBLE_GCC_AMD64_PLATFORM_AS) || \ defined(HAVE_COMPATIBLE_GCC_WIN64_PLATFORM_AS)) # define USE_AVX 1 #endif /* USE_AVX2 indicates whether to compile with Intel AVX2 code. */ #undef USE_AVX2 #if defined(__x86_64__) && defined(HAVE_GCC_INLINE_ASM_AVX2) && \ (defined(HAVE_COMPATIBLE_GCC_AMD64_PLATFORM_AS) || \ defined(HAVE_COMPATIBLE_GCC_WIN64_PLATFORM_AS)) # define USE_AVX2 1 #endif /* AMD64 assembly implementations use SystemV ABI, ABI conversion and additional * stack to store XMM6-XMM15 needed on Win64. */ #undef ASM_FUNC_ABI #undef ASM_EXTRA_STACK #if defined(USE_AVX2) && defined(HAVE_COMPATIBLE_GCC_WIN64_PLATFORM_AS) # define ASM_FUNC_ABI __attribute__((sysv_abi)) # define ASM_EXTRA_STACK (10 * 16) #else # define ASM_FUNC_ABI # define ASM_EXTRA_STACK 0 #endif #define BLAKE2B_BLOCKBYTES 128 #define BLAKE2B_OUTBYTES 64 #define BLAKE2B_KEYBYTES 64 #define BLAKE2S_BLOCKBYTES 64 #define BLAKE2S_OUTBYTES 32 #define BLAKE2S_KEYBYTES 32 typedef struct { u64 h[8]; u64 t[2]; u64 f[2]; } BLAKE2B_STATE; struct blake2b_param_s { byte digest_length; byte key_length; byte fanout; byte depth; byte leaf_length[4]; byte node_offset[4]; byte xof_length[4]; byte node_depth; byte inner_length; byte reserved[14]; byte salt[16]; byte personal[16]; }; typedef struct BLAKE2B_CONTEXT_S { BLAKE2B_STATE state; byte buf[BLAKE2B_BLOCKBYTES]; size_t buflen; size_t outlen; #ifdef USE_AVX2 unsigned int use_avx2:1; #endif } BLAKE2B_CONTEXT; typedef struct { u32 h[8]; u32 t[2]; u32 f[2]; } BLAKE2S_STATE; struct blake2s_param_s { byte digest_length; byte key_length; byte fanout; byte depth; byte leaf_length[4]; byte node_offset[4]; byte xof_length[2]; byte node_depth; byte inner_length; /* byte reserved[0]; */ byte salt[8]; byte personal[8]; }; typedef struct BLAKE2S_CONTEXT_S { BLAKE2S_STATE state; byte buf[BLAKE2S_BLOCKBYTES]; size_t buflen; size_t outlen; #ifdef USE_AVX unsigned int use_avx:1; #endif } BLAKE2S_CONTEXT; typedef unsigned int (*blake2_transform_t)(void *S, const void *inblk, size_t nblks); static const u64 blake2b_IV[8] = { U64_C(0x6a09e667f3bcc908), U64_C(0xbb67ae8584caa73b), U64_C(0x3c6ef372fe94f82b), U64_C(0xa54ff53a5f1d36f1), U64_C(0x510e527fade682d1), U64_C(0x9b05688c2b3e6c1f), U64_C(0x1f83d9abfb41bd6b), U64_C(0x5be0cd19137e2179) }; static const u32 blake2s_IV[8] = { 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL, 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL }; static byte zero_block[BLAKE2B_BLOCKBYTES] = { 0, }; static void blake2_write(void *S, const void *inbuf, size_t inlen, byte *tmpbuf, size_t *tmpbuflen, size_t blkbytes, blake2_transform_t transform_fn) { const byte* in = inbuf; unsigned int burn = 0; if (inlen > 0) { size_t left = *tmpbuflen; size_t fill = blkbytes - left; size_t nblks; if (inlen > fill) { if (fill > 0) buf_cpy (tmpbuf + left, in, fill); /* Fill buffer */ left = 0; burn = transform_fn (S, tmpbuf, 1); /* Increment counter + Compress */ in += fill; inlen -= fill; nblks = inlen / blkbytes - !(inlen % blkbytes); if (nblks) { burn = transform_fn(S, in, nblks); in += blkbytes * nblks; inlen -= blkbytes * nblks; } } gcry_assert (inlen > 0); buf_cpy (tmpbuf + left, in, inlen); *tmpbuflen = left + inlen; } if (burn) _gcry_burn_stack (burn); return; } static inline void blake2b_set_lastblock(BLAKE2B_STATE *S) { S->f[0] = U64_C(0xffffffffffffffff); } static inline int blake2b_is_lastblock(const BLAKE2B_STATE *S) { return S->f[0] != 0; } static inline void blake2b_increment_counter(BLAKE2B_STATE *S, const int inc) { S->t[0] += (u64)inc; S->t[1] += (S->t[0] < (u64)inc) - (inc < 0); } static inline u64 rotr64(u64 x, u64 n) { return ((x >> (n & 63)) | (x << ((64 - n) & 63))); } static unsigned int blake2b_transform_generic(BLAKE2B_STATE *S, const void *inblks, size_t nblks) { static const byte blake2b_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 } }; const byte* in = inblks; u64 m[16]; u64 v[16]; while (nblks--) { /* Increment counter */ blake2b_increment_counter (S, BLAKE2B_BLOCKBYTES); /* Compress */ m[0] = buf_get_le64 (in + 0 * sizeof(m[0])); m[1] = buf_get_le64 (in + 1 * sizeof(m[0])); m[2] = buf_get_le64 (in + 2 * sizeof(m[0])); m[3] = buf_get_le64 (in + 3 * sizeof(m[0])); m[4] = buf_get_le64 (in + 4 * sizeof(m[0])); m[5] = buf_get_le64 (in + 5 * sizeof(m[0])); m[6] = buf_get_le64 (in + 6 * sizeof(m[0])); m[7] = buf_get_le64 (in + 7 * sizeof(m[0])); m[8] = buf_get_le64 (in + 8 * sizeof(m[0])); m[9] = buf_get_le64 (in + 9 * sizeof(m[0])); m[10] = buf_get_le64 (in + 10 * sizeof(m[0])); m[11] = buf_get_le64 (in + 11 * sizeof(m[0])); m[12] = buf_get_le64 (in + 12 * sizeof(m[0])); m[13] = buf_get_le64 (in + 13 * sizeof(m[0])); m[14] = buf_get_le64 (in + 14 * sizeof(m[0])); m[15] = buf_get_le64 (in + 15 * sizeof(m[0])); v[ 0] = S->h[0]; v[ 1] = S->h[1]; v[ 2] = S->h[2]; v[ 3] = S->h[3]; v[ 4] = S->h[4]; v[ 5] = S->h[5]; v[ 6] = S->h[6]; v[ 7] = S->h[7]; v[ 8] = blake2b_IV[0]; v[ 9] = blake2b_IV[1]; v[10] = blake2b_IV[2]; v[11] = blake2b_IV[3]; v[12] = blake2b_IV[4] ^ S->t[0]; v[13] = blake2b_IV[5] ^ S->t[1]; v[14] = blake2b_IV[6] ^ S->f[0]; v[15] = blake2b_IV[7] ^ S->f[1]; #define G(r,i,a,b,c,d) \ do { \ a = a + b + m[blake2b_sigma[r][2*i+0]]; \ d = rotr64(d ^ a, 32); \ c = c + d; \ b = rotr64(b ^ c, 24); \ a = a + b + m[blake2b_sigma[r][2*i+1]]; \ d = rotr64(d ^ a, 16); \ c = c + d; \ b = rotr64(b ^ c, 63); \ } while(0) #define ROUND(r) \ do { \ G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \ G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \ G(r,2,v[ 2],v[ 6],v[10],v[14]); \ G(r,3,v[ 3],v[ 7],v[11],v[15]); \ G(r,4,v[ 0],v[ 5],v[10],v[15]); \ G(r,5,v[ 1],v[ 6],v[11],v[12]); \ G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \ G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \ } while(0) ROUND(0); ROUND(1); ROUND(2); ROUND(3); ROUND(4); ROUND(5); ROUND(6); ROUND(7); ROUND(8); ROUND(9); ROUND(10); ROUND(11); #undef G #undef ROUND S->h[0] = S->h[0] ^ v[0] ^ v[0 + 8]; S->h[1] = S->h[1] ^ v[1] ^ v[1 + 8]; S->h[2] = S->h[2] ^ v[2] ^ v[2 + 8]; S->h[3] = S->h[3] ^ v[3] ^ v[3 + 8]; S->h[4] = S->h[4] ^ v[4] ^ v[4 + 8]; S->h[5] = S->h[5] ^ v[5] ^ v[5 + 8]; S->h[6] = S->h[6] ^ v[6] ^ v[6 + 8]; S->h[7] = S->h[7] ^ v[7] ^ v[7 + 8]; in += BLAKE2B_BLOCKBYTES; } return sizeof(void *) * 4 + sizeof(u64) * 16 * 2; } #ifdef USE_AVX2 unsigned int _gcry_blake2b_transform_amd64_avx2(BLAKE2B_STATE *S, const void *inblks, size_t nblks) ASM_FUNC_ABI; #endif static unsigned int blake2b_transform(void *ctx, const void *inblks, size_t nblks) { BLAKE2B_CONTEXT *c = ctx; unsigned int nburn; if (0) {} #ifdef USE_AVX2 if (c->use_avx2) nburn = _gcry_blake2b_transform_amd64_avx2(&c->state, inblks, nblks); #endif else nburn = blake2b_transform_generic(&c->state, inblks, nblks); if (nburn) nburn += ASM_EXTRA_STACK; return nburn; } static void blake2b_final(void *ctx) { BLAKE2B_CONTEXT *c = ctx; BLAKE2B_STATE *S = &c->state; unsigned int burn; size_t i; gcry_assert (sizeof(c->buf) >= c->outlen); if (blake2b_is_lastblock(S)) return; if (c->buflen < BLAKE2B_BLOCKBYTES) memset (c->buf + c->buflen, 0, BLAKE2B_BLOCKBYTES - c->buflen); /* Padding */ blake2b_set_lastblock (S); blake2b_increment_counter (S, (int)c->buflen - BLAKE2B_BLOCKBYTES); burn = blake2b_transform (ctx, c->buf, 1); /* Output full hash to buffer */ for (i = 0; i < 8; ++i) buf_put_le64 (c->buf + sizeof(S->h[i]) * i, S->h[i]); /* Zero out extra buffer bytes. */ if (c->outlen < sizeof(c->buf)) memset (c->buf + c->outlen, 0, sizeof(c->buf) - c->outlen); if (burn) _gcry_burn_stack (burn); } static byte *blake2b_read(void *ctx) { BLAKE2B_CONTEXT *c = ctx; return c->buf; } static void blake2b_write(void *ctx, const void *inbuf, size_t inlen) { BLAKE2B_CONTEXT *c = ctx; BLAKE2B_STATE *S = &c->state; blake2_write(S, inbuf, inlen, c->buf, &c->buflen, BLAKE2B_BLOCKBYTES, blake2b_transform); } static inline void blake2b_init_param(BLAKE2B_STATE *S, const struct blake2b_param_s *P) { const byte *p = (const byte *)P; size_t i; /* init xors IV with input parameter block */ /* IV XOR ParamBlock */ for (i = 0; i < 8; ++i) S->h[i] = blake2b_IV[i] ^ buf_get_le64(p + sizeof(S->h[i]) * i); } static inline gcry_err_code_t blake2b_init(BLAKE2B_CONTEXT *ctx, const byte *key, size_t keylen) { struct blake2b_param_s P[1] = { { 0, } }; BLAKE2B_STATE *S = &ctx->state; if (!ctx->outlen || ctx->outlen > BLAKE2B_OUTBYTES) return GPG_ERR_INV_ARG; if (sizeof(P[0]) != sizeof(u64) * 8) return GPG_ERR_INTERNAL; if (keylen && (!key || keylen > BLAKE2B_KEYBYTES)) return GPG_ERR_INV_KEYLEN; P->digest_length = ctx->outlen; P->key_length = keylen; P->fanout = 1; P->depth = 1; blake2b_init_param (S, P); wipememory (P, sizeof(P)); if (key) { blake2b_write (ctx, key, keylen); blake2b_write (ctx, zero_block, BLAKE2B_BLOCKBYTES - keylen); } return 0; } static gcry_err_code_t blake2b_init_ctx(void *ctx, unsigned int flags, const byte *key, size_t keylen, unsigned int dbits) { BLAKE2B_CONTEXT *c = ctx; unsigned int features = _gcry_get_hw_features (); (void)features; (void)flags; memset (c, 0, sizeof (*c)); #ifdef USE_AVX2 c->use_avx2 = !!(features & HWF_INTEL_AVX2); #endif c->outlen = dbits / 8; c->buflen = 0; return blake2b_init(c, key, keylen); } /* Variable-length Hash Function H'. */ gcry_err_code_t blake2b_vl_hash (const void *in, size_t inlen, size_t outputlen, void *output) { gcry_err_code_t ec; BLAKE2B_CONTEXT ctx; unsigned char buf[4]; ec = blake2b_init_ctx (&ctx, 0, NULL, 0, (outputlen < 64 ? outputlen: 64)*8); if (ec) return ec; buf_put_le32 (buf, outputlen); blake2b_write (&ctx, buf, 4); blake2b_write (&ctx, in, inlen); blake2b_final (&ctx); if (outputlen <= 64) memcpy (output, ctx.buf, outputlen); else { - int r = (outputlen-1)/32; + int r = (outputlen-1)/32 - 1; unsigned int remained = outputlen - 32*r; int i; unsigned char d[64]; i = 0; while (1) { memcpy (d, ctx.buf, 64); memcpy ((unsigned char *)output+i*32, d, 32); if (++i >= r) break; ec = blake2b_init_ctx (&ctx, 0, NULL, 0, 64*8); if (ec) return ec; blake2b_write (&ctx, d, 64); blake2b_final (&ctx); } - if (remained) - memcpy ((unsigned char *)output+r*32, d+32, remained); + ec = blake2b_init_ctx (&ctx, 0, NULL, 0, remained*8); + if (ec) + return ec; + + blake2b_write (&ctx, d, 64); + blake2b_final (&ctx); + + memcpy ((unsigned char *)output+r*32, ctx.buf, remained); } wipememory (buf, sizeof (buf)); wipememory (&ctx, sizeof (ctx)); return 0; } static inline void blake2s_set_lastblock(BLAKE2S_STATE *S) { S->f[0] = 0xFFFFFFFFUL; } static inline int blake2s_is_lastblock(BLAKE2S_STATE *S) { return S->f[0] != 0; } static inline void blake2s_increment_counter(BLAKE2S_STATE *S, const int inc) { S->t[0] += (u32)inc; S->t[1] += (S->t[0] < (u32)inc) - (inc < 0); } static unsigned int blake2s_transform_generic(BLAKE2S_STATE *S, const void *inblks, size_t nblks) { static const byte blake2s_sigma[10][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 }, }; unsigned int burn = 0; const byte* in = inblks; u32 m[16]; u32 v[16]; while (nblks--) { /* Increment counter */ blake2s_increment_counter (S, BLAKE2S_BLOCKBYTES); /* Compress */ m[0] = buf_get_le32 (in + 0 * sizeof(m[0])); m[1] = buf_get_le32 (in + 1 * sizeof(m[0])); m[2] = buf_get_le32 (in + 2 * sizeof(m[0])); m[3] = buf_get_le32 (in + 3 * sizeof(m[0])); m[4] = buf_get_le32 (in + 4 * sizeof(m[0])); m[5] = buf_get_le32 (in + 5 * sizeof(m[0])); m[6] = buf_get_le32 (in + 6 * sizeof(m[0])); m[7] = buf_get_le32 (in + 7 * sizeof(m[0])); m[8] = buf_get_le32 (in + 8 * sizeof(m[0])); m[9] = buf_get_le32 (in + 9 * sizeof(m[0])); m[10] = buf_get_le32 (in + 10 * sizeof(m[0])); m[11] = buf_get_le32 (in + 11 * sizeof(m[0])); m[12] = buf_get_le32 (in + 12 * sizeof(m[0])); m[13] = buf_get_le32 (in + 13 * sizeof(m[0])); m[14] = buf_get_le32 (in + 14 * sizeof(m[0])); m[15] = buf_get_le32 (in + 15 * sizeof(m[0])); v[ 0] = S->h[0]; v[ 1] = S->h[1]; v[ 2] = S->h[2]; v[ 3] = S->h[3]; v[ 4] = S->h[4]; v[ 5] = S->h[5]; v[ 6] = S->h[6]; v[ 7] = S->h[7]; v[ 8] = blake2s_IV[0]; v[ 9] = blake2s_IV[1]; v[10] = blake2s_IV[2]; v[11] = blake2s_IV[3]; v[12] = S->t[0] ^ blake2s_IV[4]; v[13] = S->t[1] ^ blake2s_IV[5]; v[14] = S->f[0] ^ blake2s_IV[6]; v[15] = S->f[1] ^ blake2s_IV[7]; #define G(r,i,a,b,c,d) \ do { \ a = a + b + m[blake2s_sigma[r][2*i+0]]; \ d = ror(d ^ a, 16); \ c = c + d; \ b = ror(b ^ c, 12); \ a = a + b + m[blake2s_sigma[r][2*i+1]]; \ d = ror(d ^ a, 8); \ c = c + d; \ b = ror(b ^ c, 7); \ } while(0) #define ROUND(r) \ do { \ G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \ G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \ G(r,2,v[ 2],v[ 6],v[10],v[14]); \ G(r,3,v[ 3],v[ 7],v[11],v[15]); \ G(r,4,v[ 0],v[ 5],v[10],v[15]); \ G(r,5,v[ 1],v[ 6],v[11],v[12]); \ G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \ G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \ } while(0) ROUND(0); ROUND(1); ROUND(2); ROUND(3); ROUND(4); ROUND(5); ROUND(6); ROUND(7); ROUND(8); ROUND(9); #undef G #undef ROUND S->h[0] = S->h[0] ^ v[0] ^ v[0 + 8]; S->h[1] = S->h[1] ^ v[1] ^ v[1 + 8]; S->h[2] = S->h[2] ^ v[2] ^ v[2 + 8]; S->h[3] = S->h[3] ^ v[3] ^ v[3 + 8]; S->h[4] = S->h[4] ^ v[4] ^ v[4 + 8]; S->h[5] = S->h[5] ^ v[5] ^ v[5 + 8]; S->h[6] = S->h[6] ^ v[6] ^ v[6 + 8]; S->h[7] = S->h[7] ^ v[7] ^ v[7 + 8]; in += BLAKE2S_BLOCKBYTES; } return burn; } #ifdef USE_AVX unsigned int _gcry_blake2s_transform_amd64_avx(BLAKE2S_STATE *S, const void *inblks, size_t nblks) ASM_FUNC_ABI; #endif static unsigned int blake2s_transform(void *ctx, const void *inblks, size_t nblks) { BLAKE2S_CONTEXT *c = ctx; unsigned int nburn; if (0) {} #ifdef USE_AVX if (c->use_avx) nburn = _gcry_blake2s_transform_amd64_avx(&c->state, inblks, nblks); #endif else nburn = blake2s_transform_generic(&c->state, inblks, nblks); if (nburn) nburn += ASM_EXTRA_STACK; return nburn; } static void blake2s_final(void *ctx) { BLAKE2S_CONTEXT *c = ctx; BLAKE2S_STATE *S = &c->state; unsigned int burn; size_t i; gcry_assert (sizeof(c->buf) >= c->outlen); if (blake2s_is_lastblock(S)) return; if (c->buflen < BLAKE2S_BLOCKBYTES) memset (c->buf + c->buflen, 0, BLAKE2S_BLOCKBYTES - c->buflen); /* Padding */ blake2s_set_lastblock (S); blake2s_increment_counter (S, (int)c->buflen - BLAKE2S_BLOCKBYTES); burn = blake2s_transform (ctx, c->buf, 1); /* Output full hash to buffer */ for (i = 0; i < 8; ++i) buf_put_le32 (c->buf + sizeof(S->h[i]) * i, S->h[i]); /* Zero out extra buffer bytes. */ if (c->outlen < sizeof(c->buf)) memset (c->buf + c->outlen, 0, sizeof(c->buf) - c->outlen); if (burn) _gcry_burn_stack (burn); } static byte *blake2s_read(void *ctx) { BLAKE2S_CONTEXT *c = ctx; return c->buf; } static void blake2s_write(void *ctx, const void *inbuf, size_t inlen) { BLAKE2S_CONTEXT *c = ctx; BLAKE2S_STATE *S = &c->state; blake2_write(S, inbuf, inlen, c->buf, &c->buflen, BLAKE2S_BLOCKBYTES, blake2s_transform); } static inline void blake2s_init_param(BLAKE2S_STATE *S, const struct blake2s_param_s *P) { const byte *p = (const byte *)P; size_t i; /* init2 xors IV with input parameter block */ /* IV XOR ParamBlock */ for (i = 0; i < 8; ++i) S->h[i] ^= blake2s_IV[i] ^ buf_get_le32(&p[i * 4]); } static inline gcry_err_code_t blake2s_init(BLAKE2S_CONTEXT *ctx, const byte *key, size_t keylen) { struct blake2s_param_s P[1] = { { 0, } }; BLAKE2S_STATE *S = &ctx->state; if (!ctx->outlen || ctx->outlen > BLAKE2S_OUTBYTES) return GPG_ERR_INV_ARG; if (sizeof(P[0]) != sizeof(u32) * 8) return GPG_ERR_INTERNAL; if (keylen && (!key || keylen > BLAKE2S_KEYBYTES)) return GPG_ERR_INV_KEYLEN; P->digest_length = ctx->outlen; P->key_length = keylen; P->fanout = 1; P->depth = 1; blake2s_init_param (S, P); wipememory (P, sizeof(P)); if (key) { blake2s_write (ctx, key, keylen); blake2s_write (ctx, zero_block, BLAKE2S_BLOCKBYTES - keylen); } return 0; } static gcry_err_code_t blake2s_init_ctx(void *ctx, unsigned int flags, const byte *key, size_t keylen, unsigned int dbits) { BLAKE2S_CONTEXT *c = ctx; unsigned int features = _gcry_get_hw_features (); (void)features; (void)flags; memset (c, 0, sizeof (*c)); #ifdef USE_AVX c->use_avx = !!(features & HWF_INTEL_AVX); #endif c->outlen = dbits / 8; c->buflen = 0; return blake2s_init(c, key, keylen); } /* Selftests from "RFC 7693, Appendix E. BLAKE2b and BLAKE2s Self-Test * Module C Source". */ static void selftest_seq(byte *out, size_t len, u32 seed) { size_t i; u32 t, a, b; a = 0xDEAD4BAD * seed; b = 1; for (i = 0; i < len; i++) { t = a + b; a = b; b = t; out[i] = (t >> 24) & 0xFF; } } static gpg_err_code_t selftests_blake2b (int algo, int extended, selftest_report_func_t report) { static const byte blake2b_res[32] = { 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 }; static const size_t b2b_md_len[4] = { 20, 32, 48, 64 }; static const size_t b2b_in_len[6] = { 0, 3, 128, 129, 255, 1024 }; size_t i, j, outlen, inlen; byte in[1024], key[64]; BLAKE2B_CONTEXT ctx; BLAKE2B_CONTEXT ctx2; const char *what; const char *errtxt; (void)extended; what = "rfc7693 BLAKE2b selftest"; /* 256-bit hash for testing */ if (blake2b_init_ctx(&ctx, 0, NULL, 0, 32 * 8)) { errtxt = "init failed"; goto failed; } for (i = 0; i < 4; i++) { outlen = b2b_md_len[i]; for (j = 0; j < 6; j++) { inlen = b2b_in_len[j]; selftest_seq(in, inlen, inlen); /* unkeyed hash */ blake2b_init_ctx(&ctx2, 0, NULL, 0, outlen * 8); blake2b_write(&ctx2, in, inlen); blake2b_final(&ctx2); blake2b_write(&ctx, ctx2.buf, outlen); /* hash the hash */ selftest_seq(key, outlen, outlen); /* keyed hash */ blake2b_init_ctx(&ctx2, 0, key, outlen, outlen * 8); blake2b_write(&ctx2, in, inlen); blake2b_final(&ctx2); blake2b_write(&ctx, ctx2.buf, outlen); /* hash the hash */ } } /* compute and compare the hash of hashes */ blake2b_final(&ctx); for (i = 0; i < 32; i++) { if (ctx.buf[i] != blake2b_res[i]) { errtxt = "digest mismatch"; goto failed; } } return 0; failed: if (report) report ("digest", algo, what, errtxt); return GPG_ERR_SELFTEST_FAILED; } static gpg_err_code_t selftests_blake2s (int algo, int extended, selftest_report_func_t report) { static const byte blake2s_res[32] = { 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 }; static const size_t b2s_md_len[4] = { 16, 20, 28, 32 }; static const size_t b2s_in_len[6] = { 0, 3, 64, 65, 255, 1024 }; size_t i, j, outlen, inlen; byte in[1024], key[32]; BLAKE2S_CONTEXT ctx; BLAKE2S_CONTEXT ctx2; const char *what; const char *errtxt; (void)extended; what = "rfc7693 BLAKE2s selftest"; /* 256-bit hash for testing */ if (blake2s_init_ctx(&ctx, 0, NULL, 0, 32 * 8)) { errtxt = "init failed"; goto failed; } for (i = 0; i < 4; i++) { outlen = b2s_md_len[i]; for (j = 0; j < 6; j++) { inlen = b2s_in_len[j]; selftest_seq(in, inlen, inlen); /* unkeyed hash */ blake2s_init_ctx(&ctx2, 0, NULL, 0, outlen * 8); blake2s_write(&ctx2, in, inlen); blake2s_final(&ctx2); blake2s_write(&ctx, ctx2.buf, outlen); /* hash the hash */ selftest_seq(key, outlen, outlen); /* keyed hash */ blake2s_init_ctx(&ctx2, 0, key, outlen, outlen * 8); blake2s_write(&ctx2, in, inlen); blake2s_final(&ctx2); blake2s_write(&ctx, ctx2.buf, outlen); /* hash the hash */ } } /* compute and compare the hash of hashes */ blake2s_final(&ctx); for (i = 0; i < 32; i++) { if (ctx.buf[i] != blake2s_res[i]) { errtxt = "digest mismatch"; goto failed; } } return 0; failed: if (report) report ("digest", algo, what, errtxt); return GPG_ERR_SELFTEST_FAILED; } gcry_err_code_t _gcry_blake2_init_with_key(void *ctx, unsigned int flags, const unsigned char *key, size_t keylen, int algo) { gcry_err_code_t rc; switch (algo) { case GCRY_MD_BLAKE2B_512: rc = blake2b_init_ctx (ctx, flags, key, keylen, 512); break; case GCRY_MD_BLAKE2B_384: rc = blake2b_init_ctx (ctx, flags, key, keylen, 384); break; case GCRY_MD_BLAKE2B_256: rc = blake2b_init_ctx (ctx, flags, key, keylen, 256); break; case GCRY_MD_BLAKE2B_160: rc = blake2b_init_ctx (ctx, flags, key, keylen, 160); break; case GCRY_MD_BLAKE2S_256: rc = blake2s_init_ctx (ctx, flags, key, keylen, 256); break; case GCRY_MD_BLAKE2S_224: rc = blake2s_init_ctx (ctx, flags, key, keylen, 224); break; case GCRY_MD_BLAKE2S_160: rc = blake2s_init_ctx (ctx, flags, key, keylen, 160); break; case GCRY_MD_BLAKE2S_128: rc = blake2s_init_ctx (ctx, flags, key, keylen, 128); break; default: rc = GPG_ERR_DIGEST_ALGO; break; } return rc; } #define DEFINE_BLAKE2_VARIANT(bs, BS, dbits, oid_branch) \ static void blake2##bs##_##dbits##_init(void *ctx, unsigned int flags) \ { \ int err = blake2##bs##_init_ctx (ctx, flags, NULL, 0, dbits); \ gcry_assert (err == 0); \ } \ static void \ _gcry_blake2##bs##_##dbits##_hash_buffers(void *outbuf, size_t nbytes, \ const gcry_buffer_t *iov, int iovcnt) \ { \ BLAKE2##BS##_CONTEXT hd; \ (void)nbytes; \ blake2##bs##_##dbits##_init (&hd, 0); \ for (;iovcnt > 0; iov++, iovcnt--) \ blake2##bs##_write (&hd, (const char*)iov[0].data + iov[0].off, \ iov[0].len); \ blake2##bs##_final (&hd); \ memcpy (outbuf, blake2##bs##_read (&hd), dbits / 8); \ } \ static const byte blake2##bs##_##dbits##_asn[] = { 0x30 }; \ static const gcry_md_oid_spec_t oid_spec_blake2##bs##_##dbits[] = \ { \ { " 1.3.6.1.4.1.1722.12.2." oid_branch }, \ { NULL } \ }; \ const gcry_md_spec_t _gcry_digest_spec_blake2##bs##_##dbits = \ { \ GCRY_MD_BLAKE2##BS##_##dbits, {0, 0}, \ "BLAKE2" #BS "_" #dbits, blake2##bs##_##dbits##_asn, \ DIM (blake2##bs##_##dbits##_asn), oid_spec_blake2##bs##_##dbits, \ dbits / 8, blake2##bs##_##dbits##_init, blake2##bs##_write, \ blake2##bs##_final, blake2##bs##_read, NULL, \ _gcry_blake2##bs##_##dbits##_hash_buffers, \ sizeof (BLAKE2##BS##_CONTEXT), selftests_blake2##bs \ }; DEFINE_BLAKE2_VARIANT(b, B, 512, "1.16") DEFINE_BLAKE2_VARIANT(b, B, 384, "1.12") DEFINE_BLAKE2_VARIANT(b, B, 256, "1.8") DEFINE_BLAKE2_VARIANT(b, B, 160, "1.5") DEFINE_BLAKE2_VARIANT(s, S, 256, "2.8") DEFINE_BLAKE2_VARIANT(s, S, 224, "2.7") DEFINE_BLAKE2_VARIANT(s, S, 160, "2.5") DEFINE_BLAKE2_VARIANT(s, S, 128, "2.4") diff --git a/cipher/kdf.c b/cipher/kdf.c index 3a47bed8..0e196432 100644 --- a/cipher/kdf.c +++ b/cipher/kdf.c @@ -1,1692 +1,1695 @@ /* kdf.c - Key Derivation Functions * Copyright (C) 1998, 2008, 2011 Free Software Foundation, Inc. * Copyright (C) 2013 g10 Code GmbH * * This file is part of Libgcrypt. * * Libgcrypt is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser general Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * Libgcrypt is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . */ #include #include #include #include #include #include "g10lib.h" #include "cipher.h" #include "kdf-internal.h" /* Transform a passphrase into a suitable key of length KEYSIZE and store this key in the caller provided buffer KEYBUFFER. The caller must provide an HASHALGO, a valid ALGO and depending on that algo a SALT of 8 bytes and the number of ITERATIONS. Code taken from gnupg/agent/protect.c:hash_passphrase. */ static gpg_err_code_t openpgp_s2k (const void *passphrase, size_t passphraselen, int algo, int hashalgo, const void *salt, size_t saltlen, unsigned long iterations, size_t keysize, void *keybuffer) { gpg_err_code_t ec; gcry_md_hd_t md; char *key = keybuffer; int pass, i; int used = 0; int secmode; if ((algo == GCRY_KDF_SALTED_S2K || algo == GCRY_KDF_ITERSALTED_S2K) && (!salt || saltlen != 8)) return GPG_ERR_INV_VALUE; secmode = _gcry_is_secure (passphrase) || _gcry_is_secure (keybuffer); ec = _gcry_md_open (&md, hashalgo, secmode? GCRY_MD_FLAG_SECURE : 0); if (ec) return ec; for (pass=0; used < keysize; pass++) { if (pass) { _gcry_md_reset (md); for (i=0; i < pass; i++) /* Preset the hash context. */ _gcry_md_putc (md, 0); } if (algo == GCRY_KDF_SALTED_S2K || algo == GCRY_KDF_ITERSALTED_S2K) { int len2 = passphraselen + 8; unsigned long count = len2; if (algo == GCRY_KDF_ITERSALTED_S2K) { count = iterations; if (count < len2) count = len2; } while (count > len2) { _gcry_md_write (md, salt, saltlen); _gcry_md_write (md, passphrase, passphraselen); count -= len2; } if (count < saltlen) _gcry_md_write (md, salt, count); else { _gcry_md_write (md, salt, saltlen); count -= saltlen; _gcry_md_write (md, passphrase, count); } } else _gcry_md_write (md, passphrase, passphraselen); _gcry_md_final (md); i = _gcry_md_get_algo_dlen (hashalgo); if (i > keysize - used) i = keysize - used; memcpy (key+used, _gcry_md_read (md, hashalgo), i); used += i; } _gcry_md_close (md); return 0; } /* Transform a passphrase into a suitable key of length KEYSIZE and store this key in the caller provided buffer KEYBUFFER. The caller must provide PRFALGO which indicates the pseudorandom function to use: This shall be the algorithms id of a hash algorithm; it is used in HMAC mode. SALT is a salt of length SALTLEN and ITERATIONS gives the number of iterations. */ gpg_err_code_t _gcry_kdf_pkdf2 (const void *passphrase, size_t passphraselen, int hashalgo, const void *salt, size_t saltlen, unsigned long iterations, size_t keysize, void *keybuffer) { gpg_err_code_t ec; gcry_md_hd_t md; int secmode; unsigned long dklen = keysize; char *dk = keybuffer; unsigned int hlen; /* Output length of the digest function. */ unsigned int l; /* Rounded up number of blocks. */ unsigned int r; /* Number of octets in the last block. */ char *sbuf; /* Malloced buffer to concatenate salt and iter as well as space to hold TBUF and UBUF. */ char *tbuf; /* Buffer for T; ptr into SBUF, size is HLEN. */ char *ubuf; /* Buffer for U; ptr into SBUF, size is HLEN. */ unsigned int lidx; /* Current block number. */ unsigned long iter; /* Current iteration number. */ unsigned int i; /* We allow for a saltlen of 0 here to support scrypt. It is not clear whether rfc2898 allows for this this, thus we do a test on saltlen > 0 only in gcry_kdf_derive. */ if (!salt || !iterations || !dklen) return GPG_ERR_INV_VALUE; hlen = _gcry_md_get_algo_dlen (hashalgo); if (!hlen) return GPG_ERR_DIGEST_ALGO; secmode = _gcry_is_secure (passphrase) || _gcry_is_secure (keybuffer); /* Step 1 */ /* If dkLen > (2^32 - 1) * hLen, output "derived key too long" and * stop. We use a stronger inequality but only if our type can hold * a larger value. */ #if SIZEOF_UNSIGNED_LONG > 4 if (dklen > 0xffffffffU) return GPG_ERR_INV_VALUE; #endif /* Step 2 */ l = ((dklen - 1)/ hlen) + 1; r = dklen - (l - 1) * hlen; /* Setup buffers and prepare a hash context. */ sbuf = (secmode ? xtrymalloc_secure (saltlen + 4 + hlen + hlen) : xtrymalloc (saltlen + 4 + hlen + hlen)); if (!sbuf) return gpg_err_code_from_syserror (); tbuf = sbuf + saltlen + 4; ubuf = tbuf + hlen; ec = _gcry_md_open (&md, hashalgo, (GCRY_MD_FLAG_HMAC | (secmode?GCRY_MD_FLAG_SECURE:0))); if (ec) { xfree (sbuf); return ec; } ec = _gcry_md_setkey (md, passphrase, passphraselen); if (ec) { _gcry_md_close (md); xfree (sbuf); return ec; } /* Step 3 and 4. */ memcpy (sbuf, salt, saltlen); for (lidx = 1; lidx <= l; lidx++) { for (iter = 0; iter < iterations; iter++) { _gcry_md_reset (md); if (!iter) /* Compute U_1: */ { sbuf[saltlen] = (lidx >> 24); sbuf[saltlen + 1] = (lidx >> 16); sbuf[saltlen + 2] = (lidx >> 8); sbuf[saltlen + 3] = lidx; _gcry_md_write (md, sbuf, saltlen + 4); memcpy (ubuf, _gcry_md_read (md, 0), hlen); memcpy (tbuf, ubuf, hlen); } else /* Compute U_(2..c): */ { _gcry_md_write (md, ubuf, hlen); memcpy (ubuf, _gcry_md_read (md, 0), hlen); for (i=0; i < hlen; i++) tbuf[i] ^= ubuf[i]; } } if (lidx == l) /* Last block. */ memcpy (dk, tbuf, r); else { memcpy (dk, tbuf, hlen); dk += hlen; } } _gcry_md_close (md); xfree (sbuf); return 0; } /* Derive a key from a passphrase. KEYSIZE gives the requested size of the keys in octets. KEYBUFFER is a caller provided buffer filled on success with the derived key. The input passphrase is taken from (PASSPHRASE,PASSPHRASELEN) which is an arbitrary memory buffer. ALGO specifies the KDF algorithm to use; these are the constants GCRY_KDF_*. SUBALGO specifies an algorithm used internally by the KDF algorithms; this is usually a hash algorithm but certain KDF algorithm may use it differently. {SALT,SALTLEN} is a salt as needed by most KDF algorithms. ITERATIONS is a positive integer parameter to most KDFs. 0 is returned on success, or an error code on failure. */ gpg_err_code_t _gcry_kdf_derive (const void *passphrase, size_t passphraselen, int algo, int subalgo, const void *salt, size_t saltlen, unsigned long iterations, size_t keysize, void *keybuffer) { gpg_err_code_t ec; if (!passphrase) { ec = GPG_ERR_INV_DATA; goto leave; } if (!keybuffer || !keysize) { ec = GPG_ERR_INV_VALUE; goto leave; } switch (algo) { case GCRY_KDF_SIMPLE_S2K: case GCRY_KDF_SALTED_S2K: case GCRY_KDF_ITERSALTED_S2K: if (!passphraselen) ec = GPG_ERR_INV_DATA; else ec = openpgp_s2k (passphrase, passphraselen, algo, subalgo, salt, saltlen, iterations, keysize, keybuffer); break; case GCRY_KDF_PBKDF1: ec = GPG_ERR_UNSUPPORTED_ALGORITHM; break; case GCRY_KDF_PBKDF2: if (!saltlen) ec = GPG_ERR_INV_VALUE; else ec = _gcry_kdf_pkdf2 (passphrase, passphraselen, subalgo, salt, saltlen, iterations, keysize, keybuffer); break; case 41: case GCRY_KDF_SCRYPT: #if USE_SCRYPT ec = _gcry_kdf_scrypt (passphrase, passphraselen, algo, subalgo, salt, saltlen, iterations, keysize, keybuffer); #else ec = GPG_ERR_UNSUPPORTED_ALGORITHM; #endif /*USE_SCRYPT*/ break; default: ec = GPG_ERR_UNKNOWN_ALGORITHM; break; } leave: return ec; } #include "bufhelp.h" typedef struct argon2_context *argon2_ctx_t; /* Per thread data for Argon2. */ struct argon2_thread_data { argon2_ctx_t a; unsigned int pass; unsigned int slice; unsigned int lane; }; /* Argon2 context */ struct argon2_context { int algo; int hash_type; unsigned int outlen; const unsigned char *password; size_t passwordlen; const unsigned char *salt; size_t saltlen; const unsigned char *key; size_t keylen; const unsigned char *ad; size_t adlen; unsigned int m_cost; unsigned int passes; unsigned int memory_blocks; unsigned int segment_length; unsigned int lane_length; unsigned int lanes; u64 *block; struct argon2_thread_data *thread_data; unsigned char out[1]; /* In future, we may use flexible array member. */ }; #define ARGON2_VERSION 0x13 #define ARGON2_WORDS_IN_BLOCK (1024/8) static void xor_block (u64 *dst, const u64 *src) { int i; for (i = 0; i < ARGON2_WORDS_IN_BLOCK; i++) dst[i] ^= src[i]; } static void beswap64_block (u64 *dst) { #ifdef WORDS_BIGENDIAN int i; /* Swap a block in big-endian 64-bit word into one in little-endian. */ for (i = 0; i < ARGON2_WORDS_IN_BLOCK; i++) dst[i] = _gcry_bswap64 (dst[i]); #else /* Nothing to do. */ (void)dst; #endif } static gpg_err_code_t argon2_fill_first_blocks (argon2_ctx_t a) { unsigned char h0_01_i[72]; unsigned char buf[10][4]; gcry_buffer_t iov[8]; unsigned int iov_count = 0; int i; /* Generate H0. */ buf_put_le32 (buf[0], a->lanes); buf_put_le32 (buf[1], a->outlen); buf_put_le32 (buf[2], a->m_cost); buf_put_le32 (buf[3], a->passes); buf_put_le32 (buf[4], ARGON2_VERSION); buf_put_le32 (buf[5], a->hash_type); buf_put_le32 (buf[6], a->passwordlen); iov[iov_count].data = buf[0]; iov[iov_count].len = 4 * 7; iov[iov_count].off = 0; iov_count++; iov[iov_count].data = (void *)a->password; iov[iov_count].len = a->passwordlen; iov[iov_count].off = 0; iov_count++; buf_put_le32 (buf[7], a->saltlen); iov[iov_count].data = buf[7]; iov[iov_count].len = 4; iov[iov_count].off = 0; iov_count++; iov[iov_count].data = (void *)a->salt; iov[iov_count].len = a->saltlen; iov[iov_count].off = 0; iov_count++; buf_put_le32 (buf[8], a->keylen); iov[iov_count].data = buf[8]; iov[iov_count].len = 4; iov[iov_count].off = 0; iov_count++; if (a->key) { iov[iov_count].data = (void *)a->key; iov[iov_count].len = a->keylen; iov[iov_count].off = 0; iov_count++; } buf_put_le32 (buf[9], a->adlen); iov[iov_count].data = buf[9]; iov[iov_count].len = 4; iov[iov_count].off = 0; iov_count++; if (a->ad) { iov[iov_count].data = (void *)a->ad; iov[iov_count].len = a->adlen; iov[iov_count].off = 0; iov_count++; } _gcry_digest_spec_blake2b_512.hash_buffers (h0_01_i, 64, iov, iov_count); for (i = 0; i < a->lanes; i++) { memset (h0_01_i+64, 0, 4); buf_put_le32 (h0_01_i+64+4, i); blake2b_vl_hash (h0_01_i, 72, 1024, &a->block[i*a->lane_length*ARGON2_WORDS_IN_BLOCK]); beswap64_block (&a->block[i*a->lane_length*ARGON2_WORDS_IN_BLOCK]); buf_put_le32 (h0_01_i+64, 1); blake2b_vl_hash (h0_01_i, 72, 1024, &a->block[(i*a->lane_length+1)*ARGON2_WORDS_IN_BLOCK]); beswap64_block (&a->block[(i*a->lane_length+1)*ARGON2_WORDS_IN_BLOCK]); } return 0; } static gpg_err_code_t argon2_init (argon2_ctx_t a, unsigned int parallelism, unsigned int m_cost, unsigned int t_cost) { gpg_err_code_t ec = 0; unsigned int memory_blocks; unsigned int segment_length; void *block; struct argon2_thread_data *thread_data; memory_blocks = m_cost; if (memory_blocks < 8 * parallelism) memory_blocks = 8 * parallelism; segment_length = memory_blocks / (parallelism * 4); memory_blocks = segment_length * parallelism * 4; a->passes = t_cost; a->memory_blocks = memory_blocks; a->segment_length = segment_length; a->lane_length = segment_length * 4; a->lanes = parallelism; a->block = NULL; a->thread_data = NULL; block = xtrymalloc (1024 * memory_blocks); if (!block) { ec = gpg_err_code_from_errno (errno); return ec; } memset (block, 0, 1024 * memory_blocks); thread_data = xtrymalloc (a->lanes * sizeof (struct argon2_thread_data)); if (!thread_data) { ec = gpg_err_code_from_errno (errno); xfree (block); return ec; } memset (thread_data, 0, a->lanes * sizeof (struct argon2_thread_data)); a->block = block; a->thread_data = thread_data; return 0; } static u64 fBlaMka (u64 x, u64 y) { const u64 m = U64_C(0xFFFFFFFF); return x + y + 2 * (x & m) * (y & m); } static u64 rotr64 (u64 w, unsigned int c) { return (w >> c) | (w << (64 - c)); } #define G(a, b, c, d) \ do { \ a = fBlaMka(a, b); \ d = rotr64(d ^ a, 32); \ c = fBlaMka(c, d); \ b = rotr64(b ^ c, 24); \ a = fBlaMka(a, b); \ d = rotr64(d ^ a, 16); \ c = fBlaMka(c, d); \ b = rotr64(b ^ c, 63); \ } while ((void)0, 0) #define BLAKE2_ROUND_NOMSG(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, \ v12, v13, v14, v15) \ do { \ G(v0, v4, v8, v12); \ G(v1, v5, v9, v13); \ G(v2, v6, v10, v14); \ G(v3, v7, v11, v15); \ G(v0, v5, v10, v15); \ G(v1, v6, v11, v12); \ G(v2, v7, v8, v13); \ G(v3, v4, v9, v14); \ } while ((void)0, 0) static void fill_block (const u64 *prev_block, const u64 *ref_block, u64 *curr_block, int with_xor) { u64 block_r[ARGON2_WORDS_IN_BLOCK]; u64 block_tmp[ARGON2_WORDS_IN_BLOCK]; int i; memcpy (block_r, ref_block, 1024); if (prev_block) xor_block (block_r, prev_block); memcpy (block_tmp, block_r, 1024); if (with_xor) xor_block (block_tmp, curr_block); for (i = 0; i < 8; ++i) BLAKE2_ROUND_NOMSG (block_r[16 * i], block_r[16 * i + 1], block_r[16 * i + 2], block_r[16 * i + 3], block_r[16 * i + 4], block_r[16 * i + 5], block_r[16 * i + 6], block_r[16 * i + 7], block_r[16 * i + 8], block_r[16 * i + 9], block_r[16 * i + 10], block_r[16 * i + 11], block_r[16 * i + 12], block_r[16 * i + 13], block_r[16 * i + 14], block_r[16 * i + 15]); for (i = 0; i < 8; i++) BLAKE2_ROUND_NOMSG (block_r[2 * i], block_r[2 * i + 1], block_r[2 * i + 16], block_r[2 * i + 17], block_r[2 * i + 32], block_r[2 * i + 33], block_r[2 * i + 48], block_r[2 * i + 49], block_r[2 * i + 64], block_r[2 * i + 65], block_r[2 * i + 80], block_r[2 * i + 81], block_r[2 * i + 96], block_r[2 * i + 97], block_r[2 * i + 112], block_r[2 * i + 113]); memcpy (curr_block, block_tmp, 1024); xor_block (curr_block, block_r); } static void pseudo_random_generate (u64 *random_block, u64 *input_block) { input_block[6]++; fill_block (NULL, input_block, random_block, 0); fill_block (NULL, random_block, random_block, 0); } static u32 index_alpha (argon2_ctx_t a, const struct argon2_thread_data *t, int segment_index, u32 random, int same_lane) { u32 reference_area_size; u64 relative_position; u32 start_position; if (t->pass == 0) { if (t->slice == 0) reference_area_size = segment_index - 1; else { if (same_lane) reference_area_size = t->slice * a->segment_length + segment_index - 1; else reference_area_size = t->slice * a->segment_length + ((segment_index == 0) ? -1 : 0); } } else { if (same_lane) reference_area_size = a->lane_length - a->segment_length + segment_index - 1; else reference_area_size = a->lane_length - a->segment_length + ((segment_index == 0) ? -1 : 0); } relative_position = (random * (u64)random) >> 32; relative_position = reference_area_size - 1 - ((reference_area_size * relative_position) >> 32); if (t->pass == 0) start_position = 0; else start_position = (t->slice == 4 - 1) ? 0 : (t->slice + 1) * a->segment_length; return (start_position + relative_position) % a->lane_length; } static void argon2_compute_segment (void *priv) { const struct argon2_thread_data *t = (const struct argon2_thread_data *)priv; argon2_ctx_t a = t->a; int i; int prev_offset, curr_offset; u32 ref_index, ref_lane; u64 input_block[1024/sizeof (u64)]; u64 address_block[1024/sizeof (u64)]; u64 *random_block = NULL; if (a->hash_type == GCRY_KDF_ARGON2I || (a->hash_type == GCRY_KDF_ARGON2ID && t->pass == 0 && t->slice < 2)) { memset (input_block, 0, 1024); input_block[0] = t->pass; input_block[1] = t->lane; input_block[2] = t->slice; input_block[3] = a->memory_blocks; input_block[4] = a->passes; input_block[5] = a->hash_type; random_block = address_block; } if (t->pass == 0 && t->slice == 0) { if (random_block) pseudo_random_generate (random_block, input_block); i = 2; } else i = 0; curr_offset = t->lane * a->lane_length + t->slice * a->segment_length + i; if ((curr_offset % a->lane_length)) prev_offset = curr_offset - 1; else prev_offset = curr_offset + a->lane_length - 1; for (; i < a->segment_length; i++, curr_offset++, prev_offset++) { u64 *ref_block, *curr_block; u64 rand64; if ((curr_offset % a->lane_length) == 1) prev_offset = curr_offset - 1; if (random_block) { if ((i % (1024/sizeof (u64))) == 0) pseudo_random_generate (random_block, input_block); rand64 = random_block[(i% (1024/sizeof (u64)))]; } else rand64 = a->block[prev_offset*ARGON2_WORDS_IN_BLOCK]; if (t->pass == 0 && t->slice == 0) ref_lane = t->lane; else ref_lane = (rand64 >> 32) % a->lanes; ref_index = index_alpha (a, t, i, (rand64 & 0xffffffff), ref_lane == t->lane); ref_block = &a->block[(a->lane_length * ref_lane + ref_index)* ARGON2_WORDS_IN_BLOCK]; curr_block = &a->block[curr_offset * ARGON2_WORDS_IN_BLOCK]; fill_block (&a->block[prev_offset * ARGON2_WORDS_IN_BLOCK], ref_block, curr_block, t->pass != 0); } } static gpg_err_code_t argon2_compute (argon2_ctx_t a, const struct gcry_kdf_thread_ops *ops) { gpg_err_code_t ec; unsigned int r; unsigned int s; unsigned int l; int ret; ec = argon2_fill_first_blocks (a); if (ec) return ec; for (r = 0; r < a->passes; r++) for (s = 0; s < 4; s++) { for (l = 0; l < a->lanes; l++) { struct argon2_thread_data *thread_data; /* launch a thread. */ thread_data = &a->thread_data[l]; thread_data->a = a; thread_data->pass = r; thread_data->slice = s; thread_data->lane = l; if (ops) { ret = ops->dispatch_job (ops->jobs_context, argon2_compute_segment, thread_data); if (ret < 0) return GPG_ERR_CANCELED; } else argon2_compute_segment (thread_data); } if (ops) { ret = ops->wait_all_jobs (ops->jobs_context); if (ret < 0) return GPG_ERR_CANCELED; } } return 0; } static gpg_err_code_t argon2_final (argon2_ctx_t a, size_t resultlen, void *result) { int i; if (resultlen != a->outlen) return GPG_ERR_INV_VALUE; memset (a->block, 0, 1024); for (i = 0; i < a->lanes; i++) { u64 *last_block; last_block = &a->block[(a->lane_length * i + (a->lane_length - 1)) * ARGON2_WORDS_IN_BLOCK]; xor_block (a->block, last_block); } beswap64_block (a->block); blake2b_vl_hash (a->block, 1024, a->outlen, result); return 0; } static void argon2_close (argon2_ctx_t a) { size_t n; n = offsetof (struct argon2_context, out) + a->outlen; if (a->block) { wipememory (a->block, 1024 * a->memory_blocks); xfree (a->block); } if (a->thread_data) xfree (a->thread_data); wipememory (a, n); xfree (a); } static gpg_err_code_t argon2_open (gcry_kdf_hd_t *hd, int subalgo, const unsigned long *param, unsigned int paramlen, const void *password, size_t passwordlen, const void *salt, size_t saltlen, const void *key, size_t keylen, const void *ad, size_t adlen) { int hash_type; unsigned int taglen; unsigned int t_cost; unsigned int m_cost; unsigned int parallelism = 1; argon2_ctx_t a; gpg_err_code_t ec; size_t n; if (subalgo != GCRY_KDF_ARGON2D && subalgo != GCRY_KDF_ARGON2I && subalgo != GCRY_KDF_ARGON2ID) return GPG_ERR_INV_VALUE; else hash_type = subalgo; /* param : [ tag_length, t_cost, m_cost, parallelism ] */ if (paramlen < 3 || paramlen > 4) return GPG_ERR_INV_VALUE; else { taglen = (unsigned int)param[0]; t_cost = (unsigned int)param[1]; m_cost = (unsigned int)param[2]; if (paramlen >= 4) parallelism = (unsigned int)param[3]; } + if (parallelism == 0) + return GPG_ERR_INV_VALUE; + n = offsetof (struct argon2_context, out) + taglen; a = xtrymalloc (n); if (!a) return gpg_err_code_from_errno (errno); a->algo = GCRY_KDF_ARGON2; a->hash_type = hash_type; a->outlen = taglen; a->password = password; a->passwordlen = passwordlen; a->salt = salt; a->saltlen = saltlen; a->key = key; a->keylen = keylen; a->ad = ad; a->adlen = adlen; a->m_cost = m_cost; a->block = NULL; a->thread_data = NULL; ec = argon2_init (a, parallelism, m_cost, t_cost); if (ec) { xfree (a); return ec; } *hd = (void *)a; return 0; } typedef struct balloon_context *balloon_ctx_t; /* Per thread data for Balloon. */ struct balloon_thread_data { balloon_ctx_t b; gpg_err_code_t ec; unsigned int idx; unsigned char *block; }; /* Balloon context */ struct balloon_context { int algo; int prng_type; unsigned int blklen; const gcry_md_spec_t *md_spec; const unsigned char *password; size_t passwordlen; const unsigned char *salt; /* Length of salt is fixed. */ unsigned int s_cost; unsigned int t_cost; unsigned int parallelism; u64 n_blocks; unsigned char *block; /* In future, we may use flexible array member. */ struct balloon_thread_data thread_data[1]; }; /* Maximum size of underlining digest size. */ #define BALLOON_BLOCK_LEN_MAX 64 static gpg_err_code_t prng_aes_ctr_init (gcry_cipher_hd_t *hd_p, balloon_ctx_t b, gcry_buffer_t *iov, unsigned int iov_count) { gpg_err_code_t ec; gcry_cipher_hd_t hd; unsigned char key[BALLOON_BLOCK_LEN_MAX]; int cipher_algo; unsigned int keylen, blklen; switch (b->blklen) { case 64: cipher_algo = GCRY_CIPHER_AES256; break; case 48: cipher_algo = GCRY_CIPHER_AES192; break; default: case 32: cipher_algo = GCRY_CIPHER_AES; break; } keylen = _gcry_cipher_get_algo_keylen (cipher_algo); blklen = _gcry_cipher_get_algo_blklen (cipher_algo); b->md_spec->hash_buffers (key, b->blklen, iov, iov_count); ec = _gcry_cipher_open (&hd, cipher_algo, GCRY_CIPHER_MODE_CTR, 0); if (ec) return ec; ec = _gcry_cipher_setkey (hd, key, keylen); if (ec) { _gcry_cipher_close (hd); return ec; } if (cipher_algo == GCRY_CIPHER_AES && b->md_spec == &_gcry_digest_spec_sha256) /* Original Balloon uses zero IV. */ ; else { ec = _gcry_cipher_setiv (hd, key+keylen, blklen); if (ec) { _gcry_cipher_close (hd); return ec; } } wipememory (key, BALLOON_BLOCK_LEN_MAX); *hd_p = hd; return ec; } static u64 prng_aes_ctr_get_rand64 (gcry_cipher_hd_t hd) { static const unsigned char zero64[8]; unsigned char rand64[8]; _gcry_cipher_encrypt (hd, rand64, sizeof (rand64), zero64, sizeof (zero64)); return buf_get_le64 (rand64); } static void prng_aes_ctr_fini (gcry_cipher_hd_t hd) { _gcry_cipher_close (hd); } static size_t ballon_context_size (unsigned int parallelism) { size_t n; n = offsetof (struct balloon_context, thread_data) + parallelism * sizeof (struct balloon_thread_data); return n; } static gpg_err_code_t balloon_open (gcry_kdf_hd_t *hd, int subalgo, const unsigned long *param, unsigned int paramlen, const void *password, size_t passwordlen, const void *salt, size_t saltlen) { unsigned int blklen; int hash_type; unsigned int s_cost; unsigned int t_cost; unsigned int parallelism = 1; balloon_ctx_t b; gpg_err_code_t ec; size_t n; unsigned char *block; unsigned int i; const gcry_md_spec_t *md_spec; hash_type = subalgo; switch (hash_type) { case GCRY_MD_SHA256: md_spec = &_gcry_digest_spec_sha256; break; case GCRY_MD_SHA384: md_spec = &_gcry_digest_spec_sha384; break; case GCRY_MD_SHA512: md_spec = &_gcry_digest_spec_sha512; break; case GCRY_MD_SHA3_256: md_spec = &_gcry_digest_spec_sha3_256; break; case GCRY_MD_SHA3_384: md_spec = &_gcry_digest_spec_sha3_384; break; case GCRY_MD_SHA3_512: md_spec = &_gcry_digest_spec_sha3_512; break; default: return GPG_ERR_NOT_SUPPORTED; } blklen = _gcry_md_get_algo_dlen (hash_type); if (!blklen || blklen > BALLOON_BLOCK_LEN_MAX) return GPG_ERR_NOT_SUPPORTED; if (saltlen != blklen) return GPG_ERR_NOT_SUPPORTED; /* * It should have space_cost and time_cost. * Optionally, for parallelised version, it has parallelism. * Possibly (in future), it may have option to specify PRNG type. */ if (paramlen != 2 && paramlen != 3) return GPG_ERR_INV_VALUE; else { s_cost = (unsigned int)param[0]; t_cost = (unsigned int)param[1]; if (paramlen >= 3) parallelism = (unsigned int)param[2]; } if (s_cost < 1) return GPG_ERR_INV_VALUE; n = ballon_context_size (parallelism); b = xtrymalloc (n); if (!b) return gpg_err_code_from_errno (errno); b->algo = GCRY_KDF_BALLOON; b->md_spec = md_spec; b->blklen = blklen; b->password = password; b->passwordlen = passwordlen; b->salt = salt; b->s_cost = s_cost; b->t_cost = t_cost; b->parallelism = parallelism; b->n_blocks = (s_cost * 1024) / b->blklen; block = xtrycalloc (parallelism * b->n_blocks, b->blklen); if (!block) { ec = gpg_err_code_from_errno (errno); xfree (b); return ec; } b->block = block; for (i = 0; i < parallelism; i++) { struct balloon_thread_data *t = &b->thread_data[i]; t->b = b; t->ec = 0; t->idx = i; t->block = block; block += b->blklen * b->n_blocks; } *hd = (void *)b; return 0; } static void balloon_xor_block (balloon_ctx_t b, u64 *dst, const u64 *src) { int i; for (i = 0; i < b->blklen/8; i++) dst[i] ^= src[i]; } #define BALLOON_COMPRESS_BLOCKS 5 static void balloon_compress (balloon_ctx_t b, u64 *counter_p, unsigned char *out, const unsigned char *blocks[BALLOON_COMPRESS_BLOCKS]) { gcry_buffer_t iov[1+BALLOON_COMPRESS_BLOCKS]; unsigned char octet_counter[sizeof (u64)]; unsigned int i; buf_put_le64 (octet_counter, *counter_p); iov[0].data = octet_counter; iov[0].len = sizeof (octet_counter); iov[0].off = 0; for (i = 1; i < 1+BALLOON_COMPRESS_BLOCKS; i++) { iov[i].data = (void *)blocks[i-1]; iov[i].len = b->blklen; iov[i].off = 0; } b->md_spec->hash_buffers (out, b->blklen, iov, 1+BALLOON_COMPRESS_BLOCKS); *counter_p += 1; } static void balloon_expand (balloon_ctx_t b, u64 *counter_p, unsigned char *block, u64 n_blocks) { gcry_buffer_t iov[2]; unsigned char octet_counter[sizeof (u64)]; u64 i; iov[0].data = octet_counter; iov[0].len = sizeof (octet_counter); iov[0].off = 0; iov[1].len = b->blklen; iov[1].off = 0; for (i = 1; i < n_blocks; i++) { buf_put_le64 (octet_counter, *counter_p); iov[1].data = block; block += b->blklen; b->md_spec->hash_buffers (block, b->blklen, iov, 2); *counter_p += 1; } } static void balloon_compute_fill (balloon_ctx_t b, struct balloon_thread_data *t, const unsigned char *salt, u64 *counter_p) { gcry_buffer_t iov[6]; unsigned char octet_counter[sizeof (u64)]; unsigned char octet_s_cost[4]; unsigned char octet_t_cost[4]; unsigned char octet_parallelism[4]; buf_put_le64 (octet_counter, *counter_p); buf_put_le32 (octet_s_cost, b->s_cost); buf_put_le32 (octet_t_cost, b->t_cost); buf_put_le32 (octet_parallelism, b->parallelism); iov[0].data = octet_counter; iov[0].len = sizeof (octet_counter); iov[0].off = 0; iov[1].data = (void *)salt; iov[1].len = b->blklen; iov[1].off = 0; iov[2].data = (void *)b->password; iov[2].len = b->passwordlen; iov[2].off = 0; iov[3].data = octet_s_cost; iov[3].len = 4; iov[3].off = 0; iov[4].data = octet_t_cost; iov[4].len = 4; iov[4].off = 0; iov[5].data = octet_parallelism; iov[5].len = 4; iov[5].off = 0; b->md_spec->hash_buffers (t->block, b->blklen, iov, 6); *counter_p += 1; balloon_expand (b, counter_p, t->block, b->n_blocks); } static void balloon_compute_mix (gcry_cipher_hd_t prng, balloon_ctx_t b, struct balloon_thread_data *t, u64 *counter_p) { u64 i; for (i = 0; i < b->n_blocks; i++) { unsigned char *cur_block = t->block + (b->blklen * i); const unsigned char *blocks[BALLOON_COMPRESS_BLOCKS]; const unsigned char *prev_block; unsigned int n; prev_block = i ? cur_block - b->blklen : t->block + (b->blklen * (t->b->n_blocks - 1)); n = 0; blocks[n++] = prev_block; blocks[n++] = cur_block; for (; n < BALLOON_COMPRESS_BLOCKS; n++) { u64 rand64 = prng_aes_ctr_get_rand64 (prng); blocks[n] = t->block + (b->blklen * (rand64 % b->n_blocks)); } balloon_compress (b, counter_p, cur_block, blocks); } } static void balloon_compute (void *priv) { struct balloon_thread_data *t = (struct balloon_thread_data *)priv; balloon_ctx_t b = t->b; gcry_cipher_hd_t prng; gcry_buffer_t iov[4]; unsigned char salt[BALLOON_BLOCK_LEN_MAX]; unsigned char octet_s_cost[4]; unsigned char octet_t_cost[4]; unsigned char octet_parallelism[4]; u32 u; u64 counter; unsigned int i; counter = 0; memcpy (salt, b->salt, b->blklen); u = buf_get_le32 (b->salt) + t->idx; buf_put_le32 (salt, u); buf_put_le32 (octet_s_cost, b->s_cost); buf_put_le32 (octet_t_cost, b->t_cost); buf_put_le32 (octet_parallelism, b->parallelism); iov[0].data = salt; iov[0].len = b->blklen; iov[0].off = 0; iov[1].data = octet_s_cost; iov[1].len = 4; iov[1].off = 0; iov[2].data = octet_t_cost; iov[2].len = 4; iov[2].off = 0; iov[3].data = octet_parallelism; iov[3].len = 4; iov[3].off = 0; t->ec = prng_aes_ctr_init (&prng, b, iov, 4); if (t->ec) return; balloon_compute_fill (b, t, salt, &counter); for (i = 0; i < b->t_cost; i++) balloon_compute_mix (prng, b, t, &counter); /* The result is now at the last block. */ prng_aes_ctr_fini (prng); } static gpg_err_code_t balloon_compute_all (balloon_ctx_t b, const struct gcry_kdf_thread_ops *ops) { unsigned int parallelism = b->parallelism; unsigned int i; int ret; for (i = 0; i < parallelism; i++) { struct balloon_thread_data *t = &b->thread_data[i]; if (ops) { ret = ops->dispatch_job (ops->jobs_context, balloon_compute, t); if (ret < 0) return GPG_ERR_CANCELED; } else balloon_compute (t); } if (ops) { ret = ops->wait_all_jobs (ops->jobs_context); if (ret < 0) return GPG_ERR_CANCELED; } return 0; } static gpg_err_code_t balloon_final (balloon_ctx_t b, size_t resultlen, void *result) { unsigned int parallelism = b->parallelism; unsigned int i; u64 out[BALLOON_BLOCK_LEN_MAX/8]; if (resultlen != b->blklen) return GPG_ERR_INV_VALUE; memset (out, 0, b->blklen); for (i = 0; i < parallelism; i++) { struct balloon_thread_data *t = &b->thread_data[i]; const unsigned char *last_block; if (t->ec) return t->ec; last_block = t->block + (b->blklen * (t->b->n_blocks - 1)); balloon_xor_block (b, out, (const u64 *)(void *)last_block); } memcpy (result, out, b->blklen); return 0; } static void balloon_close (balloon_ctx_t b) { unsigned int parallelism = b->parallelism; size_t n = ballon_context_size (parallelism); if (b->block) { wipememory (b->block, parallelism * b->n_blocks); xfree (b->block); } wipememory (b, n); xfree (b); } struct gcry_kdf_handle { int algo; /* And algo specific parts come. */ }; gpg_err_code_t _gcry_kdf_open (gcry_kdf_hd_t *hd, int algo, int subalgo, const unsigned long *param, unsigned int paramlen, const void *passphrase, size_t passphraselen, const void *salt, size_t saltlen, const void *key, size_t keylen, const void *ad, size_t adlen) { gpg_err_code_t ec; switch (algo) { case GCRY_KDF_ARGON2: if (!passphraselen || !saltlen) ec = GPG_ERR_INV_VALUE; else ec = argon2_open (hd, subalgo, param, paramlen, passphrase, passphraselen, salt, saltlen, key, keylen, ad, adlen); break; case GCRY_KDF_BALLOON: if (!passphraselen || !saltlen || keylen || adlen) ec = GPG_ERR_INV_VALUE; else { (void)key; (void)ad; ec = balloon_open (hd, subalgo, param, paramlen, passphrase, passphraselen, salt, saltlen); } break; default: ec = GPG_ERR_UNKNOWN_ALGORITHM; break; } return ec; } gpg_err_code_t _gcry_kdf_compute (gcry_kdf_hd_t h, const struct gcry_kdf_thread_ops *ops) { gpg_err_code_t ec; switch (h->algo) { case GCRY_KDF_ARGON2: ec = argon2_compute ((argon2_ctx_t)(void *)h, ops); break; case GCRY_KDF_BALLOON: ec = balloon_compute_all ((balloon_ctx_t)(void *)h, ops); break; default: ec = GPG_ERR_UNKNOWN_ALGORITHM; break; } return ec; } gpg_err_code_t _gcry_kdf_final (gcry_kdf_hd_t h, size_t resultlen, void *result) { gpg_err_code_t ec; switch (h->algo) { case GCRY_KDF_ARGON2: ec = argon2_final ((argon2_ctx_t)(void *)h, resultlen, result); break; case GCRY_KDF_BALLOON: ec = balloon_final ((balloon_ctx_t)(void *)h, resultlen, result); break; default: ec = GPG_ERR_UNKNOWN_ALGORITHM; break; } return ec; } void _gcry_kdf_close (gcry_kdf_hd_t h) { switch (h->algo) { case GCRY_KDF_ARGON2: argon2_close ((argon2_ctx_t)(void *)h); break; case GCRY_KDF_BALLOON: balloon_close ((balloon_ctx_t)(void *)h); break; default: break; } } /* Check one KDF call with ALGO and HASH_ALGO using the regular KDF * API. (passphrase,passphraselen) is the password to be derived, * (salt,saltlen) the salt for the key derivation, * iterations is the number of the kdf iterations, * and (expect,expectlen) the expected result. Returns NULL on * success or a string describing the failure. */ static const char * check_one (int algo, int hash_algo, const void *passphrase, size_t passphraselen, const void *salt, size_t saltlen, unsigned long iterations, const void *expect, size_t expectlen) { unsigned char key[512]; /* hardcoded to avoid allocation */ size_t keysize = expectlen; /* Skip test with shoter passphrase in FIPS mode. */ if (fips_mode () && passphraselen < 14) return NULL; if (keysize > sizeof(key)) return "invalid tests data"; if (_gcry_kdf_derive (passphrase, passphraselen, algo, hash_algo, salt, saltlen, iterations, keysize, key)) return "gcry_kdf_derive failed"; if (memcmp (key, expect, expectlen)) return "does not match"; return NULL; } static gpg_err_code_t selftest_pbkdf2 (int extended, selftest_report_func_t report) { static const struct { const char *desc; const char *p; /* Passphrase. */ size_t plen; /* Length of P. */ const char *salt; size_t saltlen; int hashalgo; unsigned long c; /* Iterations. */ int dklen; /* Requested key length. */ const char *dk; /* Derived key. */ int disabled; } tv[] = { #if USE_SHA1 #define NUM_TEST_VECTORS 9 /* SHA1 test vectors are from RFC-6070. */ { "Basic PBKDF2 SHA1 #1", "password", 8, "salt", 4, GCRY_MD_SHA1, 1, 20, "\x0c\x60\xc8\x0f\x96\x1f\x0e\x71\xf3\xa9" "\xb5\x24\xaf\x60\x12\x06\x2f\xe0\x37\xa6" }, { "Basic PBKDF2 SHA1 #2", "password", 8, "salt", 4, GCRY_MD_SHA1, 2, 20, "\xea\x6c\x01\x4d\xc7\x2d\x6f\x8c\xcd\x1e" "\xd9\x2a\xce\x1d\x41\xf0\xd8\xde\x89\x57" }, { "Basic PBKDF2 SHA1 #3", "password", 8, "salt", 4, GCRY_MD_SHA1, 4096, 20, "\x4b\x00\x79\x01\xb7\x65\x48\x9a\xbe\xad" "\x49\xd9\x26\xf7\x21\xd0\x65\xa4\x29\xc1" }, { "Basic PBKDF2 SHA1 #4", "password", 8, "salt", 4, GCRY_MD_SHA1, 16777216, 20, "\xee\xfe\x3d\x61\xcd\x4d\xa4\xe4\xe9\x94" "\x5b\x3d\x6b\xa2\x15\x8c\x26\x34\xe9\x84", 1 /* This test takes too long. */ }, { "Basic PBKDF2 SHA1 #5", "passwordPASSWORDpassword", 24, "saltSALTsaltSALTsaltSALTsaltSALTsalt", 36, GCRY_MD_SHA1, 4096, 25, "\x3d\x2e\xec\x4f\xe4\x1c\x84\x9b\x80\xc8" "\xd8\x36\x62\xc0\xe4\x4a\x8b\x29\x1a\x96" "\x4c\xf2\xf0\x70\x38" }, { "Basic PBKDF2 SHA1 #6", "pass\0word", 9, "sa\0lt", 5, GCRY_MD_SHA1, 4096, 16, "\x56\xfa\x6a\xa7\x55\x48\x09\x9d\xcc\x37" "\xd7\xf0\x34\x25\xe0\xc3" }, { /* empty password test, not in RFC-6070 */ "Basic PBKDF2 SHA1 #7", "", 0, "salt", 4, GCRY_MD_SHA1, 2, 20, "\x13\x3a\x4c\xe8\x37\xb4\xd2\x52\x1e\xe2" "\xbf\x03\xe1\x1c\x71\xca\x79\x4e\x07\x97" }, #else #define NUM_TEST_VECTORS 2 #endif { "Basic PBKDF2 SHA256", "password", 8, "salt", 4, GCRY_MD_SHA256, 2, 32, "\xae\x4d\x0c\x95\xaf\x6b\x46\xd3\x2d\x0a\xdf\xf9\x28\xf0\x6d\xd0" "\x2a\x30\x3f\x8e\xf3\xc2\x51\xdf\xd6\xe2\xd8\x5a\x95\x47\x4c\x43" }, { "Extended PBKDF2 SHA256", "passwordPASSWORDpassword", 24, "saltSALTsaltSALTsaltSALTsaltSALTsalt", 36, GCRY_MD_SHA256, 4096, 40, "\x34\x8c\x89\xdb\xcb\xd3\x2b\x2f\x32\xd8\x14\xb8\x11\x6e\x84\xcf" "\x2b\x17\x34\x7e\xbc\x18\x00\x18\x1c\x4e\x2a\x1f\xb8\xdd\x53\xe1" "\xc6\x35\x51\x8c\x7d\xac\x47\xe9" }, { NULL } }; const char *what; const char *errtxt; int tvidx; for (tvidx=0; tv[tvidx].desc; tvidx++) { what = tv[tvidx].desc; if (tv[tvidx].disabled) continue; errtxt = check_one (GCRY_KDF_PBKDF2, tv[tvidx].hashalgo, tv[tvidx].p, tv[tvidx].plen, tv[tvidx].salt, tv[tvidx].saltlen, tv[tvidx].c, tv[tvidx].dk, tv[tvidx].dklen); if (errtxt) goto failed; if (tvidx >= NUM_TEST_VECTORS - 1 && !extended) break; } return 0; /* Succeeded. */ failed: if (report) report ("kdf", GCRY_KDF_PBKDF2, what, errtxt); return GPG_ERR_SELFTEST_FAILED; } /* Run the selftests for KDF with KDF algorithm ALGO with optional reporting function REPORT. */ gpg_error_t _gcry_kdf_selftest (int algo, int extended, selftest_report_func_t report) { gcry_err_code_t ec = 0; if (algo == GCRY_KDF_PBKDF2) ec = selftest_pbkdf2 (extended, report); else { ec = GPG_ERR_UNSUPPORTED_ALGORITHM; if (report) report ("kdf", algo, "module", "algorithm not available"); } return gpg_error (ec); }