diff --git a/cipher/kdf.c b/cipher/kdf.c index d371bdd7..3e51e115 100644 --- a/cipher/kdf.c +++ b/cipher/kdf.c @@ -1,2225 +1,2228 @@ /* 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++; + if (a->passwordlen) + { + 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); } typedef struct onestep_kdf_context *onestep_kdf_ctx_t; /* OneStepKDF context */ struct onestep_kdf_context { int algo; gcry_md_hd_t md; unsigned int blklen; unsigned int outlen; const void *input; size_t inputlen; const void *fixedinfo; size_t fixedinfolen; }; static gpg_err_code_t onestep_kdf_open (gcry_kdf_hd_t *hd, int hashalgo, const unsigned long *param, unsigned int paramlen, const void *input, size_t inputlen, const void *fixedinfo, size_t fixedinfolen) { gpg_err_code_t ec; unsigned int outlen; onestep_kdf_ctx_t o; size_t n; if (paramlen != 1) return GPG_ERR_INV_VALUE; else outlen = (unsigned int)param[0]; n = sizeof (struct onestep_kdf_context); o = xtrymalloc (n); if (!o) return gpg_err_code_from_errno (errno); o->blklen = _gcry_md_get_algo_dlen (hashalgo); if (!o->blklen) { xfree (o); return GPG_ERR_DIGEST_ALGO; } ec = _gcry_md_open (&o->md, hashalgo, 0); if (ec) { xfree (o); return ec; } o->algo = GCRY_KDF_ONESTEP_KDF; o->outlen = outlen; o->input = input; o->inputlen = inputlen; o->fixedinfo = fixedinfo; o->fixedinfolen = fixedinfolen; *hd = (void *)o; return 0; } static gpg_err_code_t onestep_kdf_compute (onestep_kdf_ctx_t o, const struct gcry_kdf_thread_ops *ops) { (void)o; if (ops != NULL) return GPG_ERR_INV_VALUE; return 0; } static gpg_err_code_t onestep_kdf_final (onestep_kdf_ctx_t o, size_t resultlen, void *result) { u32 counter = 0; unsigned char cnt[4]; int i; if (resultlen != o->outlen) return GPG_ERR_INV_VALUE; for (i = 0; i < o->outlen / o->blklen; i++) { counter++; buf_put_be32 (cnt, counter); _gcry_md_write (o->md, cnt, sizeof (cnt)); _gcry_md_write (o->md, o->input, o->inputlen); _gcry_md_write (o->md, o->fixedinfo, o->fixedinfolen); _gcry_md_final (o->md); memcpy ((char *)result + o->blklen * i, _gcry_md_read (o->md, 0), o->blklen); resultlen -= o->blklen; _gcry_md_reset (o->md); } if (resultlen) { counter++; buf_put_be32 (cnt, counter); _gcry_md_write (o->md, cnt, sizeof (cnt)); _gcry_md_write (o->md, o->input, o->inputlen); _gcry_md_write (o->md, o->fixedinfo, o->fixedinfolen); _gcry_md_final (o->md); memcpy ((char *)result + o->blklen * i, _gcry_md_read (o->md, 0), resultlen); } return 0; } static void onestep_kdf_close (onestep_kdf_ctx_t o) { _gcry_md_close (o->md); xfree (o); } typedef struct onestep_kdf_mac_context *onestep_kdf_mac_ctx_t; /* OneStep_KDF_MAC context */ struct onestep_kdf_mac_context { int algo; gcry_mac_hd_t md; unsigned int blklen; unsigned int outlen; const void *input; size_t inputlen; const void *salt; size_t saltlen; const void *fixedinfo; size_t fixedinfolen; }; static gpg_err_code_t onestep_kdf_mac_open (gcry_kdf_hd_t *hd, int macalgo, const unsigned long *param, unsigned int paramlen, const void *input, size_t inputlen, const void *key, size_t keylen, const void *fixedinfo, size_t fixedinfolen) { gpg_err_code_t ec; unsigned int outlen; onestep_kdf_mac_ctx_t o; size_t n; if (paramlen != 1) return GPG_ERR_INV_VALUE; else outlen = (unsigned int)param[0]; n = sizeof (struct onestep_kdf_mac_context); o = xtrymalloc (n); if (!o) return gpg_err_code_from_errno (errno); o->blklen = _gcry_mac_get_algo_maclen (macalgo); if (!o->blklen) { xfree (o); return GPG_ERR_MAC_ALGO; } ec = _gcry_mac_open (&o->md, macalgo, 0, NULL); if (ec) { xfree (o); return ec; } o->algo = GCRY_KDF_ONESTEP_KDF_MAC; o->outlen = outlen; o->input = input; o->inputlen = inputlen; o->salt = key; o->saltlen = keylen; o->fixedinfo = fixedinfo; o->fixedinfolen = fixedinfolen; *hd = (void *)o; return 0; } static gpg_err_code_t onestep_kdf_mac_compute (onestep_kdf_mac_ctx_t o, const struct gcry_kdf_thread_ops *ops) { (void)o; if (ops != NULL) return GPG_ERR_INV_VALUE; return 0; } static gpg_err_code_t onestep_kdf_mac_final (onestep_kdf_mac_ctx_t o, size_t resultlen, void *result) { u32 counter = 0; unsigned char cnt[4]; int i; gcry_err_code_t ec; size_t len = o->blklen; if (resultlen != o->outlen) return GPG_ERR_INV_VALUE; ec = _gcry_mac_setkey (o->md, o->salt, o->saltlen); if (ec) return ec; for (i = 0; i < o->outlen / o->blklen; i++) { counter++; buf_put_be32 (cnt, counter); ec = _gcry_mac_write (o->md, cnt, sizeof (cnt)); if (ec) return ec; ec = _gcry_mac_write (o->md, o->input, o->inputlen); if (ec) return ec; ec = _gcry_mac_write (o->md, o->fixedinfo, o->fixedinfolen); if (ec) return ec; ec = _gcry_mac_read (o->md, (char *)result + o->blklen * i, &len); if (ec) return ec; resultlen -= o->blklen; ec = _gcry_mac_ctl (o->md, GCRYCTL_RESET, NULL, 0); if (ec) return ec; } if (resultlen) { counter++; len = resultlen; buf_put_be32 (cnt, counter); ec = _gcry_mac_write (o->md, cnt, sizeof (cnt)); if (ec) return ec; ec = _gcry_mac_write (o->md, o->input, o->inputlen); if (ec) return ec; ec =_gcry_mac_write (o->md, o->fixedinfo, o->fixedinfolen); if (ec) return ec; ec = _gcry_mac_read (o->md, (char *)result + o->blklen * i, &len); if (ec) return ec; } return 0; } static void onestep_kdf_mac_close (onestep_kdf_mac_ctx_t o) { _gcry_mac_close (o->md); xfree (o); } typedef struct hkdf_context *hkdf_ctx_t; /* Hkdf context */ struct hkdf_context { int algo; gcry_mac_hd_t md; int mode; unsigned int blklen; unsigned int outlen; const void *input; size_t inputlen; const void *salt; size_t saltlen; const void *fixedinfo; size_t fixedinfolen; unsigned char *prk; }; static gpg_err_code_t hkdf_open (gcry_kdf_hd_t *hd, int macalgo, const unsigned long *param, unsigned int paramlen, const void *input, size_t inputlen, const void *salt, size_t saltlen, const void *fixedinfo, size_t fixedinfolen) { gpg_err_code_t ec; unsigned int outlen; int mode; hkdf_ctx_t h; size_t n; unsigned char *prk; if (paramlen != 1 && paramlen != 2) return GPG_ERR_INV_VALUE; else { outlen = (unsigned int)param[0]; /* MODE: support extract only, expand only: FIXME*/ if (paramlen == 2) mode = (unsigned int)param[1]; else mode = 0; } n = sizeof (struct hkdf_context); h = xtrymalloc (n); if (!h) return gpg_err_code_from_errno (errno); h->blklen = _gcry_mac_get_algo_maclen (macalgo); if (!h->blklen) { xfree (h); return GPG_ERR_MAC_ALGO; } if (outlen > 255 * h->blklen) { xfree (h); return GPG_ERR_INV_VALUE; } ec = _gcry_mac_open (&h->md, macalgo, 0, NULL); if (ec) { xfree (h); return ec; } prk = xtrymalloc (h->blklen); if (!prk) { _gcry_mac_close (h->md); xfree (h); return gpg_err_code_from_errno (errno); } h->prk = prk; h->algo = GCRY_KDF_HKDF; h->outlen = outlen; h->mode = mode; h->input = input; h->inputlen = inputlen; h->salt = salt; h->saltlen = saltlen; h->fixedinfo = fixedinfo; h->fixedinfolen = fixedinfolen; *hd = (void *)h; return 0; } static gpg_err_code_t hkdf_compute (hkdf_ctx_t h, const struct gcry_kdf_thread_ops *ops) { gcry_err_code_t ec; size_t len = h->blklen; if (ops != NULL) return GPG_ERR_INV_VALUE; /* Extract */ ec = _gcry_mac_setkey (h->md, h->salt, h->saltlen); if (ec) return ec; ec = _gcry_mac_write (h->md, h->input, h->inputlen); if (ec) return ec; ec = _gcry_mac_read (h->md, h->prk, &len); if (ec) return ec; ec = _gcry_mac_ctl (h->md, GCRYCTL_RESET, NULL, 0); if (ec) return ec; return 0; } static gpg_err_code_t hkdf_final (hkdf_ctx_t h, size_t resultlen, void *result) { unsigned char counter = 0; int i; gcry_err_code_t ec; size_t len = h->blklen; if (resultlen != h->outlen) return GPG_ERR_INV_VALUE; /* Expand */ ec = _gcry_mac_setkey (h->md, h->prk, h->blklen); if (ec) return ec; /* We re-use the memory of ->prk. */ for (i = 0; i < h->outlen / h->blklen; i++) { counter++; if (i) { ec = _gcry_mac_write (h->md, h->prk, h->blklen); if (ec) return ec; } if (h->fixedinfo) { ec = _gcry_mac_write (h->md, h->fixedinfo, h->fixedinfolen); if (ec) return ec; } ec = _gcry_mac_write (h->md, &counter, 1); if (ec) return ec; ec = _gcry_mac_read (h->md, h->prk, &len); if (ec) return ec; memcpy ((char *)result + h->blklen * i, h->prk, len); resultlen -= h->blklen; ec = _gcry_mac_ctl (h->md, GCRYCTL_RESET, NULL, 0); if (ec) return ec; } if (resultlen) { counter++; len = resultlen; if (i) { ec = _gcry_mac_write (h->md, h->prk, h->blklen); if (ec) return ec; } if (h->fixedinfo) { ec = _gcry_mac_write (h->md, h->fixedinfo, h->fixedinfolen); if (ec) return ec; } ec = _gcry_mac_write (h->md, &counter, 1); if (ec) return ec; ec = _gcry_mac_read (h->md, (char *)result + h->blklen * i, &len); if (ec) return ec; } return 0; } static void hkdf_close (hkdf_ctx_t h) { _gcry_mac_close (h->md); xfree (h->prk); xfree (h); } 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 *input, size_t inputlen, 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 (!inputlen || !saltlen) + if (!saltlen) ec = GPG_ERR_INV_VALUE; else ec = argon2_open (hd, subalgo, param, paramlen, input, inputlen, salt, saltlen, key, keylen, ad, adlen); break; case GCRY_KDF_BALLOON: if (!inputlen || !saltlen || keylen || adlen) ec = GPG_ERR_INV_VALUE; else { (void)key; (void)ad; ec = balloon_open (hd, subalgo, param, paramlen, input, inputlen, salt, saltlen); } break; case GCRY_KDF_ONESTEP_KDF: if (!inputlen || !paramlen || !adlen) ec = GPG_ERR_INV_VALUE; else { (void)salt; (void)key; ec = onestep_kdf_open (hd, subalgo, param, paramlen, input, inputlen, ad, adlen); } break; case GCRY_KDF_ONESTEP_KDF_MAC: if (!inputlen || !paramlen || !keylen || !adlen) ec = GPG_ERR_INV_VALUE; else { (void)salt; ec = onestep_kdf_mac_open (hd, subalgo, param, paramlen, input, inputlen, key, keylen, ad, adlen); } break; case GCRY_KDF_HKDF: if (!inputlen || !paramlen) ec = GPG_ERR_INV_VALUE; else { (void)salt; ec = hkdf_open (hd, subalgo, param, paramlen, input, inputlen, key, keylen, ad, adlen); } 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; case GCRY_KDF_ONESTEP_KDF: ec = onestep_kdf_compute ((onestep_kdf_ctx_t)(void *)h, ops); break; case GCRY_KDF_ONESTEP_KDF_MAC: ec = onestep_kdf_mac_compute ((onestep_kdf_mac_ctx_t)(void *)h, ops); break; case GCRY_KDF_HKDF: ec = hkdf_compute ((hkdf_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; case GCRY_KDF_ONESTEP_KDF: ec = onestep_kdf_final ((onestep_kdf_ctx_t)(void *)h, resultlen, result); break; case GCRY_KDF_ONESTEP_KDF_MAC: ec = onestep_kdf_mac_final ((onestep_kdf_mac_ctx_t)(void *)h, resultlen, result); break; case GCRY_KDF_HKDF: ec = hkdf_final ((hkdf_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; case GCRY_KDF_ONESTEP_KDF: onestep_kdf_close ((onestep_kdf_ctx_t)(void *)h); break; case GCRY_KDF_ONESTEP_KDF_MAC: onestep_kdf_mac_close ((onestep_kdf_mac_ctx_t)(void *)h); break; case GCRY_KDF_HKDF: hkdf_close ((hkdf_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); } diff --git a/tests/t-kdf.c b/tests/t-kdf.c index 17402762..c0192d7b 100644 --- a/tests/t-kdf.c +++ b/tests/t-kdf.c @@ -1,1981 +1,2007 @@ /* t-kdf.c - KDF regression tests * Copyright (C) 2011 Free Software Foundation, Inc. * * 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "stopwatch.h" #define PGM "t-kdf" #include "t-common.h" static int in_fips_mode; static void dummy_consumer (volatile char *buffer, size_t buflen) { (void)buffer; (void)buflen; } static void bench_s2k (unsigned long s2kcount) { gpg_error_t err; const char passphrase[] = "123456789abcdef0"; char keybuf[128/8]; unsigned int repetitions = 10; unsigned int count; const char *elapsed; int pass = 0; again: start_timer (); for (count = 0; count < repetitions; count++) { err = gcry_kdf_derive (passphrase, strlen (passphrase), GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "saltsalt", 8, s2kcount, sizeof keybuf, keybuf); if (err) die ("gcry_kdf_derive failed: %s\n", gpg_strerror (err)); dummy_consumer (keybuf, sizeof keybuf); } stop_timer (); elapsed = elapsed_time (repetitions); if (!pass++) { if (!atoi (elapsed)) { repetitions = 10000; goto again; } else if (atoi (elapsed) < 10) { repetitions = 100; goto again; } } printf ("%s\n", elapsed); } static void check_openpgp (void) { /* Test vectors manually created with gpg 1.4 derived code: In passphrase.c:hash_passpharse, add this code to the end of the function: ===8<=== printf ("{\n" " \""); for (i=0; i < pwlen; i++) { if (i && !(i%16)) printf ("\"\n \""); printf ("\\x%02x", ((const unsigned char *)pw)[i]); } printf ("\", %d,\n", pwlen); printf (" %s, %s,\n", s2k->mode == 0? "GCRY_KDF_SIMPLE_S2K": s2k->mode == 1? "GCRY_KDF_SALTED_S2K": s2k->mode == 3? "GCRY_KDF_ITERSALTED_S2K":"?", s2k->hash_algo == DIGEST_ALGO_MD5 ? "GCRY_MD_MD5" : s2k->hash_algo == DIGEST_ALGO_SHA1 ? "GCRY_MD_SHA1" : s2k->hash_algo == DIGEST_ALGO_RMD160? "GCRY_MD_RMD160" : s2k->hash_algo == DIGEST_ALGO_SHA256? "GCRY_MD_SHA256" : s2k->hash_algo == DIGEST_ALGO_SHA384? "GCRY_MD_SHA384" : s2k->hash_algo == DIGEST_ALGO_SHA512? "GCRY_MD_SHA512" : s2k->hash_algo == DIGEST_ALGO_SHA224? "GCRY_MD_SHA224" : "?"); if (s2k->mode == 0) printf (" NULL, 0,\n"); else { printf (" \""); for (i=0; i < 8; i++) printf ("\\x%02x", (unsigned int)s2k->salt[i]); printf ("\", %d,\n", 8); } if (s2k->mode == 3) printf (" %lu,\n", (unsigned long)S2K_DECODE_COUNT(s2k->count)); else printf (" 0,\n"); printf (" %d,\n", (int)dek->keylen); printf (" \""); for (i=0; i < dek->keylen; i++) { if (i && !(i%16)) printf ("\"\n \""); printf ("\\x%02x", ((unsigned char *)dek->key)[i]); } printf ("\"\n},\n"); ===>8=== Then prepare a file x.inp with utf8 encoding: ===8<=== 0 aes md5 1024 a 0 aes md5 1024 ab 0 aes md5 1024 abc 0 aes md5 1024 abcd 0 aes md5 1024 abcde 0 aes md5 1024 abcdef 0 aes md5 1024 abcdefg 0 aes md5 1024 abcdefgh 0 aes md5 1024 abcdefghi 0 aes md5 1024 abcdefghijklmno 0 aes md5 1024 abcdefghijklmnop 0 aes md5 1024 abcdefghijklmnopq 0 aes md5 1024 Long_sentence_used_as_passphrase 0 aes md5 1024 With_utf8_umlauts:äüÖß 0 aes sha1 1024 a 0 aes sha1 1024 ab 0 aes sha1 1024 abc 0 aes sha1 1024 abcd 0 aes sha1 1024 abcde 0 aes sha1 1024 abcdef 0 aes sha1 1024 abcdefg 0 aes sha1 1024 abcdefgh 0 aes sha1 1024 abcdefghi 0 aes sha1 1024 abcdefghijklmno 0 aes sha1 1024 abcdefghijklmnop 0 aes sha1 1024 abcdefghijklmnopq 0 aes sha1 1024 abcdefghijklmnopqr 0 aes sha1 1024 abcdefghijklmnopqrs 0 aes sha1 1024 abcdefghijklmnopqrst 0 aes sha1 1024 abcdefghijklmnopqrstu 0 aes sha1 1024 Long_sentence_used_as_passphrase 0 aes256 sha1 1024 Long_sentence_used_as_passphrase 0 aes sha1 1024 With_utf8_umlauts:äüÖß 3 aes sha1 1024 a 3 aes sha1 1024 ab 3 aes sha1 1024 abc 3 aes sha1 1024 abcd 3 aes sha1 1024 abcde 3 aes sha1 1024 abcdef 3 aes sha1 1024 abcdefg 3 aes sha1 1024 abcdefgh 3 aes sha1 1024 abcdefghi 3 aes sha1 1024 abcdefghijklmno 3 aes sha1 1024 abcdefghijklmnop 3 aes sha1 1024 abcdefghijklmnopq 3 aes sha1 1024 abcdefghijklmnopqr 3 aes sha1 1024 abcdefghijklmnopqrs 3 aes sha1 1024 abcdefghijklmnopqrst 3 aes sha1 1024 abcdefghijklmnopqrstu 3 aes sha1 1024 With_utf8_umlauts:äüÖß 3 aes sha1 1024 Long_sentence_used_as_passphrase 3 aes sha1 10240 Long_sentence_used_as_passphrase 3 aes sha1 102400 Long_sentence_used_as_passphrase 3 aes192 sha1 1024 a 3 aes192 sha1 1024 abcdefg 3 aes192 sha1 1024 abcdefghi 3 aes192 sha1 1024 abcdefghi 3 aes192 sha1 1024 Long_sentence_used_as_passphrase 3 aes256 sha1 1024 a 3 aes256 sha1 1024 abcdefg 3 aes256 sha1 1024 abcdefghi 3 aes256 sha1 1024 abcdefghi 3 aes256 sha1 1024 Long_sentence_used_as_passphrase 0 aes sha256 1024 Long_sentence_used_as_passphrase 1 aes sha256 1024 Long_sentence_used_as_passphrase 3 aes sha256 1024 Long_sentence_used_as_passphrase 3 aes sha256 10240 Long_sentence_used_as_passphrase 3 aes sha384 1024 Long_sentence_used_as_passphrase 3 aes sha512 1024 Long_sentence_used_as_passphrase 3 aes256 sha512 1024 Long_sentence_used_as_passphrase 3 3des sha512 1024 Long_sentence_used_as_passphrase ===>8=== and finally using a proper utf-8 enabled shell, run: cat x.inp | while read mode cipher digest count pass dummy; do \ ./gpg x.out */ static struct { const char *p; /* Passphrase. */ size_t plen; /* Length of P. */ int algo; int hashalgo; const char *salt; size_t saltlen; unsigned long c; /* Iterations. */ int dklen; /* Requested key length. */ const char *dk; /* Derived key. */ int disabled; } tv[] = { { "\x61", 1, GCRY_KDF_SIMPLE_S2K, GCRY_MD_MD5, NULL, 0, 0, 16, "\x0c\xc1\x75\xb9\xc0\xf1\xb6\xa8\x31\xc3\x99\xe2\x69\x77\x26\x61" }, { "\x61\x62", 2, GCRY_KDF_SIMPLE_S2K, GCRY_MD_MD5, NULL, 0, 0, 16, "\x18\x7e\xf4\x43\x61\x22\xd1\xcc\x2f\x40\xdc\x2b\x92\xf0\xeb\xa0" }, { "\x61\x62\x63", 3, GCRY_KDF_SIMPLE_S2K, GCRY_MD_MD5, NULL, 0, 0, 16, "\x90\x01\x50\x98\x3c\xd2\x4f\xb0\xd6\x96\x3f\x7d\x28\xe1\x7f\x72" }, { "\x61\x62\x63\x64", 4, GCRY_KDF_SIMPLE_S2K, GCRY_MD_MD5, NULL, 0, 0, 16, "\xe2\xfc\x71\x4c\x47\x27\xee\x93\x95\xf3\x24\xcd\x2e\x7f\x33\x1f" }, { "\x61\x62\x63\x64\x65", 5, GCRY_KDF_SIMPLE_S2K, GCRY_MD_MD5, NULL, 0, 0, 16, "\xab\x56\xb4\xd9\x2b\x40\x71\x3a\xcc\x5a\xf8\x99\x85\xd4\xb7\x86" }, { "\x61\x62\x63\x64\x65\x66", 6, GCRY_KDF_SIMPLE_S2K, GCRY_MD_MD5, NULL, 0, 0, 16, "\xe8\x0b\x50\x17\x09\x89\x50\xfc\x58\xaa\xd8\x3c\x8c\x14\x97\x8e" }, { "\x61\x62\x63\x64\x65\x66\x67", 7, GCRY_KDF_SIMPLE_S2K, GCRY_MD_MD5, NULL, 0, 0, 16, "\x7a\xc6\x6c\x0f\x14\x8d\xe9\x51\x9b\x8b\xd2\x64\x31\x2c\x4d\x64" }, { "\x61\x62\x63\x64\x65\x66\x67\x68", 8, GCRY_KDF_SIMPLE_S2K, GCRY_MD_MD5, NULL, 0, 0, 16, "\xe8\xdc\x40\x81\xb1\x34\x34\xb4\x51\x89\xa7\x20\xb7\x7b\x68\x18" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69", 9, GCRY_KDF_SIMPLE_S2K, GCRY_MD_MD5, NULL, 0, 0, 16, "\x8a\xa9\x9b\x1f\x43\x9f\xf7\x12\x93\xe9\x53\x57\xba\xc6\xfd\x94" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f", 15, GCRY_KDF_SIMPLE_S2K, GCRY_MD_MD5, NULL, 0, 0, 16, "\x8a\x73\x19\xdb\xf6\x54\x4a\x74\x22\xc9\xe2\x54\x52\x58\x0e\xa5" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70", 16, GCRY_KDF_SIMPLE_S2K, GCRY_MD_MD5, NULL, 0, 0, 16, "\x1d\x64\xdc\xe2\x39\xc4\x43\x7b\x77\x36\x04\x1d\xb0\x89\xe1\xb9" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" "\x71", 17, GCRY_KDF_SIMPLE_S2K, GCRY_MD_MD5, NULL, 0, 0, 16, "\x9a\x8d\x98\x45\xa6\xb4\xd8\x2d\xfc\xb2\xc2\xe3\x51\x62\xc8\x30" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_SIMPLE_S2K, GCRY_MD_MD5, NULL, 0, 0, 16, "\x35\x2a\xf0\xfc\xdf\xe9\xbb\x62\x16\xfc\x99\x9d\x8d\x58\x05\xcb" }, { "\x57\x69\x74\x68\x5f\x75\x74\x66\x38\x5f\x75\x6d\x6c\x61\x75\x74" "\x73\x3a\xc3\xa4\xc3\xbc\xc3\x96\xc3\x9f", 26, GCRY_KDF_SIMPLE_S2K, GCRY_MD_MD5, NULL, 0, 0, 16, "\x21\xa4\xeb\xd8\xfd\xf0\x59\x25\xd1\x32\x31\xdb\xe7\xf2\x13\x5d" }, { "\x61", 1, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\x86\xf7\xe4\x37\xfa\xa5\xa7\xfc\xe1\x5d\x1d\xdc\xb9\xea\xea\xea" }, { "\x61\x62", 2, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\xda\x23\x61\x4e\x02\x46\x9a\x0d\x7c\x7b\xd1\xbd\xab\x5c\x9c\x47" }, { "\x61\x62\x63", 3, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\xa9\x99\x3e\x36\x47\x06\x81\x6a\xba\x3e\x25\x71\x78\x50\xc2\x6c" }, { "\x61\x62\x63\x64", 4, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\x81\xfe\x8b\xfe\x87\x57\x6c\x3e\xcb\x22\x42\x6f\x8e\x57\x84\x73" }, { "\x61\x62\x63\x64\x65", 5, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\x03\xde\x6c\x57\x0b\xfe\x24\xbf\xc3\x28\xcc\xd7\xca\x46\xb7\x6e" }, { "\x61\x62\x63\x64\x65\x66", 6, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\x1f\x8a\xc1\x0f\x23\xc5\xb5\xbc\x11\x67\xbd\xa8\x4b\x83\x3e\x5c" }, { "\x61\x62\x63\x64\x65\x66\x67", 7, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\x2f\xb5\xe1\x34\x19\xfc\x89\x24\x68\x65\xe7\xa3\x24\xf4\x76\xec" }, { "\x61\x62\x63\x64\x65\x66\x67\x68", 8, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\x42\x5a\xf1\x2a\x07\x43\x50\x2b\x32\x2e\x93\xa0\x15\xbc\xf8\x68" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69", 9, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\xc6\x3b\x19\xf1\xe4\xc8\xb5\xf7\x6b\x25\xc4\x9b\x8b\x87\xf5\x7d" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f", 15, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\x29\x38\xdc\xc2\xe3\xaa\x77\x98\x7c\x7e\x5d\x4a\x0f\x26\x96\x67" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70", 16, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\x14\xf3\x99\x52\x88\xac\xd1\x89\xe6\xe5\x0a\x7a\xf4\x7e\xe7\x09" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" "\x71", 17, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\xd8\x3d\x62\x1f\xcd\x2d\x4d\x29\x85\x54\x70\x43\xa7\xa5\xfd\x4d" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" "\x71\x72", 18, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\xe3\x81\xfe\x42\xc5\x7e\x48\xa0\x82\x17\x86\x41\xef\xfd\x1c\xb9" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" "\x71\x72\x73", 19, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\x89\x3e\x69\xff\x01\x09\xf3\x45\x9c\x42\x43\x01\x3b\x3d\xe8\xb1" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" "\x71\x72\x73\x74", 20, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\x14\xa2\x3a\xd7\x0f\x2a\x5d\xd7\x25\x57\x5d\xe6\xc4\x3e\x1c\xdd" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" "\x71\x72\x73\x74\x75", 21, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\xec\xa9\x86\xb9\x5d\x58\x7f\x34\xd7\x1c\xa7\x75\x2a\x4e\x00\x10" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\x3e\x1b\x9a\x50\x7d\x6e\x9a\xd8\x93\x64\x96\x7a\x3f\xcb\x27\x3f" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 32, "\x3e\x1b\x9a\x50\x7d\x6e\x9a\xd8\x93\x64\x96\x7a\x3f\xcb\x27\x3f" "\xc3\x7b\x3a\xb2\xef\x4d\x68\xaa\x9c\xd7\xe4\x88\xee\xd1\x5e\x70" }, { "\x57\x69\x74\x68\x5f\x75\x74\x66\x38\x5f\x75\x6d\x6c\x61\x75\x74" "\x73\x3a\xc3\xa4\xc3\xbc\xc3\x96\xc3\x9f", 26, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\xe0\x4e\x1e\xe3\xad\x0b\x49\x7c\x7a\x5f\x37\x3b\x4d\x90\x3c\x2e" }, { "\x61", 1, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x6d\x47\xe3\x68\x5d\x2c\x36\x16", 8, 1024, 16, "\x41\x9f\x48\x6e\xbf\xe6\xdd\x05\x9a\x72\x23\x17\x44\xd8\xd3\xf3" }, { "\x61\x62", 2, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x7c\x34\x78\xfb\x28\x2d\x25\xc7", 8, 1024, 16, "\x0a\x9d\x09\x06\x43\x3d\x4f\xf9\x87\xd6\xf7\x48\x90\xde\xd1\x1c" }, { "\x61\x62\x63", 3, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\xc3\x16\x37\x2e\x27\xf6\x9f\x6f", 8, 1024, 16, "\xf8\x27\xa0\x07\xc6\xcb\xdd\xf1\xfe\x5c\x88\x3a\xfc\xcd\x84\x4d" }, { "\x61\x62\x63\x64", 4, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\xf0\x0c\x73\x38\xb7\xc3\xd5\x14", 8, 1024, 16, "\x9b\x5f\x26\xba\x52\x3b\xcd\xd9\xa5\x2a\xef\x3c\x03\x4d\xd1\x52" }, { "\x61\x62\x63\x64\x65", 5, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\xe1\x7d\xa2\x36\x09\x59\xee\xc5", 8, 1024, 16, "\x94\x9d\x5b\x1a\x5a\x66\x8c\xfa\x8f\x6f\x22\xaf\x8b\x60\x9f\xaf" }, { "\x61\x62\x63\x64\x65\x66", 6, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\xaf\xa7\x0c\x68\xdf\x7e\xaa\x27", 8, 1024, 16, "\xe5\x38\xf4\x39\x62\x27\xcd\xcc\x91\x37\x7f\x1b\xdc\x58\x64\x27" }, { "\x61\x62\x63\x64\x65\x66\x67", 7, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x40\x57\xb2\x9d\x5f\xbb\x11\x4f", 8, 1024, 16, "\xad\xa2\x33\xd9\xdd\xe0\xfb\x94\x8e\xcc\xec\xcc\xb3\xa8\x3a\x9e" }, { "\x61\x62\x63\x64\x65\x66\x67\x68", 8, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x38\xf5\x65\xc5\x0f\x8c\x19\x61", 8, 1024, 16, "\xa0\xb0\x3e\x29\x76\xe6\x8f\xa0\xd8\x34\x8f\xa4\x2d\xfd\x65\xee" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69", 9, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\xc3\xb7\x99\xcc\xda\x2d\x05\x7b", 8, 1024, 16, "\x27\x21\xc8\x99\x5f\xcf\x20\xeb\xf2\xd9\xff\x6a\x69\xff\xad\xe8" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f", 15, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x7d\xd8\x68\x8a\x1c\xc5\x47\x22", 8, 1024, 16, "\x0f\x96\x7a\x12\x23\x54\xf6\x92\x61\x67\x07\xb4\x68\x17\xb8\xaa" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70", 16, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x8a\x95\xd4\x88\x0b\xb8\xe9\x9d", 8, 1024, 16, "\xcc\xe4\xc8\x82\x53\x32\xf1\x93\x5a\x00\xd4\x7f\xd4\x46\xfa\x07" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" "\x71", 17, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\xb5\x22\x48\xa6\xc4\xad\x74\x67", 8, 1024, 16, "\x0c\xe3\xe0\xee\x3d\x8f\x35\xd2\x35\x14\x14\x29\x0c\xf1\xe3\x34" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" "\x71\x72", 18, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\xac\x9f\x04\x63\x83\x0e\x3c\x95", 8, 1024, 16, "\x49\x0a\x04\x68\xa8\x2a\x43\x6f\xb9\x73\x94\xb4\x85\x9a\xaa\x0e" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" "\x71\x72\x73", 19, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x03\x6f\x60\x30\x3a\x19\x61\x0d", 8, 1024, 16, "\x15\xe5\x9b\xbf\x1c\xf0\xbe\x74\x95\x1a\xb2\xc4\xda\x09\xcd\x99" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" "\x71\x72\x73\x74", 20, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x51\x40\xa5\x57\xf5\x28\xfd\x03", 8, 1024, 16, "\xa6\xf2\x7e\x6b\x30\x4d\x8d\x67\xd4\xa2\x7f\xa2\x57\x27\xab\x96" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" "\x71\x72\x73\x74\x75", 21, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x4c\xf1\x10\x11\x04\x70\xd3\x6e", 8, 1024, 16, "\x2c\x50\x79\x8d\x83\x23\xac\xd6\x22\x29\x37\xaf\x15\x0d\xdd\x8f" }, { "\x57\x69\x74\x68\x5f\x75\x74\x66\x38\x5f\x75\x6d\x6c\x61\x75\x74" "\x73\x3a\xc3\xa4\xc3\xbc\xc3\x96\xc3\x9f", 26, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\xfe\x3a\x25\xcb\x78\xef\xe1\x21", 8, 1024, 16, "\x2a\xb0\x53\x08\xf3\x2f\xd4\x6e\xeb\x01\x49\x5d\x87\xf6\x27\xf6" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x04\x97\xd0\x02\x6a\x44\x2d\xde", 8, 1024, 16, "\x57\xf5\x70\x41\xa0\x9b\x8c\x09\xca\x74\xa9\x22\xa5\x82\x2d\x17" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\xdd\xf3\x31\x7c\xce\xf4\x81\x26", 8, 10240, 16, "\xc3\xdd\x01\x6d\xaf\xf6\x58\xc8\xd7\x79\xb4\x40\x00\xb5\xe8\x0b" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x95\xd6\x72\x4e\xfb\xe1\xc3\x1a", 8, 102400, 16, "\xf2\x3f\x36\x7f\xb4\x6a\xd0\x3a\x31\x9e\x65\x11\x8e\x2b\x99\x9b" }, { "\x61", 1, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x6d\x69\x15\x18\xe4\x13\x42\x82", 8, 1024, 24, "\x28\x0c\x7e\xf2\x31\xf6\x1c\x6b\x5c\xef\x6a\xd5\x22\x64\x97\x91" "\xe3\x5e\x37\xfd\x50\xe2\xfc\x6c" }, { "\x61\x62\x63\x64\x65\x66\x67", 7, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x9b\x76\x5e\x81\xde\x13\xdf\x15", 8, 1024, 24, "\x91\x1b\xa1\xc1\x7b\x4f\xc3\xb1\x80\x61\x26\x08\xbe\x53\xe6\x50" "\x40\x6f\x28\xed\xc6\xe6\x67\x55" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69", 9, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x7a\xac\xcc\x6e\x15\x56\xbd\xa1", 8, 1024, 24, "\xfa\x7e\x20\x07\xb6\x47\xb0\x09\x46\xb8\x38\xfb\xa1\xaf\xf7\x75" "\x2a\xfa\x77\x14\x06\x54\xcb\x34" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69", 9, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x1c\x68\xf8\xfb\x98\xf7\x8c\x39", 8, 1024, 24, "\xcb\x1e\x86\xf5\xe0\xe4\xfb\xbf\x71\x34\x99\x24\xf4\x39\x8c\xc2" "\x8e\x25\x1c\x4c\x96\x47\x22\xe8" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x10\xa9\x4e\xc1\xa5\xec\x17\x52", 8, 1024, 24, "\x0f\x83\xa2\x77\x92\xbb\xe4\x58\x68\xc5\xf2\x14\x6e\x6e\x2e\x6b" "\x98\x17\x70\x92\x07\x44\xe0\x51" }, { "\x61", 1, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\xef\x8f\x37\x61\x8f\xab\xae\x4f", 8, 1024, 32, "\x6d\x65\xae\x86\x23\x91\x39\x98\xec\x1c\x23\x44\xb6\x0d\xad\x32" "\x54\x46\xc7\x23\x26\xbb\xdf\x4b\x54\x6e\xd4\xc2\xfa\xc6\x17\x17" }, { "\x61\x62\x63\x64\x65\x66\x67", 7, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\xaa\xfb\xd9\x06\x7d\x7c\x40\xaf", 8, 1024, 32, "\x7d\x10\x54\x13\x3c\x43\x7a\xb3\x54\x1f\x38\xd4\x8f\x70\x0a\x09" "\xe2\xfa\xab\x97\x9a\x70\x16\xef\x66\x68\xca\x34\x2e\xce\xfa\x1f" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69", 9, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x58\x03\x4f\x56\x8b\x97\xd4\x98", 8, 1024, 32, "\xf7\x40\xb1\x25\x86\x0d\x35\x8f\x9f\x91\x2d\xce\x04\xee\x5a\x04" "\x9d\xbd\x44\x23\x4c\xa6\xbb\xab\xb0\xd0\x56\x82\xa9\xda\x47\x16" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69", 9, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x5d\x41\x3d\xa3\xa7\xfc\x5d\x0c", 8, 1024, 32, "\x4c\x7a\x86\xed\x81\x8a\x94\x99\x7d\x4a\xc4\xf7\x1c\xf8\x08\xdb" "\x09\x35\xd9\xa3\x2d\x22\xde\x32\x2d\x74\x38\xe5\xc8\xf2\x50\x6e" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\xca\xa7\xdc\x59\xce\x31\xe7\x49", 8, 1024, 32, "\x67\xe9\xd6\x29\x49\x1c\xb6\xa0\x85\xe8\xf9\x8b\x85\x47\x3a\x7e" "\xa7\xee\x89\x52\x6f\x19\x00\x53\x93\x07\x0a\x8b\xb9\xa8\x86\x94" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA256, NULL, 0, 0, 16, "\x88\x36\x78\x6b\xd9\x5a\x62\xff\x47\xd3\xfb\x79\xc9\x08\x70\x56" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_SALTED_S2K, GCRY_MD_SHA256, "\x05\x8b\xfe\x31\xaa\xf3\x29\x11", 8, 0, 16, "\xb2\x42\xfe\x5e\x09\x02\xd9\x62\xb9\x35\xf3\xa8\x43\x80\x9f\xb1" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA256, "\xd3\x4a\xea\xc9\x97\x1b\xcc\x83", 8, 1024, 16, "\x35\x37\x99\x62\x07\x26\x68\x23\x05\x47\xb2\xa0\x0b\x2b\x2b\x8d" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA256, "\x5e\x71\xbd\x00\x5f\x96\xc4\x23", 8, 10240, 16, "\xa1\x6a\xee\xba\xde\x73\x25\x25\xd1\xab\xa0\xc5\x7e\xc6\x39\xa7" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA384, "\xc3\x08\xeb\x17\x62\x08\x89\xef", 8, 1024, 16, "\x9b\x7f\x0c\x81\x6f\x71\x59\x9b\xd5\xf6\xbf\x3a\x86\x20\x16\x33" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA512, "\xe6\x7d\x13\x6b\x39\xe3\x44\x05", 8, 1024, 16, "\xc8\xcd\x4b\xa4\xf3\xf1\xd5\xb0\x59\x06\xf0\xbb\x89\x34\x6a\xad" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA512, "\xed\x7d\x30\x47\xe4\xc3\xf8\xb6", 8, 1024, 32, "\x89\x7a\xef\x70\x97\xe7\x10\xdb\x75\xcc\x20\x22\xab\x7b\xf3\x05" "\x4b\xb6\x2e\x17\x11\x9f\xd6\xeb\xbf\xdf\x4d\x70\x59\xf0\xf9\xe5" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA512, "\xbb\x1a\x45\x30\x68\x62\x6d\x63", 8, 1024, 24, "\xde\x5c\xb8\xd5\x75\xf6\xad\x69\x5b\xc9\xf6\x2f\xba\xeb\xfb\x36" "\x34\xf2\xb8\xee\x3b\x37\x21\xb7" } }; int tvidx; gpg_error_t err; unsigned char outbuf[32]; int i; for (tvidx=0; tvidx < DIM(tv); tvidx++) { if (tv[tvidx].disabled) continue; /* MD5 isn't supported in fips mode */ if (in_fips_mode && tv[tvidx].hashalgo == GCRY_MD_MD5) continue; if (gcry_md_test_algo (tv[tvidx].hashalgo) != 0) continue; if (verbose) fprintf (stderr, "checking S2K test vector %d\n", tvidx); assert (tv[tvidx].dklen <= sizeof outbuf); err = gcry_kdf_derive (tv[tvidx].p, tv[tvidx].plen, tv[tvidx].algo, tv[tvidx].hashalgo, tv[tvidx].salt, tv[tvidx].saltlen, tv[tvidx].c, tv[tvidx].dklen, outbuf); if (err) fail ("s2k test %d failed: %s\n", tvidx, gpg_strerror (err)); else if (memcmp (outbuf, tv[tvidx].dk, tv[tvidx].dklen)) { fail ("s2k test %d failed: mismatch\n", tvidx); fputs ("got:", stderr); for (i=0; i < tv[tvidx].dklen; i++) fprintf (stderr, " %02x", outbuf[i]); putc ('\n', stderr); } } } static void check_pbkdf2 (void) { /* Test vectors are from RFC-6070. */ static struct { 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[] = { { "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" }, { "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" }, { "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" }, { "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. */ }, { "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" }, { "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 */ "", 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" }, { "password", 8, "salt", 4, GCRY_MD_GOSTR3411_CP, 1, 32, "\x73\x14\xe7\xc0\x4f\xb2\xe6\x62\xc5\x43\x67\x42\x53\xf6\x8b\xd0" "\xb7\x34\x45\xd0\x7f\x24\x1b\xed\x87\x28\x82\xda\x21\x66\x2d\x58" }, { "password", 8, "salt", 4, GCRY_MD_GOSTR3411_CP, 2, 32, "\x99\x0d\xfa\x2b\xd9\x65\x63\x9b\xa4\x8b\x07\xb7\x92\x77\x5d\xf7" "\x9f\x2d\xb3\x4f\xef\x25\xf2\x74\x37\x88\x72\xfe\xd7\xed\x1b\xb3" }, { "password", 8, "salt", 4, GCRY_MD_GOSTR3411_CP, 4096, 32, "\x1f\x18\x29\xa9\x4b\xdf\xf5\xbe\x10\xd0\xae\xb3\x6a\xf4\x98\xe7" "\xa9\x74\x67\xf3\xb3\x11\x16\xa5\xa7\xc1\xaf\xff\x9d\xea\xda\xfe" }, /* { -- takes too long (4-5 min) to calculate "password", 8, "salt", 4, GCRY_MD_GOSTR3411_CP, 16777216, 32, "\xa5\x7a\xe5\xa6\x08\x83\x96\xd1\x20\x85\x0c\x5c\x09\xde\x0a\x52" "\x51\x00\x93\x8a\x59\xb1\xb5\xc3\xf7\x81\x09\x10\xd0\x5f\xcd\x97" }, */ { "passwordPASSWORDpassword", 24, "saltSALTsaltSALTsaltSALTsaltSALTsalt", 36, GCRY_MD_GOSTR3411_CP, 4096, 40, "\x78\x83\x58\xc6\x9c\xb2\xdb\xe2\x51\xa7\xbb\x17\xd5\xf4\x24\x1f" "\x26\x5a\x79\x2a\x35\xbe\xcd\xe8\xd5\x6f\x32\x6b\x49\xc8\x50\x47" "\xb7\x63\x8a\xcb\x47\x64\xb1\xfd" }, { "pass\0word", 9, "sa\0lt", 5, GCRY_MD_GOSTR3411_CP, 4096, 20, "\x43\xe0\x6c\x55\x90\xb0\x8c\x02\x25\x24" "\x23\x73\x12\x7e\xdf\x9c\x8e\x9c\x32\x91" }, { "password", 8, "salt", 4, GCRY_MD_STRIBOG512, 1, 64, "\x64\x77\x0a\xf7\xf7\x48\xc3\xb1\xc9\xac\x83\x1d\xbc\xfd\x85\xc2" "\x61\x11\xb3\x0a\x8a\x65\x7d\xdc\x30\x56\xb8\x0c\xa7\x3e\x04\x0d" "\x28\x54\xfd\x36\x81\x1f\x6d\x82\x5c\xc4\xab\x66\xec\x0a\x68\xa4" "\x90\xa9\xe5\xcf\x51\x56\xb3\xa2\xb7\xee\xcd\xdb\xf9\xa1\x6b\x47" }, { "password", 8, "salt", 4, GCRY_MD_STRIBOG512, 2, 64, "\x5a\x58\x5b\xaf\xdf\xbb\x6e\x88\x30\xd6\xd6\x8a\xa3\xb4\x3a\xc0" "\x0d\x2e\x4a\xeb\xce\x01\xc9\xb3\x1c\x2c\xae\xd5\x6f\x02\x36\xd4" "\xd3\x4b\x2b\x8f\xbd\x2c\x4e\x89\xd5\x4d\x46\xf5\x0e\x47\xd4\x5b" "\xba\xc3\x01\x57\x17\x43\x11\x9e\x8d\x3c\x42\xba\x66\xd3\x48\xde" }, { "password", 8, "salt", 4, GCRY_MD_STRIBOG512, 4096, 64, "\xe5\x2d\xeb\x9a\x2d\x2a\xaf\xf4\xe2\xac\x9d\x47\xa4\x1f\x34\xc2" "\x03\x76\x59\x1c\x67\x80\x7f\x04\x77\xe3\x25\x49\xdc\x34\x1b\xc7" "\x86\x7c\x09\x84\x1b\x6d\x58\xe2\x9d\x03\x47\xc9\x96\x30\x1d\x55" "\xdf\x0d\x34\xe4\x7c\xf6\x8f\x4e\x3c\x2c\xda\xf1\xd9\xab\x86\xc3" }, /* { -- takes toooo long "password", 8, "salt", 4, GCRY_MD_STRIBOG512, 16777216, 64, "\x49\xe4\x84\x3b\xba\x76\xe3\x00\xaf\xe2\x4c\x4d\x23\xdc\x73\x92" "\xde\xf1\x2f\x2c\x0e\x24\x41\x72\x36\x7c\xd7\x0a\x89\x82\xac\x36" "\x1a\xdb\x60\x1c\x7e\x2a\x31\x4e\x8c\xb7\xb1\xe9\xdf\x84\x0e\x36" "\xab\x56\x15\xbe\x5d\x74\x2b\x6c\xf2\x03\xfb\x55\xfd\xc4\x80\x71" }, */ { "passwordPASSWORDpassword", 24, "saltSALTsaltSALTsaltSALTsaltSALTsalt", 36, GCRY_MD_STRIBOG512, 4096, 100, "\xb2\xd8\xf1\x24\x5f\xc4\xd2\x92\x74\x80\x20\x57\xe4\xb5\x4e\x0a" "\x07\x53\xaa\x22\xfc\x53\x76\x0b\x30\x1c\xf0\x08\x67\x9e\x58\xfe" "\x4b\xee\x9a\xdd\xca\xe9\x9b\xa2\xb0\xb2\x0f\x43\x1a\x9c\x5e\x50" "\xf3\x95\xc8\x93\x87\xd0\x94\x5a\xed\xec\xa6\xeb\x40\x15\xdf\xc2" "\xbd\x24\x21\xee\x9b\xb7\x11\x83\xba\x88\x2c\xee\xbf\xef\x25\x9f" "\x33\xf9\xe2\x7d\xc6\x17\x8c\xb8\x9d\xc3\x74\x28\xcf\x9c\xc5\x2a" "\x2b\xaa\x2d\x3a" }, { "pass\0word", 9, "sa\0lt", 5, GCRY_MD_STRIBOG512, 4096, 64, "\x50\xdf\x06\x28\x85\xb6\x98\x01\xa3\xc1\x02\x48\xeb\x0a\x27\xab" "\x6e\x52\x2f\xfe\xb2\x0c\x99\x1c\x66\x0f\x00\x14\x75\xd7\x3a\x4e" "\x16\x7f\x78\x2c\x18\xe9\x7e\x92\x97\x6d\x9c\x1d\x97\x08\x31\xea" "\x78\xcc\xb8\x79\xf6\x70\x68\xcd\xac\x19\x10\x74\x08\x44\xe8\x30" } }; int tvidx; gpg_error_t err; unsigned char outbuf[100]; int i; for (tvidx=0; tvidx < DIM(tv); tvidx++) { if (tv[tvidx].disabled) continue; if (gcry_md_test_algo (tv[tvidx].hashalgo) != 0) continue; if (verbose) fprintf (stderr, "checking PBKDF2 test vector %d algo %d\n", tvidx, tv[tvidx].hashalgo); assert (tv[tvidx].dklen <= sizeof outbuf); err = gcry_kdf_derive (tv[tvidx].p, tv[tvidx].plen, GCRY_KDF_PBKDF2, tv[tvidx].hashalgo, tv[tvidx].salt, tv[tvidx].saltlen, tv[tvidx].c, tv[tvidx].dklen, outbuf); if (in_fips_mode && tvidx > 6) { if (!err) fail ("pbkdf2 test %d unexpectedly passed in FIPS mode: %s\n", tvidx, gpg_strerror (err)); continue; } if (err) { if (in_fips_mode && tv[tvidx].plen < 14) { if (verbose) fprintf (stderr, " shorter key (%u) rejected correctly in fips mode\n", (unsigned int)tv[tvidx].plen); } else fail ("pbkdf2 test %d failed: %s\n", tvidx, gpg_strerror (err)); } else if (memcmp (outbuf, tv[tvidx].dk, tv[tvidx].dklen)) { fail ("pbkdf2 test %d failed: mismatch\n", tvidx); fputs ("got:", stderr); for (i=0; i < tv[tvidx].dklen; i++) fprintf (stderr, " %02x", outbuf[i]); putc ('\n', stderr); } } } static void check_scrypt (void) { /* Test vectors are from draft-josefsson-scrypt-kdf-01. */ static struct { const char *p; /* Passphrase. */ size_t plen; /* Length of P. */ const char *salt; size_t saltlen; int parm_n; /* CPU/memory cost. */ int parm_r; /* blocksize */ unsigned long parm_p; /* parallelization. */ int dklen; /* Requested key length. */ const char *dk; /* Derived key. */ int disabled; } tv[] = { { "", 0, "", 0, 16, 1, 1, 64, "\x77\xd6\x57\x62\x38\x65\x7b\x20\x3b\x19\xca\x42\xc1\x8a\x04\x97" "\xf1\x6b\x48\x44\xe3\x07\x4a\xe8\xdf\xdf\xfa\x3f\xed\xe2\x14\x42" "\xfc\xd0\x06\x9d\xed\x09\x48\xf8\x32\x6a\x75\x3a\x0f\xc8\x1f\x17" "\xe8\xd3\xe0\xfb\x2e\x0d\x36\x28\xcf\x35\xe2\x0c\x38\xd1\x89\x06" }, { "password", 8, "NaCl", 4, 1024, 8, 16, 64, "\xfd\xba\xbe\x1c\x9d\x34\x72\x00\x78\x56\xe7\x19\x0d\x01\xe9\xfe" "\x7c\x6a\xd7\xcb\xc8\x23\x78\x30\xe7\x73\x76\x63\x4b\x37\x31\x62" "\x2e\xaf\x30\xd9\x2e\x22\xa3\x88\x6f\xf1\x09\x27\x9d\x98\x30\xda" "\xc7\x27\xaf\xb9\x4a\x83\xee\x6d\x83\x60\xcb\xdf\xa2\xcc\x06\x40" }, { "pleaseletmein", 13, "SodiumChloride", 14, 16384, 8, 1, 64, "\x70\x23\xbd\xcb\x3a\xfd\x73\x48\x46\x1c\x06\xcd\x81\xfd\x38\xeb" "\xfd\xa8\xfb\xba\x90\x4f\x8e\x3e\xa9\xb5\x43\xf6\x54\x5d\xa1\xf2" "\xd5\x43\x29\x55\x61\x3f\x0f\xcf\x62\xd4\x97\x05\x24\x2a\x9a\xf9" "\xe6\x1e\x85\xdc\x0d\x65\x1e\x40\xdf\xcf\x01\x7b\x45\x57\x58\x87" }, { "pleaseletmein", 13, "SodiumChloride", 14, 1048576, 8, 1, 64, "\x21\x01\xcb\x9b\x6a\x51\x1a\xae\xad\xdb\xbe\x09\xcf\x70\xf8\x81" "\xec\x56\x8d\x57\x4a\x2f\xfd\x4d\xab\xe5\xee\x98\x20\xad\xaa\x47" "\x8e\x56\xfd\x8f\x4b\xa5\xd0\x9f\xfa\x1c\x6d\x92\x7c\x40\xf4\xc3" "\x37\x30\x40\x49\xe8\xa9\x52\xfb\xcb\xf4\x5c\x6f\xa7\x7a\x41\xa4", 2 /* Only in debug mode. */ } }; int tvidx; gpg_error_t err; unsigned char outbuf[64]; int i; for (tvidx=0; tvidx < DIM(tv); tvidx++) { if (tv[tvidx].disabled && !(tv[tvidx].disabled == 2 && debug)) continue; if (verbose) fprintf (stderr, "checking SCRYPT test vector %d\n", tvidx); assert (tv[tvidx].dklen <= sizeof outbuf); err = gcry_kdf_derive (tv[tvidx].p, tv[tvidx].plen, tv[tvidx].parm_r == 1 ? 41 : GCRY_KDF_SCRYPT, tv[tvidx].parm_n, tv[tvidx].salt, tv[tvidx].saltlen, tv[tvidx].parm_p, tv[tvidx].dklen, outbuf); if (err) { if (in_fips_mode && tv[tvidx].plen < 14) { if (verbose) fprintf (stderr, " shorter key (%u) rejected correctly in fips mode\n", (unsigned int)tv[tvidx].plen); } else fail ("scrypt test %d failed: %s\n", tvidx, gpg_strerror (err)); } else if (memcmp (outbuf, tv[tvidx].dk, tv[tvidx].dklen)) { fail ("scrypt test %d failed: mismatch\n", tvidx); fputs ("got:", stderr); for (i=0; i < tv[tvidx].dklen; i++) fprintf (stderr, " %02x", outbuf[i]); putc ('\n', stderr); } } } #ifdef HAVE_PTHREAD #include #define MAX_THREADS 8 struct user_defined_threads_ctx { int oldest_thread_idx; int next_thread_idx; int num_threads_running; pthread_attr_t attr; pthread_t thread[MAX_THREADS]; struct job_thread_param { gcry_kdf_job_fn_t job; void *priv; } work[MAX_THREADS]; }; static void * job_thread (void *p) { struct job_thread_param *param = p; param->job (param->priv); pthread_exit (NULL); } static int wait_all_jobs_completion (void *jobs_context); static int pthread_jobs_launch_job (void *jobs_context, gcry_kdf_job_fn_t job, void *job_priv) { struct user_defined_threads_ctx *ctx = jobs_context; int ret; if (ctx->next_thread_idx == ctx->oldest_thread_idx) { assert (ctx->num_threads_running == MAX_THREADS); /* thread limit reached, join a thread */ ret = pthread_join (ctx->thread[ctx->oldest_thread_idx], NULL); if (ret) return -1; ctx->oldest_thread_idx = (ctx->oldest_thread_idx + 1) % MAX_THREADS; ctx->num_threads_running--; } ctx->work[ctx->next_thread_idx].job = job; ctx->work[ctx->next_thread_idx].priv = job_priv; ret = pthread_create (&ctx->thread[ctx->next_thread_idx], &ctx->attr, job_thread, &ctx->work[ctx->next_thread_idx]); if (ret) { /* could not create new thread. */ (void)wait_all_jobs_completion (jobs_context); return -1; } if (ctx->oldest_thread_idx < 0) ctx->oldest_thread_idx = ctx->next_thread_idx; ctx->next_thread_idx = (ctx->next_thread_idx + 1) % MAX_THREADS; ctx->num_threads_running++; return 0; } static int wait_all_jobs_completion (void *jobs_context) { struct user_defined_threads_ctx *ctx = jobs_context; int i, idx; int ret; for (i = 0; i < ctx->num_threads_running; i++) { idx = (ctx->oldest_thread_idx + i) % MAX_THREADS; ret = pthread_join (ctx->thread[idx], NULL); if (ret) return -1; } /* reset context for next round of parallel work */ ctx->num_threads_running = 0; ctx->oldest_thread_idx = -1; ctx->next_thread_idx = 0; return 0; } #endif static gcry_error_t my_kdf_derive (int parallel, int algo, int subalgo, const unsigned long *params, unsigned int paramslen, const char *pass, size_t passlen, const char *salt, size_t saltlen, const char *key, size_t keylen, const char *ad, size_t adlen, size_t outlen, unsigned char *out) { gcry_error_t err; gcry_kdf_hd_t hd; (void)parallel; err = gcry_kdf_open (&hd, algo, subalgo, params, paramslen, pass, passlen, salt, saltlen, key, keylen, ad, adlen); if (err) return err; #ifdef HAVE_PTHREAD if (parallel) { struct user_defined_threads_ctx jobs_context; const gcry_kdf_thread_ops_t ops = { &jobs_context, pthread_jobs_launch_job, wait_all_jobs_completion }; memset (&jobs_context, 0, sizeof (struct user_defined_threads_ctx)); jobs_context.oldest_thread_idx = -1; if (pthread_attr_init (&jobs_context.attr)) { err = gpg_error_from_syserror (); gcry_kdf_close (hd); return err; } if (pthread_attr_setdetachstate (&jobs_context.attr, PTHREAD_CREATE_JOINABLE)) { err = gpg_error_from_syserror (); pthread_attr_destroy (&jobs_context.attr); gcry_kdf_close (hd); return err; } err = gcry_kdf_compute (hd, &ops); pthread_attr_destroy (&jobs_context. attr); } else #endif { err = gcry_kdf_compute (hd, NULL); } if (!err) err = gcry_kdf_final (hd, outlen, out); gcry_kdf_close (hd); return err; } static void check_argon2 (void) { gcry_error_t err; static struct { int subalgo; unsigned long param[4]; size_t passlen; const char *pass; size_t saltlen; const char *salt; size_t keylen; const char *key; size_t adlen; const char *ad; size_t dklen; const char *dk; } tv[] = { { GCRY_KDF_ARGON2D, { 32, 3, 32, 4 }, 32, "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01", 16, "\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02", 8, "\x03\x03\x03\x03\x03\x03\x03\x03", 12, "\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04", 32, "\x51\x2b\x39\x1b\x6f\x11\x62\x97\x53\x71\xd3\x09\x19\x73\x42\x94" "\xf8\x68\xe3\xbe\x39\x84\xf3\xc1\xa1\x3a\x4d\xb9\xfa\xbe\x4a\xcb" }, { GCRY_KDF_ARGON2I, { 32, 3, 32, 4 }, 32, "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01", 16, "\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02", 8, "\x03\x03\x03\x03\x03\x03\x03\x03", 12, "\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04", 32, "\xc8\x14\xd9\xd1\xdc\x7f\x37\xaa\x13\xf0\xd7\x7f\x24\x94\xbd\xa1" "\xc8\xde\x6b\x01\x6d\xd3\x88\xd2\x99\x52\xa4\xc4\x67\x2b\x6c\xe8" }, { GCRY_KDF_ARGON2ID, { 32, 3, 32, 4 }, 32, "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01", 16, "\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02", 8, "\x03\x03\x03\x03\x03\x03\x03\x03", 12, "\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04", 32, "\x0d\x64\x0d\xf5\x8d\x78\x76\x6c\x08\xc0\x37\xa3\x4a\x8b\x53\xc9" "\xd0\x1e\xf0\x45\x2d\x75\xb6\x5e\xb5\x25\x20\xe9\x6b\x01\xe6\x59" }, + { + /* empty password */ + GCRY_KDF_ARGON2I, + { 32, 3, 128, 1 }, + 0, NULL, + 16, + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + 0, NULL, + 0, NULL, + 32, + "\xbb\x1f\xf2\xb9\x9f\xd4\x4a\xd9\xdf\x7f\xb9\x54\x55\x9e\xb8\xeb" + "\xb5\x9d\xab\xce\x2e\x62\x9f\x9b\x89\x09\xfe\xde\x57\xcc\x63\x86" + }, + { + /* empty password */ + GCRY_KDF_ARGON2ID, + { 32, 3, 128, 1 }, + 0, NULL, + 16, + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + 0, NULL, + 0, NULL, + 32, + "\x09\x2f\x38\x35\xac\xb2\x43\x92\x93\xeb\xcd\xe8\x04\x16\x6a\x31" + "\xce\x14\xd4\x55\xdb\xd8\xf7\xe6\xb4\xf5\x9d\x64\x8e\xd0\x3a\xdb" + }, }; unsigned char out[32]; int i; int count; for (count = 0; count < DIM(tv); count++) { if (verbose) fprintf (stderr, "checking ARGON2 test vector %d\n", count); err = my_kdf_derive (0, GCRY_KDF_ARGON2, tv[count].subalgo, tv[count].param, 4, tv[count].pass, tv[count].passlen, tv[count].salt, tv[count].saltlen, tv[count].key, tv[count].keylen, tv[count].ad, tv[count].adlen, tv[count].dklen, out); if (err) fail ("argon2 test %d failed: %s\n", count*2+0, gpg_strerror (err)); else if (memcmp (out, tv[count].dk, tv[count].dklen)) { fail ("argon2 test %d failed: mismatch\n", count*2+0); fputs ("got:", stderr); for (i=0; i < tv[count].dklen; i++) fprintf (stderr, " %02x", out[i]); putc ('\n', stderr); } #ifdef HAVE_PTHREAD err = my_kdf_derive (1, GCRY_KDF_ARGON2, tv[count].subalgo, tv[count].param, 4, tv[count].pass, tv[count].passlen, tv[count].salt, tv[count].saltlen, tv[count].key, tv[count].keylen, tv[count].ad, tv[count].adlen, tv[count].dklen, out); if (err) fail ("argon2 test %d failed: %s\n", count*2+1, gpg_strerror (err)); else if (memcmp (out, tv[count].dk, tv[count].dklen)) { fail ("argon2 test %d failed: mismatch\n", count*2+1); fputs ("got:", stderr); for (i=0; i < tv[count].dklen; i++) fprintf (stderr, " %02x", out[i]); putc ('\n', stderr); } #endif } } static void check_balloon (void) { gcry_error_t err; /* Two test vectors generated by the research prototype implementation. $ balloon abcdefghijklmno t_cost = 1 s_cost = 1024 p_cost = 1 passwd = abcdefghijklmno Time total : 0.0527251 Hashes per sec : 18.9663 Output : $balloon$v=1$s=1024,t=1,p=1 $FRzqOiIuPvuoy55vGfKzyse+2f28F7m9iFHCctnEBwg= $NxOGNPyTPZzKiJjgj7H6pJDLIgR05HI7VaxJpxEao5Q= $ balloon -t 12 -s 4096 -p 4 Long_sentence_used_as_passphrase t_cost = 12 s_cost = 4096 p_cost = 4 passwd = Long_sentence_used_as_passphrase Time total : 3.70399 Hashes per sec : 0.269979 Output : $balloon$v=1$s=4096,t=12,p=4 $8Yor74EqTwBrrdaeYeSVx0VXVAgDrsILAnJWdVUy93s= $FaNb9ofeWEggzhW9BUSODgZH5/awzNz5Adoub48+BgQ= */ static struct { int subalgo; unsigned long param[3]; size_t passlen; const char *pass; size_t saltlen; const char *salt; size_t dklen; const char *dk; } tv[] = { { GCRY_MD_SHA256, { 1024, 1, 1 }, 15, "abcdefghijklmno", 32, "\x15\x1c\xea\x3a\x22\x2e\x3e\xfb\xa8\xcb\x9e\x6f\x19\xf2\xb3\xca" "\xc7\xbe\xd9\xfd\xbc\x17\xb9\xbd\x88\x51\xc2\x72\xd9\xc4\x07\x08", 32, "\x37\x13\x86\x34\xfc\x93\x3d\x9c\xca\x88\x98\xe0\x8f\xb1\xfa\xa4" "\x90\xcb\x22\x04\x74\xe4\x72\x3b\x55\xac\x49\xa7\x11\x1a\xa3\x94" }, { GCRY_MD_SHA256, { 4096, 12, 4 }, 32, "Long_sentence_used_as_passphrase", 32, "\xf1\x8a\x2b\xef\x81\x2a\x4f\x00\x6b\xad\xd6\x9e\x61\xe4\x95\xc7" "\x45\x57\x54\x08\x03\xae\xc2\x0b\x02\x72\x56\x75\x55\x32\xf7\x7b", 32, "\x15\xa3\x5b\xf6\x87\xde\x58\x48\x20\xce\x15\xbd\x05\x44\x8e\x0e" "\x06\x47\xe7\xf6\xb0\xcc\xdc\xf9\x01\xda\x2e\x6f\x8f\x3e\x06\x04" } }; unsigned char out[32]; int i; int count; for (count = 0; count < DIM(tv); count++) { if (verbose) fprintf (stderr, "checking Balloon test vector %d\n", count); err = my_kdf_derive (0, GCRY_KDF_BALLOON, tv[count].subalgo, tv[count].param, 3, tv[count].pass, tv[count].passlen, tv[count].salt, tv[count].saltlen, NULL, 0, NULL, 0, tv[count].dklen, out); if (err) fail ("balloon test %d failed: %s\n", count*2+0, gpg_strerror (err)); else if (memcmp (out, tv[count].dk, tv[count].dklen)) { fail ("balloon test %d failed: mismatch\n", count*2+0); fputs ("got:", stderr); for (i=0; i < tv[count].dklen; i++) fprintf (stderr, " %02x", out[i]); putc ('\n', stderr); } #ifdef HAVE_PTHREAD err = my_kdf_derive (1, GCRY_KDF_BALLOON, tv[count].subalgo, tv[count].param, 3, tv[count].pass, tv[count].passlen, tv[count].salt, tv[count].saltlen, NULL, 0, NULL, 0, tv[count].dklen, out); if (err) fail ("balloon test %d failed: %s\n", count*2+1, gpg_strerror (err)); else if (memcmp (out, tv[count].dk, tv[count].dklen)) { fail ("balloon test %d failed: mismatch\n", count*2+1); fputs ("got:", stderr); for (i=0; i < tv[count].dklen; i++) fprintf (stderr, " %02x", out[i]); putc ('\n', stderr); } #endif } } static void check_onestep_kdf (void) { gcry_error_t err; static struct { int algo; int subalgo; unsigned long param[1]; size_t inputlen; const char *input; size_t otherlen; const char *other; size_t keylen; const char *key; size_t dklen; const char *dk; } tv[] = { { GCRY_KDF_ONESTEP_KDF, GCRY_MD_SHA256, { 38 }, 16, "\x3f\x89\x2b\xd8\xb8\x4d\xae\x64\xa7\x82\xa3\x5f\x6e\xaa\x8f\x00", 12, "\xec\x3f\x1c\xd8\x73\xd2\x88\x58\xa5\x8c\xc3\x9e", 0, NULL, 38, "\xa7\xc0\x66\x52\x98\x25\x25\x31\xe0\xdb\x37\x73\x7a\x37\x46\x51" "\xb3\x68\x27\x5f\x20\x48\x28\x4d\x16\xa1\x66\xc6\xd8\xa9\x0a\x91" "\xa4\x91\xc1\x6f\x49\x64" }, { GCRY_KDF_ONESTEP_KDF, GCRY_MD_SHA512, { 68 }, 16, "\xe6\x5b\x19\x05\x87\x8b\x95\xf6\x8b\x55\x35\xbd\x3b\x2b\x10\x13", 12, "\x83\x02\x21\xb1\x73\x0d\x91\x76\xf8\x07\xd4\x07", 0, NULL, 68, "\xb8\xc4\x4b\xdf\x0b\x85\xa6\x4b\x6a\x51\xc1\x2a\x06\x71\x0e\x37" "\x3d\x82\x9b\xb1\xfd\xa5\xb4\xe1\xa2\x07\x95\xc6\x19\x95\x94\xf6" "\xfa\x65\x19\x8a\x72\x12\x57\xf7\xd5\x8c\xb2\xf6\xf6\xdb\x9b\xb5" "\x69\x9f\x73\x86\x30\x45\x90\x90\x54\xb2\x38\x9e\x06\xec\x00\xfe" "\x31\x8c\xab\xd9" }, { GCRY_KDF_ONESTEP_KDF_MAC, GCRY_MAC_HMAC_SHA256, { 44 }, 16, "\x02\xb4\x0d\x33\xe3\xf6\x85\xae\xae\x67\x7a\xc3\x44\xee\xaf\x77", 12, "\xc6\x7c\x38\x95\x80\x12\x8f\x18\xf6\xcf\x85\x92", 16, "\x0a\xd5\x2c\x93\x57\xc8\x5e\x47\x81\x29\x6a\x36\xca\x72\x03\x9c", 44, "\xbe\x32\xe7\xd3\x06\xd8\x91\x02\x8b\xe0\x88\xf2\x13\xf9\xf9\x47" "\xc5\x04\x20\xd9\xb5\xa1\x2c\xa6\x98\x18\xdd\x99\x95\xde\xdd\x8e" "\x61\x37\xc7\x10\x4d\x67\xf2\xca\x90\x91\x5d\xda" }, { GCRY_KDF_ONESTEP_KDF_MAC, GCRY_MAC_HMAC_SHA512, { 56 }, 16, "\x8e\x5c\xd5\xf6\xae\x55\x8f\xfa\x04\xcd\xa2\xfa\xd9\x4d\xd6\x16", 12, "\x4a\x43\x30\x18\xe5\x1c\x09\xbb\xd6\x13\x26\xbb", 16, "\x6e\xd9\x3b\x6f\xe5\xb3\x50\x2b\xb4\x2b\x4c\x0f\xcb\x13\x36\x62", 56, "\x29\x5d\xfb\xeb\x54\xec\x0f\xe2\x4e\xce\x32\xf5\xb8\x7c\x85\x3e" "\x69\x9a\x62\xe3\x9d\x9c\x9e\xe6\xee\x78\xf8\xb9\xa0\xee\x50\xa3" "\x6a\x82\xe6\x06\x2c\x95\xed\x53\xbc\x36\x67\x00\xe2\xd0\xe0\x93" "\xbf\x75\x2e\xea\x42\x99\x47\x2e" }, }; unsigned char out[68]; int i; int count; for (count = 0; count < DIM(tv); count++) { if (verbose) fprintf (stderr, "checking OneStepKDF test vector %d\n", count); err = my_kdf_derive (0, tv[count].algo, tv[count].subalgo, tv[count].param, 1, tv[count].input, tv[count].inputlen, NULL, 0, tv[count].key, tv[count].keylen, tv[count].other, tv[count].otherlen, tv[count].dklen, out); if (err) fail ("OneStepKDF test %d failed: %s\n", count, gpg_strerror (err)); else if (memcmp (out, tv[count].dk, tv[count].dklen)) { fail ("OneStepKDF test %d failed: mismatch\n", count); fputs ("got:", stderr); for (i=0; i < tv[count].dklen; i++) fprintf (stderr, " %02x", out[i]); putc ('\n', stderr); } } } static void check_hkdf (void) { gcry_error_t err; static struct { unsigned long param[1]; size_t inputlen; const char *input; size_t saltlen; const char *salt; size_t infolen; const char *info; size_t dklen; const char *dk; } tv[] = { { { 42 }, 22, "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" "\x0b\x0b\x0b\x0b\x0b\x0b", 13, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c", 10, "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9", 42, "\x3c\xb2\x5f\x25\xfa\xac\xd5\x7a\x90\x43\x4f\x64\xd0\x36\x2f\x2a" "\x2d\x2d\x0a\x90\xcf\x1a\x5a\x4c\x5d\xb0\x2d\x56\xec\xc4\xc5\xbf" "\x34\x00\x72\x08\xd5\xb8\x87\x18\x58\x65" }, { { 82 }, 80, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f", 80, "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf", 80, "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef" "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", 82, "\xb1\x1e\x39\x8d\xc8\x03\x27\xa1\xc8\xe7\xf7\x8c\x59\x6a\x49\x34" "\x4f\x01\x2e\xda\x2d\x4e\xfa\xd8\xa0\x50\xcc\x4c\x19\xaf\xa9\x7c" "\x59\x04\x5a\x99\xca\xc7\x82\x72\x71\xcb\x41\xc6\x5e\x59\x0e\x09" "\xda\x32\x75\x60\x0c\x2f\x09\xb8\x36\x77\x93\xa9\xac\xa3\xdb\x71" "\xcc\x30\xc5\x81\x79\xec\x3e\x87\xc1\x4c\x01\xd5\xc1\xf3\x43\x4f" "\x1d\x87" }, { { 42 }, 22, "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" "\x0b\x0b\x0b\x0b\x0b\x0b", 0, NULL, 0, NULL, 42, "\x8d\xa4\xe7\x75\xa5\x63\xc1\x8f\x71\x5f\x80\x2a\x06\x3c\x5a\x31" "\xb8\xa1\x1f\x5c\x5e\xe1\x87\x9e\xc3\x45\x4e\x5f\x3c\x73\x8d\x2d" "\x9d\x20\x13\x95\xfa\xa4\xb6\x1a\x96\xc8" }, }; unsigned char out[82]; int i; int count; for (count = 0; count < DIM(tv); count++) { if (verbose) fprintf (stderr, "checking HKDF test vector %d\n", count); err = my_kdf_derive (0, GCRY_KDF_HKDF, GCRY_MAC_HMAC_SHA256, tv[count].param, 1, tv[count].input, tv[count].inputlen, NULL, 0, tv[count].salt, tv[count].saltlen, tv[count].info, tv[count].infolen, tv[count].dklen, out); if (err) fail ("HKDF test %d failed: %s\n", count, gpg_strerror (err)); else if (memcmp (out, tv[count].dk, tv[count].dklen)) { fail ("HKDF test %d failed: mismatch\n", count); fputs ("got:", stderr); for (i=0; i < tv[count].dklen; i++) fprintf (stderr, " %02x", out[i]); putc ('\n', stderr); } } } static void check_fips_indicators (void) { enum gcry_kdf_algos fips_kdf_algos[] = { GCRY_KDF_PBKDF2, }; enum gcry_kdf_algos kdf_algos[] = { GCRY_KDF_SIMPLE_S2K, GCRY_KDF_SALTED_S2K, GCRY_KDF_ITERSALTED_S2K, GCRY_KDF_PBKDF1, GCRY_KDF_PBKDF2, GCRY_KDF_SCRYPT, GCRY_KDF_ARGON2 , GCRY_KDF_BALLOON , GCRY_KDF_ONESTEP_KDF, GCRY_KDF_ONESTEP_KDF_MAC, GCRY_KDF_HKDF, }; size_t i, j; for (i = 0; i < sizeof(kdf_algos) / sizeof(*kdf_algos); i++) { int is_fips_kdf_algo = 0; gcry_error_t err = gcry_control (GCRYCTL_FIPS_SERVICE_INDICATOR_KDF, kdf_algos[i]); if (verbose) fprintf (stderr, "checking FIPS indicator for KDF %d: %s\n", kdf_algos[i], gcry_strerror (err)); for (j = 0; j < sizeof(fips_kdf_algos) / sizeof(*fips_kdf_algos); j++) { if (kdf_algos[i] == fips_kdf_algos[j]) { is_fips_kdf_algo = 1; break; } } switch (err & GPG_ERR_CODE_MASK) { case GPG_ERR_NO_ERROR: if (!is_fips_kdf_algo) fail ("KDF algorithm %d is marked as approved by" " GCRYCTL_FIPS_SERVICE_INDICATOR_KDF, but only PBKDF2 should" " be marked as approved.", kdf_algos[i]); break; case GPG_ERR_NOT_SUPPORTED: if (is_fips_kdf_algo) fail ("KDF algorithm %d is marked as not approved by" " GCRYCTL_FIPS_SERVICE_INDICATOR_KDF, but it should be" " approved", kdf_algos[i]); break; default: fail ("Unexpected error '%s' (%d) returned by" " GCRYCTL_FIPS_SERVICE_INDICATOR_KDF for KDF algorithm %d", gcry_strerror (err), err, kdf_algos[i]); } } } int main (int argc, char **argv) { int last_argc = -1; unsigned long s2kcount = 0; if (argc) { argc--; argv++; } while (argc && last_argc != argc ) { last_argc = argc; if (!strcmp (*argv, "--")) { argc--; argv++; break; } else if (!strcmp (*argv, "--help")) { fputs ("usage: t-kdf [options]" "Options:\n" " --verbose print timinigs etc.\n" " --debug flyswatter\n" " --s2k print the time needed for S2K\n", stdout); exit (0); } else if (!strcmp (*argv, "--verbose")) { verbose++; argc--; argv++; } else if (!strcmp (*argv, "--debug")) { verbose += 2; debug++; argc--; argv++; } else if (!strcmp (*argv, "--s2k")) { s2kcount = 1; argc--; argv++; } else if (!strncmp (*argv, "--", 2)) die ("unknown option '%s'\n", *argv); } if (s2kcount) { if (argc != 1) die ("usage: t-kdf --s2k S2KCOUNT\n"); s2kcount = strtoul (*argv, NULL, 10); if (!s2kcount) die ("t-kdf: S2KCOUNT must be positive\n"); } if (!gcry_check_version (GCRYPT_VERSION)) die ("version mismatch\n"); if (gcry_fips_mode_active ()) in_fips_mode = 1; if (!in_fips_mode) xgcry_control ((GCRYCTL_DISABLE_SECMEM, 0)); xgcry_control ((GCRYCTL_INITIALIZATION_FINISHED, 0)); if (debug) xgcry_control ((GCRYCTL_SET_DEBUG_FLAGS, 1u, 0)); if (s2kcount) bench_s2k (s2kcount); else { check_openpgp (); check_pbkdf2 (); check_scrypt (); check_argon2 (); check_balloon (); check_onestep_kdf (); check_hkdf (); if (in_fips_mode) check_fips_indicators(); } return error_count ? 1 : 0; }