Page MenuHome GnuPG

No OneTemporary

diff --git a/cipher/kdf.c b/cipher/kdf.c
index b4c5f83a..c6d2a10c 100644
--- a/cipher/kdf.c
+++ b/cipher/kdf.c
@@ -1,2395 +1,2401 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#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;
}
+ if (fips_mode ())
+ /* Clear the FIPS service indicator. */
+ _gcry_thread_context_set_fsi (0);
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
{
/* FIPS requires minimum passphrase length, see FIPS 140-3 IG D.N */
if (fips_mode () && passphraselen < 8)
return GPG_ERR_INV_VALUE;
/* FIPS requires minimum salt length of 128 b (SP 800-132 sec. 5.1, p.6) */
if (fips_mode () && saltlen < 16)
return GPG_ERR_INV_VALUE;
/* FIPS requires minimum iterations bound (SP 800-132 sec 5.2, p.6) */
if (fips_mode () && iterations < 1000)
return GPG_ERR_INV_VALUE;
/* Check minimum key size */
if (fips_mode () && keysize < 14)
return GPG_ERR_INV_VALUE;
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;
}
+ if (ec == 0 && fips_mode ())
+ _gcry_fips_check_kdf_compliant (algo, subalgo);
+
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++;
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);
}
typedef struct x963_kdf_context *x963_kdf_ctx_t;
/* X963KDF context */
struct x963_kdf_context {
int algo;
gcry_md_hd_t md;
unsigned int blklen;
unsigned int outlen;
const void *input;
size_t inputlen;
const void *sharedinfo;
size_t sharedinfolen;
};
static gpg_err_code_t
x963_kdf_open (gcry_kdf_hd_t *hd, int hashalgo,
const unsigned long *param, unsigned int paramlen,
const void *input, size_t inputlen,
const void *sharedinfo, size_t sharedinfolen)
{
gpg_err_code_t ec;
unsigned int outlen;
x963_kdf_ctx_t o;
size_t n;
if (paramlen != 1)
return GPG_ERR_INV_VALUE;
else
outlen = (unsigned int)param[0];
n = sizeof (struct x963_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_X963_KDF;
o->outlen = outlen;
o->input = input;
o->inputlen = inputlen;
o->sharedinfo = sharedinfo;
o->sharedinfolen = sharedinfolen;
*hd = (void *)o;
return 0;
}
static gpg_err_code_t
x963_kdf_compute (x963_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
x963_kdf_final (x963_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++;
_gcry_md_write (o->md, o->input, o->inputlen);
buf_put_be32 (cnt, counter);
_gcry_md_write (o->md, cnt, sizeof (cnt));
if (o->sharedinfolen)
_gcry_md_write (o->md, o->sharedinfo, o->sharedinfolen);
_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++;
_gcry_md_write (o->md, o->input, o->inputlen);
buf_put_be32 (cnt, counter);
_gcry_md_write (o->md, cnt, sizeof (cnt));
if (o->sharedinfolen)
_gcry_md_write (o->md, o->sharedinfo, o->sharedinfolen);
_gcry_md_final (o->md);
memcpy ((char *)result + o->blklen * i,
_gcry_md_read (o->md, 0), resultlen);
}
return 0;
}
static void
x963_kdf_close (x963_kdf_ctx_t o)
{
_gcry_md_close (o->md);
xfree (o);
}
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 (!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;
case GCRY_KDF_X963_KDF:
if (!inputlen || !paramlen)
ec = GPG_ERR_INV_VALUE;
else
{
(void)salt;
(void)key;
ec = x963_kdf_open (hd, subalgo, param, paramlen,
input, inputlen, 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;
case GCRY_KDF_X963_KDF:
ec = x963_kdf_compute ((x963_kdf_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;
case GCRY_KDF_X963_KDF:
ec = x963_kdf_final ((x963_kdf_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;
case GCRY_KDF_X963_KDF:
x963_kdf_close ((x963_kdf_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;
int rv;
if (keysize > sizeof(key))
return "invalid tests data";
rv = _gcry_kdf_derive (passphrase, passphraselen, algo,
hash_algo, salt, saltlen, iterations,
keysize, key);
/* In fips mode we have special requirements for the input and
* output parameters */
if (fips_mode ())
{
if (rv && (passphraselen < 8 || saltlen < 16 ||
iterations < 1000 || expectlen < 14))
return NULL;
else if (rv)
return "gcry_kdf_derive unexpectedly failed in FIPS Mode";
}
else if (rv)
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/src/fips.c b/src/fips.c
index bf70b4c1..2c400438 100644
--- a/src/fips.c
+++ b/src/fips.c
@@ -1,1201 +1,1218 @@
/* fips.c - FIPS mode management
* Copyright (C) 2008 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, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#ifdef ENABLE_HMAC_BINARY_CHECK
# include <dlfcn.h>
# include <elf.h>
# include <limits.h>
# include <link.h>
#endif
#ifdef HAVE_SYSLOG
# include <syslog.h>
#endif /*HAVE_SYSLOG*/
/* The name of the file used to force libgcrypt into fips mode. */
#define FIPS_FORCE_FILE "/etc/gcrypt/fips_enabled"
#include "g10lib.h"
#include "cipher-proto.h"
#include "../random/random.h"
/* The states of the finite state machine used in fips mode. */
enum module_states
{
/* POWEROFF cannot be represented. */
STATE_POWERON = 0,
STATE_INIT,
STATE_SELFTEST,
STATE_OPERATIONAL,
STATE_ERROR,
STATE_FATALERROR,
STATE_SHUTDOWN
};
/* Flag telling whether we are in fips mode. It uses inverse logic so
that fips mode is the default unless changed by the initialization
code. To check whether fips mode is enabled, use the function
fips_mode()! */
int _gcry_no_fips_mode_required;
/* This is the lock we use to protect the FSM. */
GPGRT_LOCK_DEFINE (fsm_lock);
/* The current state of the FSM. The whole state machinery is only
used while in fips mode. Change this only while holding fsm_lock. */
static enum module_states current_state;
struct gcry_thread_context {
unsigned long fips_service_indicator;
};
#ifdef HAVE_GCC_STORAGE_CLASS__THREAD
static __thread struct gcry_thread_context the_tc;
#else
#error libgcrypt requires thread-local storage to support FIPS mode
#endif
void
_gcry_thread_context_set_fsi (unsigned long fsi)
{
the_tc.fips_service_indicator = fsi;
}
unsigned long
_gcry_thread_context_get_fsi (void)
{
return the_tc.fips_service_indicator;
}
static void fips_new_state (enum module_states new_state);
/* Convert lowercase hex digits; assumes valid hex digits. */
#define loxtoi_1(p) (*(p) <= '9'? (*(p)- '0'): (*(p)-'a'+10))
#define loxtoi_2(p) ((loxtoi_1(p) * 16) + loxtoi_1((p)+1))
/* Returns true if P points to a lowercase hex digit. */
#define loxdigit_p(p) !!strchr ("01234567890abcdef", *(p))
/*
* Returns 1 if the FIPS mode is to be activated based on the
* environment variable LIBGCRYPT_FORCE_FIPS_MODE, the file defined by
* FIPS_FORCE_FILE, or /proc/sys/crypto/fips_enabled.
* This function aborts on misconfigured filesystems.
*/
static int
check_fips_system_setting (void)
{
/* Do we have the environment variable set? */
if (getenv ("LIBGCRYPT_FORCE_FIPS_MODE"))
return 1;
/* For testing the system it is useful to override the system
provided detection of the FIPS mode and force FIPS mode using a
file. The filename is hardwired so that there won't be any
confusion on whether /etc/gcrypt/ or /usr/local/etc/gcrypt/ is
actually used. The file itself may be empty. */
if ( !access (FIPS_FORCE_FILE, F_OK) )
return 1;
/* Checking based on /proc file properties. */
{
static const char procfname[] = "/proc/sys/crypto/fips_enabled";
FILE *fp;
int saved_errno;
fp = fopen (procfname, "r");
if (fp)
{
char line[256];
if (fgets (line, sizeof line, fp) && atoi (line))
{
/* System is in fips mode. */
fclose (fp);
return 1;
}
fclose (fp);
}
else if ((saved_errno = errno) != ENOENT
&& saved_errno != EACCES
&& !access ("/proc/version", F_OK) )
{
/* Problem reading the fips file despite that we have the proc
file system. We better stop right away. */
log_info ("FATAL: error reading `%s' in libgcrypt: %s\n",
procfname, strerror (saved_errno));
#ifdef HAVE_SYSLOG
syslog (LOG_USER|LOG_ERR, "Libgcrypt error: "
"reading `%s' failed: %s - abort",
procfname, strerror (saved_errno));
#endif /*HAVE_SYSLOG*/
abort ();
}
}
return 0;
}
/*
* Initial check if the FIPS mode should be activated on startup.
* Called by the constructor at the initialization of the library.
*/
int
_gcry_fips_to_activate (void)
{
return check_fips_system_setting ();
}
/* Check whether the OS is in FIPS mode and record that in a module
local variable. If FORCE is passed as true, fips mode will be
enabled anyway. Note: This function is not thread-safe and should
be called before any threads are created. This function may only
be called once. */
void
_gcry_initialize_fips_mode (int force)
{
static int done;
gpg_error_t err;
/* Make sure we are not accidentally called twice. */
if (done)
{
if ( fips_mode () )
{
fips_new_state (STATE_FATALERROR);
fips_noreturn ();
}
/* If not in fips mode an assert is sufficient. */
gcry_assert (!done);
}
done = 1;
/* If the calling application explicitly requested fipsmode, do so. */
if (force)
{
gcry_assert (!_gcry_no_fips_mode_required);
goto leave;
}
/* If the system explicitly requested fipsmode, do so. */
if (check_fips_system_setting ())
{
gcry_assert (!_gcry_no_fips_mode_required);
goto leave;
}
/* Fips not not requested, set flag. */
_gcry_no_fips_mode_required = 1;
leave:
if (!_gcry_no_fips_mode_required)
{
/* Yes, we are in FIPS mode. */
/* Intitialize the lock to protect the FSM. */
err = gpgrt_lock_init (&fsm_lock);
if (err)
{
/* If that fails we can't do anything but abort the
process. We need to use log_info so that the FSM won't
get involved. */
log_info ("FATAL: failed to create the FSM lock in libgcrypt: %s\n",
gpg_strerror (err));
#ifdef HAVE_SYSLOG
syslog (LOG_USER|LOG_ERR, "Libgcrypt error: "
"creating FSM lock failed: %s - abort",
gpg_strerror (err));
#endif /*HAVE_SYSLOG*/
abort ();
}
/* Now get us into the INIT state. */
fips_new_state (STATE_INIT);
}
return;
}
static void
lock_fsm (void)
{
gpg_error_t err;
err = gpgrt_lock_lock (&fsm_lock);
if (err)
{
log_info ("FATAL: failed to acquire the FSM lock in libgrypt: %s\n",
gpg_strerror (err));
#ifdef HAVE_SYSLOG
syslog (LOG_USER|LOG_ERR, "Libgcrypt error: "
"acquiring FSM lock failed: %s - abort",
gpg_strerror (err));
#endif /*HAVE_SYSLOG*/
abort ();
}
}
static void
unlock_fsm (void)
{
gpg_error_t err;
err = gpgrt_lock_unlock (&fsm_lock);
if (err)
{
log_info ("FATAL: failed to release the FSM lock in libgrypt: %s\n",
gpg_strerror (err));
#ifdef HAVE_SYSLOG
syslog (LOG_USER|LOG_ERR, "Libgcrypt error: "
"releasing FSM lock failed: %s - abort",
gpg_strerror (err));
#endif /*HAVE_SYSLOG*/
abort ();
}
}
static const char *
state2str (enum module_states state)
{
const char *s;
switch (state)
{
case STATE_POWERON: s = "Power-On"; break;
case STATE_INIT: s = "Init"; break;
case STATE_SELFTEST: s = "Self-Test"; break;
case STATE_OPERATIONAL: s = "Operational"; break;
case STATE_ERROR: s = "Error"; break;
case STATE_FATALERROR: s = "Fatal-Error"; break;
case STATE_SHUTDOWN: s = "Shutdown"; break;
default: s = "?"; break;
}
return s;
}
/* Return true if the library is in the operational state. */
int
_gcry_fips_is_operational (void)
{
int result;
if (!fips_mode ())
result = 1;
else
{
lock_fsm ();
if (current_state == STATE_INIT)
{
/* If we are still in the INIT state, we need to run the
selftests so that the FSM can eventually get into
operational state. Given that we would need a 2-phase
initialization of libgcrypt, but that has traditionally
not been enforced, we use this on demand self-test
checking. Note that Proper applications would do the
application specific libgcrypt initialization between a
gcry_check_version() and gcry_control
(GCRYCTL_INITIALIZATION_FINISHED) where the latter will
run the selftests. The drawback of these on-demand
self-tests are a small chance that self-tests are
performed by several threads; that is no problem because
our FSM make sure that we won't oversee any error. */
unlock_fsm ();
_gcry_fips_run_selftests (0);
/* Release resources for random. */
_gcry_random_close_fds ();
lock_fsm ();
}
result = (current_state == STATE_OPERATIONAL);
unlock_fsm ();
}
return result;
}
/* This is test on whether the library is in the operational state. In
contrast to _gcry_fips_is_operational this function won't do a
state transition on the fly. */
int
_gcry_fips_test_operational (void)
{
int result;
if (!fips_mode ())
result = 1;
else
{
lock_fsm ();
result = (current_state == STATE_OPERATIONAL);
unlock_fsm ();
}
return result;
}
gpg_err_code_t
_gcry_fips_indicator (unsigned long *p)
{
*p = _gcry_thread_context_get_fsi ();
return 0;
}
int
_gcry_fips_indicator_cipher (va_list arg_ptr)
{
enum gcry_cipher_algos alg = va_arg (arg_ptr, enum gcry_cipher_algos);
enum gcry_cipher_modes mode;
switch (alg)
{
case GCRY_CIPHER_AES:
case GCRY_CIPHER_AES192:
case GCRY_CIPHER_AES256:
mode = va_arg (arg_ptr, enum gcry_cipher_modes);
switch (mode)
{
case GCRY_CIPHER_MODE_ECB:
case GCRY_CIPHER_MODE_CBC:
case GCRY_CIPHER_MODE_CFB:
case GCRY_CIPHER_MODE_CFB8:
case GCRY_CIPHER_MODE_OFB:
case GCRY_CIPHER_MODE_CTR:
case GCRY_CIPHER_MODE_CCM:
case GCRY_CIPHER_MODE_GCM:
case GCRY_CIPHER_MODE_XTS:
case GCRY_CIPHER_MODE_AESWRAP:
return GPG_ERR_NO_ERROR;
default:
return GPG_ERR_NOT_SUPPORTED;
}
default:
return GPG_ERR_NOT_SUPPORTED;
}
}
int
_gcry_fips_indicator_mac (va_list arg_ptr)
{
enum gcry_mac_algos alg = va_arg (arg_ptr, enum gcry_mac_algos);
switch (alg)
{
case GCRY_MAC_CMAC_AES:
case GCRY_MAC_HMAC_SHA1:
case GCRY_MAC_HMAC_SHA224:
case GCRY_MAC_HMAC_SHA256:
case GCRY_MAC_HMAC_SHA384:
case GCRY_MAC_HMAC_SHA512:
case GCRY_MAC_HMAC_SHA512_224:
case GCRY_MAC_HMAC_SHA512_256:
case GCRY_MAC_HMAC_SHA3_224:
case GCRY_MAC_HMAC_SHA3_256:
case GCRY_MAC_HMAC_SHA3_384:
case GCRY_MAC_HMAC_SHA3_512:
return GPG_ERR_NO_ERROR;
default:
return GPG_ERR_NOT_SUPPORTED;
}
}
int
_gcry_fips_indicator_md (va_list arg_ptr)
{
enum gcry_md_algos alg = va_arg (arg_ptr, enum gcry_md_algos);
switch (alg)
{
case GCRY_MD_SHA1:
case GCRY_MD_SHA224:
case GCRY_MD_SHA256:
case GCRY_MD_SHA384:
case GCRY_MD_SHA512:
case GCRY_MD_SHA512_224:
case GCRY_MD_SHA512_256:
case GCRY_MD_SHA3_224:
case GCRY_MD_SHA3_256:
case GCRY_MD_SHA3_384:
case GCRY_MD_SHA3_512:
case GCRY_MD_SHAKE128:
case GCRY_MD_SHAKE256:
case GCRY_MD_CSHAKE128:
case GCRY_MD_CSHAKE256:
return GPG_ERR_NO_ERROR;
default:
return GPG_ERR_NOT_SUPPORTED;
}
}
int
_gcry_fips_indicator_kdf (va_list arg_ptr)
{
enum gcry_kdf_algos alg = va_arg (arg_ptr, enum gcry_kdf_algos);
switch (alg)
{
case GCRY_KDF_PBKDF2:
return GPG_ERR_NO_ERROR;
default:
return GPG_ERR_NOT_SUPPORTED;
}
}
int
_gcry_fips_indicator_function (va_list arg_ptr)
{
const char *function = va_arg (arg_ptr, const char *);
if (strcmp (function, "gcry_pk_sign") == 0 ||
strcmp (function, "gcry_pk_verify") == 0 ||
strcmp (function, "gcry_pk_encrypt") == 0 ||
strcmp (function, "gcry_pk_decrypt") == 0 ||
strcmp (function, "gcry_pk_random_override_new") == 0)
return GPG_ERR_NOT_SUPPORTED;
return GPG_ERR_NO_ERROR;
}
/* Note: the array should be sorted. */
static const char *valid_string_in_sexp[] = {
"curve",
"d",
"data",
"e",
"ecdsa",
"eddsa",
"flags",
"genkey",
"hash",
"n",
"nbits",
"pkcs1",
"private-key",
"pss",
"public-key",
"q",
"r",
"raw",
"rsa",
"rsa-use-e",
"s",
"salt-length",
"sig-val",
"value"
};
static int
compare_string (const void *v1, const void *v2)
{
const char * const *p_str1 = v1;
const char * const *p_str2 = v2;
return strcmp (*p_str1, *p_str2);
}
int
_gcry_fips_indicator_pk_flags (va_list arg_ptr)
{
const char *flag = va_arg (arg_ptr, const char *);
if (bsearch (&flag, valid_string_in_sexp, DIM (valid_string_in_sexp),
sizeof (char *), compare_string))
return GPG_ERR_NO_ERROR;
return GPG_ERR_NOT_SUPPORTED;
}
/* This is a test on whether the library is in the error or
operational state. */
int
_gcry_fips_test_error_or_operational (void)
{
int result;
if (!fips_mode ())
result = 1;
else
{
lock_fsm ();
result = (current_state == STATE_OPERATIONAL
|| current_state == STATE_ERROR);
unlock_fsm ();
}
return result;
}
static void
reporter (const char *domain, int algo, const char *what, const char *errtxt)
{
if (!errtxt && !_gcry_log_verbosity (2))
return;
log_info ("libgcrypt selftest: %s %s%s (%d): %s%s%s%s\n",
!strcmp (domain, "hmac")? "digest":domain,
!strcmp (domain, "hmac")? "HMAC-":"",
!strcmp (domain, "cipher")? _gcry_cipher_algo_name (algo) :
!strcmp (domain, "digest")? _gcry_md_algo_name (algo) :
!strcmp (domain, "hmac")? _gcry_md_algo_name (algo) :
!strcmp (domain, "pubkey")? _gcry_pk_algo_name (algo) : "",
algo, errtxt? errtxt:"Okay",
what?" (":"", what? what:"", what?")":"");
}
/* Run self-tests for all required cipher algorithms. Return 0 on
success. */
static int
run_cipher_selftests (int extended)
{
static int algos[] =
{
GCRY_CIPHER_AES128,
GCRY_CIPHER_AES192,
GCRY_CIPHER_AES256,
0
};
int idx;
gpg_error_t err;
int anyerr = 0;
for (idx=0; algos[idx]; idx++)
{
err = _gcry_cipher_selftest (algos[idx], extended, reporter);
reporter ("cipher", algos[idx], NULL,
err? gpg_strerror (err):NULL);
if (err)
anyerr = 1;
}
return anyerr;
}
/* Run self-tests for all required hash algorithms. Return 0 on
success. */
static int
run_digest_selftests (int extended)
{
static int algos[] =
{
GCRY_MD_SHA1,
GCRY_MD_SHA224,
#ifndef ENABLE_HMAC_BINARY_CHECK
GCRY_MD_SHA256,
#endif
GCRY_MD_SHA384,
GCRY_MD_SHA512,
0
};
int idx;
gpg_error_t err;
int anyerr = 0;
for (idx=0; algos[idx]; idx++)
{
err = _gcry_md_selftest (algos[idx], extended, reporter);
reporter ("digest", algos[idx], NULL,
err? gpg_strerror (err):NULL);
if (err)
anyerr = 1;
}
return anyerr;
}
/* Run self-tests for MAC algorithms. Return 0 on success. */
static int
run_mac_selftests (int extended)
{
static int algos[] =
{
GCRY_MAC_HMAC_SHA1,
GCRY_MAC_HMAC_SHA224,
#ifndef ENABLE_HMAC_BINARY_CHECK
GCRY_MAC_HMAC_SHA256,
#endif
GCRY_MAC_HMAC_SHA384,
GCRY_MAC_HMAC_SHA512,
GCRY_MAC_HMAC_SHA3_224,
GCRY_MAC_HMAC_SHA3_256,
GCRY_MAC_HMAC_SHA3_384,
GCRY_MAC_HMAC_SHA3_512,
GCRY_MAC_CMAC_AES,
0
};
int idx;
gpg_error_t err;
int anyerr = 0;
for (idx=0; algos[idx]; idx++)
{
err = _gcry_mac_selftest (algos[idx], extended, reporter);
reporter ("mac", algos[idx], NULL,
err? gpg_strerror (err):NULL);
if (err)
anyerr = 1;
}
return anyerr;
}
/* Run self-tests for all KDF algorithms. Return 0 on success. */
static int
run_kdf_selftests (int extended)
{
static int algos[] =
{
GCRY_KDF_PBKDF2,
0
};
int idx;
gpg_error_t err;
int anyerr = 0;
for (idx=0; algos[idx]; idx++)
{
err = _gcry_kdf_selftest (algos[idx], extended, reporter);
reporter ("kdf", algos[idx], NULL, err? gpg_strerror (err):NULL);
if (err)
anyerr = 1;
}
return anyerr;
}
/* Run self-tests for all required public key algorithms. Return 0 on
success. */
static int
run_pubkey_selftests (int extended)
{
static int algos[] =
{
#if USE_RSA
GCRY_PK_RSA,
#endif /* USE_RSA */
#if USE_ECC
GCRY_PK_ECC,
#endif /* USE_ECC */
0
};
int idx;
gpg_error_t err;
int anyerr = 0;
for (idx=0; algos[idx]; idx++)
{
err = _gcry_pk_selftest (algos[idx], extended, reporter);
reporter ("pubkey", algos[idx], NULL,
err? gpg_strerror (err):NULL);
if (err)
anyerr = 1;
}
return anyerr;
}
/* Run self-tests for the random number generator. Returns 0 on
success. */
static int
run_random_selftests (void)
{
gpg_error_t err;
err = _gcry_random_selftest (reporter);
reporter ("random", 0, NULL, err? gpg_strerror (err):NULL);
return !!err;
}
#ifdef ENABLE_HMAC_BINARY_CHECK
# ifndef KEY_FOR_BINARY_CHECK
# define KEY_FOR_BINARY_CHECK "What am I, a doctor or a moonshuttle conductor?"
# endif
#define HMAC_LEN 32
/*
* In the ELF file opened as FP, fill the ELF header to the pointer
* EHDR_P, determine the maximum offset of segments in R_OFFSET.
* Also, find the section which contains the hmac value and return it
* in HMAC. Rewinds FP to the beginning on success.
*/
static gpg_error_t
get_file_offset (FILE *fp, ElfW (Ehdr) *ehdr_p,
unsigned long *r_offset, unsigned char hmac[HMAC_LEN])
{
ElfW (Phdr) phdr;
ElfW (Shdr) shdr;
int i;
unsigned long off_segment = 0;
/* Read the ELF header */
if (fseek (fp, 0, SEEK_SET) != 0)
return gpg_error_from_syserror ();
if (fread (ehdr_p, sizeof (*ehdr_p), 1, fp) != 1)
return gpg_error_from_syserror ();
/* The program header entry size should match the size of the phdr struct */
if (ehdr_p->e_phentsize != sizeof (phdr))
return gpg_error (GPG_ERR_INV_OBJ);
if (ehdr_p->e_phoff == 0)
return gpg_error (GPG_ERR_INV_OBJ);
/* Jump to the first program header */
if (fseek (fp, ehdr_p->e_phoff, SEEK_SET) != 0)
return gpg_error_from_syserror ();
/* Iterate over the program headers, determine the last offset of
segments. */
for (i = 0; i < ehdr_p->e_phnum; i++)
{
unsigned long off;
if (fread (&phdr, sizeof (phdr), 1, fp) != 1)
return gpg_error_from_syserror ();
off = phdr.p_offset + phdr.p_filesz;
if (off_segment < off)
off_segment = off;
}
if (!off_segment)
/* No segment found in the file */
return gpg_error (GPG_ERR_INV_OBJ);
/* The section header entry size should match the size of the shdr struct */
if (ehdr_p->e_shentsize != sizeof (shdr))
return gpg_error (GPG_ERR_INV_OBJ);
if (ehdr_p->e_shoff == 0)
return gpg_error (GPG_ERR_INV_OBJ);
/* Jump to the first section header */
if (fseek (fp, ehdr_p->e_shoff, SEEK_SET) != 0)
return gpg_error_from_syserror ();
/* Iterate over the section headers, determine the note section,
read the hmac value. */
for (i = 0; i < ehdr_p->e_shnum; i++)
{
long off;
if (fread (&shdr, sizeof (shdr), 1, fp) != 1)
return gpg_error_from_syserror ();
off = ftell (fp);
if (off < 0)
return gpg_error_from_syserror ();
if (shdr.sh_type == SHT_NOTE && shdr.sh_flags == 0 && shdr.sh_size == 48)
{
const char header_of_the_note[] = {
0x04, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00,
0xca, 0xfe, 0x2a, 0x8e,
'F', 'D', 'O', 0x00
};
unsigned char header[16];
/* Jump to the note section. */
if (fseek (fp, shdr.sh_offset, SEEK_SET) != 0)
return gpg_error_from_syserror ();
if (fread (header, sizeof (header), 1, fp) != 1)
return gpg_error_from_syserror ();
if (!memcmp (header, header_of_the_note, 16))
{
/* Found. Read the hmac value into HMAC. */
if (fread (hmac, HMAC_LEN, 1, fp) != 1)
return gpg_error_from_syserror ();
break;
}
/* Back to the next section header. */
if (fseek (fp, off, SEEK_SET) != 0)
return gpg_error_from_syserror ();
}
}
if (i == ehdr_p->e_shnum)
/* The note section not found. */
return gpg_error (GPG_ERR_INV_OBJ);
/* Fix up the ELF header, clean all section information. */
ehdr_p->e_shoff = 0;
ehdr_p->e_shentsize = 0;
ehdr_p->e_shnum = 0;
ehdr_p->e_shstrndx = 0;
*r_offset = off_segment;
if (fseek (fp, 0, SEEK_SET) != 0)
return gpg_error_from_syserror ();
return 0;
}
static gpg_error_t
hmac256_check (const char *filename, const char *key)
{
gpg_error_t err;
FILE *fp;
gcry_md_hd_t hd;
const size_t buffer_size = 32768;
size_t nread;
char *buffer;
unsigned long offset = 0;
unsigned long pos = 0;
ElfW (Ehdr) ehdr;
unsigned char hmac[HMAC_LEN];
fp = fopen (filename, "rb");
if (!fp)
return gpg_error (GPG_ERR_INV_OBJ);
err = get_file_offset (fp, &ehdr, &offset, hmac);
if (err)
{
fclose (fp);
return err;
}
err = _gcry_md_open (&hd, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
if (err)
{
fclose (fp);
return err;
}
err = _gcry_md_setkey (hd, key, strlen (key));
if (err)
{
fclose (fp);
_gcry_md_close (hd);
return err;
}
buffer = xtrymalloc (buffer_size);
if (!buffer)
{
err = gpg_error_from_syserror ();
fclose (fp);
_gcry_md_close (hd);
return err;
}
while (1)
{
nread = fread (buffer, 1, buffer_size, fp);
if (pos + nread >= offset)
nread = offset - pos;
/* Copy the fixed ELF header at the beginning. */
if (pos == 0)
memcpy (buffer, &ehdr, sizeof (ehdr));
_gcry_md_write (hd, buffer, nread);
if (nread < buffer_size)
break;
pos += nread;
}
if (ferror (fp))
err = gpg_error (GPG_ERR_INV_HANDLE);
else
{
unsigned char *digest;
digest = _gcry_md_read (hd, 0);
if (!memcmp (digest, hmac, HMAC_LEN))
/* Success. */
err = 0;
else
err = gpg_error (GPG_ERR_CHECKSUM);
}
_gcry_md_close (hd);
xfree (buffer);
fclose (fp);
return err;
}
/* Run an integrity check on the binary. Returns 0 on success. */
static int
check_binary_integrity (void)
{
gpg_error_t err;
Dl_info info;
const char *key = KEY_FOR_BINARY_CHECK;
if (!dladdr (hmac256_check, &info))
err = gpg_error_from_syserror ();
else
err = hmac256_check (info.dli_fname, key);
reporter ("binary", 0, NULL, err? gpg_strerror (err):NULL);
#ifdef HAVE_SYSLOG
if (err)
syslog (LOG_USER|LOG_ERR, "Libgcrypt error: "
"integrity check failed: %s",
gpg_strerror (err));
#endif /*HAVE_SYSLOG*/
return !!err;
}
/* Run self-tests for HMAC-SHA256 algorithm before verifying library integrity.
* Return 0 on success. */
static int
run_hmac_sha256_selftests (int extended)
{
gpg_error_t err;
int anyerr = 0;
err = _gcry_md_selftest (GCRY_MD_SHA256, extended, reporter);
reporter ("digest", GCRY_MD_SHA256, NULL,
err? gpg_strerror (err):NULL);
if (err)
anyerr = 1;
err = _gcry_mac_selftest (GCRY_MAC_HMAC_SHA256, extended, reporter);
reporter ("mac", GCRY_MAC_HMAC_SHA256, NULL,
err? gpg_strerror (err):NULL);
if (err)
anyerr = 1;
return anyerr;
}
#endif
/* Run the self-tests. If EXTENDED is true, extended versions of the
selftest are run, that is more tests than required by FIPS. */
gpg_err_code_t
_gcry_fips_run_selftests (int extended)
{
enum module_states result = STATE_ERROR;
gcry_err_code_t ec = GPG_ERR_SELFTEST_FAILED;
if (fips_mode ())
fips_new_state (STATE_SELFTEST);
#ifdef ENABLE_HMAC_BINARY_CHECK
if (run_hmac_sha256_selftests (extended))
goto leave;
if (fips_mode ())
{
/* Now check the integrity of the binary. We do this this after
having checked the HMAC code. */
if (check_binary_integrity ())
goto leave;
}
#endif
if (run_cipher_selftests (extended))
goto leave;
if (run_digest_selftests (extended))
goto leave;
if (run_mac_selftests (extended))
goto leave;
if (run_kdf_selftests (extended))
goto leave;
/* Run random tests before the pubkey tests because the latter
require random. */
if (run_random_selftests ())
goto leave;
if (run_pubkey_selftests (extended))
goto leave;
/* All selftests passed. */
result = STATE_OPERATIONAL;
ec = 0;
leave:
if (fips_mode ())
fips_new_state (result);
return ec;
}
/* This function is used to tell the FSM about errors in the library.
The FSM will be put into an error state. This function should not
be called directly but by one of the macros
fips_signal_error (description)
fips_signal_fatal_error (description)
where DESCRIPTION is a string describing the error. */
void
_gcry_fips_signal_error (const char *srcfile, int srcline, const char *srcfunc,
int is_fatal, const char *description)
{
if (!fips_mode ())
return; /* Not required. */
/* Set new state before printing an error. */
fips_new_state (is_fatal? STATE_FATALERROR : STATE_ERROR);
/* Print error. */
log_info ("%serror in libgcrypt, file %s, line %d%s%s: %s\n",
is_fatal? "fatal ":"",
srcfile, srcline,
srcfunc? ", function ":"", srcfunc? srcfunc:"",
description? description : "no description available");
#ifdef HAVE_SYSLOG
syslog (LOG_USER|LOG_ERR, "Libgcrypt error: "
"%serror in file %s, line %d%s%s: %s",
is_fatal? "fatal ":"",
srcfile, srcline,
srcfunc? ", function ":"", srcfunc? srcfunc:"",
description? description : "no description available");
#endif /*HAVE_SYSLOG*/
}
/* Perform a state transition to NEW_STATE. If this is an invalid
transition, the module will go into a fatal error state. */
static void
fips_new_state (enum module_states new_state)
{
int ok = 0;
enum module_states last_state;
lock_fsm ();
last_state = current_state;
switch (current_state)
{
case STATE_POWERON:
if (new_state == STATE_INIT
|| new_state == STATE_ERROR
|| new_state == STATE_FATALERROR)
ok = 1;
break;
case STATE_INIT:
if (new_state == STATE_SELFTEST
|| new_state == STATE_ERROR
|| new_state == STATE_FATALERROR)
ok = 1;
break;
case STATE_SELFTEST:
if (new_state == STATE_OPERATIONAL
|| new_state == STATE_ERROR
|| new_state == STATE_FATALERROR)
ok = 1;
break;
case STATE_OPERATIONAL:
if (new_state == STATE_SHUTDOWN
|| new_state == STATE_SELFTEST
|| new_state == STATE_ERROR
|| new_state == STATE_FATALERROR)
ok = 1;
break;
case STATE_ERROR:
if (new_state == STATE_SHUTDOWN
|| new_state == STATE_ERROR
|| new_state == STATE_FATALERROR
|| new_state == STATE_SELFTEST)
ok = 1;
break;
case STATE_FATALERROR:
if (new_state == STATE_SHUTDOWN )
ok = 1;
break;
case STATE_SHUTDOWN:
/* We won't see any transition *from* Shutdown because the only
allowed new state is Power-Off and that one can't be
represented. */
break;
}
if (ok)
{
current_state = new_state;
}
unlock_fsm ();
if (!ok || _gcry_log_verbosity (2))
log_info ("libgcrypt state transition %s => %s %s\n",
state2str (last_state), state2str (new_state),
ok? "granted":"denied");
if (!ok)
{
/* Invalid state transition. Halting library. */
#ifdef HAVE_SYSLOG
syslog (LOG_USER|LOG_ERR,
"Libgcrypt error: invalid state transition %s => %s",
state2str (last_state), state2str (new_state));
#endif /*HAVE_SYSLOG*/
fips_noreturn ();
}
else if (new_state == STATE_ERROR || new_state == STATE_FATALERROR)
{
#ifdef HAVE_SYSLOG
syslog (LOG_USER|LOG_WARNING,
"Libgcrypt notice: state transition %s => %s",
state2str (last_state), state2str (new_state));
#endif /*HAVE_SYSLOG*/
}
}
/* This function should be called to ensure that the execution shall
not continue. */
void
_gcry_fips_noreturn (void)
{
#ifdef HAVE_SYSLOG
syslog (LOG_USER|LOG_ERR, "Libgcrypt terminated the application");
#endif /*HAVE_SYSLOG*/
fflush (NULL);
abort ();
/*NOTREACHED*/
}
+
+void
+_gcry_fips_check_kdf_compliant (int algo, int subalgo)
+{
+ switch (algo)
+ {
+ case GCRY_KDF_PBKDF2:
+ /* It's valid. */
+ break;
+
+ default:
+ /* Record the reason of failure, in the indicator.
+ * Or putting GPG_ERR_NOT_SUPPORTED would be enough. */
+ _gcry_thread_context_set_fsi ((unsigned long)((algo << 16) | subalgo));
+ break;
+ }
+}
diff --git a/src/g10lib.h b/src/g10lib.h
index 1c3bda93..30117d0e 100644
--- a/src/g10lib.h
+++ b/src/g10lib.h
@@ -1,500 +1,501 @@
/* g10lib.h - Internal definitions for libgcrypt
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005
* 2007, 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, see <http://www.gnu.org/licenses/>.
*/
/* This header is to be used inside of libgcrypt in place of gcrypt.h.
This way we can better distinguish between internal and external
usage of gcrypt.h. */
#ifndef G10LIB_H
#define G10LIB_H 1
#ifdef _GCRYPT_H
#error gcrypt.h already included
#endif
#ifndef _GCRYPT_IN_LIBGCRYPT
#error something is wrong with config.h
#endif
#include <stdio.h>
#include <stdarg.h>
#include "visibility.h"
#include "types.h"
/* Attribute handling macros. */
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 )
#define JNLIB_GCC_M_FUNCTION 1
#define JNLIB_GCC_A_NR __attribute__ ((__noreturn__))
#define JNLIB_GCC_A_PRINTF( f, a ) __attribute__ ((format (printf,f,a)))
#define JNLIB_GCC_A_NR_PRINTF( f, a ) \
__attribute__ ((__noreturn__, format (printf,f,a)))
#define GCC_ATTR_NORETURN __attribute__ ((__noreturn__))
#else
#define JNLIB_GCC_A_NR
#define JNLIB_GCC_A_PRINTF( f, a )
#define JNLIB_GCC_A_NR_PRINTF( f, a )
#define GCC_ATTR_NORETURN
#endif
#if __GNUC__ >= 3
/* According to glibc this attribute is available since 2.8 however we
better play safe and use it only with gcc 3 or newer. */
#define GCC_ATTR_FORMAT_ARG(a) __attribute__ ((format_arg (a)))
#else
#define GCC_ATTR_FORMAT_ARG(a)
#endif
/* I am not sure since when the unused attribute is really supported.
In any case it it only needed for gcc versions which print a
warning. Thus let us require gcc >= 3.5. */
#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 5 )
#define GCC_ATTR_UNUSED __attribute__ ((unused))
#else
#define GCC_ATTR_UNUSED
#endif
#if __GNUC__ > 3
#define NOINLINE_FUNC __attribute__((noinline))
#else
#define NOINLINE_FUNC
#endif
#if __GNUC__ >= 3
#define LIKELY(expr) __builtin_expect( !!(expr), 1 )
#define UNLIKELY(expr) __builtin_expect( !!(expr), 0 )
#define CONSTANT_P(expr) __builtin_constant_p( expr )
#else
#define LIKELY(expr) (!!(expr))
#define UNLIKELY(expr) (!!(expr))
#define CONSTANT_P(expr) (0)
#endif
/* Gettext macros. */
#define _(a) _gcry_gettext(a)
#define N_(a) (a)
/* Some handy macros */
#ifndef STR
#define STR(v) #v
#endif
#define STR2(v) STR(v)
#define DIM(v) (sizeof(v)/sizeof((v)[0]))
#define DIMof(type,member) DIM(((type *)0)->member)
#define my_isascii(c) (!((c) & 0x80))
/*-- src/global.c -*/
extern int _gcry_global_any_init_done;
int _gcry_global_is_operational (void);
gcry_err_code_t _gcry_vcontrol (enum gcry_ctl_cmds cmd, va_list arg_ptr);
void _gcry_pre_syscall (void);
void _gcry_post_syscall (void);
int _gcry_get_debug_flag (unsigned int mask);
char *_gcry_get_config (int mode, const char *what);
/* Malloc functions and common wrapper macros. */
void *_gcry_malloc (size_t n) _GCRY_GCC_ATTR_MALLOC;
void *_gcry_calloc (size_t n, size_t m) _GCRY_GCC_ATTR_MALLOC;
void *_gcry_malloc_secure (size_t n) _GCRY_GCC_ATTR_MALLOC;
void *_gcry_calloc_secure (size_t n, size_t m) _GCRY_GCC_ATTR_MALLOC;
void *_gcry_realloc (void *a, size_t n);
char *_gcry_strdup (const char *string) _GCRY_GCC_ATTR_MALLOC;
void *_gcry_xmalloc (size_t n) _GCRY_GCC_ATTR_MALLOC;
void *_gcry_xcalloc (size_t n, size_t m) _GCRY_GCC_ATTR_MALLOC;
void *_gcry_xmalloc_secure (size_t n) _GCRY_GCC_ATTR_MALLOC;
void *_gcry_xcalloc_secure (size_t n, size_t m) _GCRY_GCC_ATTR_MALLOC;
void *_gcry_xrealloc (void *a, size_t n);
char *_gcry_xstrdup (const char * a) _GCRY_GCC_ATTR_MALLOC;
void _gcry_free (void *a);
int _gcry_is_secure (const void *a) _GCRY_GCC_ATTR_PURE;
#define xtrymalloc(a) _gcry_malloc ((a))
#define xtrycalloc(a,b) _gcry_calloc ((a),(b))
#define xtrymalloc_secure(a) _gcry_malloc_secure ((a))
#define xtrycalloc_secure(a,b) _gcry_calloc_secure ((a),(b))
#define xtryrealloc(a,b) _gcry_realloc ((a),(b))
#define xtrystrdup(a) _gcry_strdup ((a))
#define xmalloc(a) _gcry_xmalloc ((a))
#define xcalloc(a,b) _gcry_xcalloc ((a),(b))
#define xmalloc_secure(a) _gcry_xmalloc_secure ((a))
#define xcalloc_secure(a,b) _gcry_xcalloc_secure ((a),(b))
#define xrealloc(a,b) _gcry_xrealloc ((a),(b))
#define xstrdup(a) _gcry_xstrdup ((a))
#define xfree(a) _gcry_free ((a))
/*-- src/misc.c --*/
#if defined(JNLIB_GCC_M_FUNCTION) || __STDC_VERSION__ >= 199901L
void _gcry_bug (const char *file, int line,
const char *func) GCC_ATTR_NORETURN;
void _gcry_assert_failed (const char *expr, const char *file, int line,
const char *func) GCC_ATTR_NORETURN;
#else
void _gcry_bug (const char *file, int line);
void _gcry_assert_failed (const char *expr, const char *file, int line);
#endif
void _gcry_divide_by_zero (void) JNLIB_GCC_A_NR;
const char *_gcry_gettext (const char *key) GCC_ATTR_FORMAT_ARG(1);
void _gcry_fatal_error(int rc, const char *text ) JNLIB_GCC_A_NR;
void _gcry_set_gpgrt_post_log_handler (void);
void _gcry_logv (int level, const char *fmt,
va_list arg_ptr) JNLIB_GCC_A_PRINTF(2,0);
void _gcry_log( int level, const char *fmt, ... ) JNLIB_GCC_A_PRINTF(2,3);
void _gcry_log_bug( const char *fmt, ... ) JNLIB_GCC_A_NR_PRINTF(1,2);
void _gcry_log_fatal( const char *fmt, ... ) JNLIB_GCC_A_NR_PRINTF(1,2);
void _gcry_log_error( const char *fmt, ... ) JNLIB_GCC_A_PRINTF(1,2);
void _gcry_log_info( const char *fmt, ... ) JNLIB_GCC_A_PRINTF(1,2);
void _gcry_log_debug( const char *fmt, ... ) JNLIB_GCC_A_PRINTF(1,2);
void _gcry_log_printf ( const char *fmt, ... ) JNLIB_GCC_A_PRINTF(1,2);
void _gcry_log_printhex (const char *text, const void *buffer, size_t length);
void _gcry_log_printmpi (const char *text, gcry_mpi_t mpi);
void _gcry_log_printsxp (const char *text, gcry_sexp_t sexp);
void _gcry_set_log_verbosity( int level );
int _gcry_log_verbosity( int level );
#ifdef JNLIB_GCC_M_FUNCTION
#define BUG() _gcry_bug( __FILE__ , __LINE__, __FUNCTION__ )
#define gcry_assert(expr) (LIKELY(expr)? (void)0 \
: _gcry_assert_failed (STR(expr), __FILE__, __LINE__, __FUNCTION__))
#elif __STDC_VERSION__ >= 199901L
#define BUG() _gcry_bug( __FILE__ , __LINE__, __func__ )
#define gcry_assert(expr) (LIKELY(expr)? (void)0 \
: _gcry_assert_failed (STR(expr), __FILE__, __LINE__, __func__))
#else
#define BUG() _gcry_bug( __FILE__ , __LINE__ )
#define gcry_assert(expr) (LIKELY(expr)? (void)0 \
: _gcry_assert_failed (STR(expr), __FILE__, __LINE__))
#endif
#define log_bug _gcry_log_bug
#define log_fatal _gcry_log_fatal
#define log_error _gcry_log_error
#define log_info _gcry_log_info
#define log_debug _gcry_log_debug
#define log_printf _gcry_log_printf
#define log_printhex _gcry_log_printhex
#define log_printmpi _gcry_log_printmpi
#define log_printsxp _gcry_log_printsxp
/* Compatibility macro. */
#define log_mpidump _gcry_log_printmpi
/* Tokeninze STRING and return a malloced array. */
char **_gcry_strtokenize (const char *string, const char *delim);
/*-- src/hwfeatures.c --*/
#if defined(HAVE_CPU_ARCH_X86)
#define HWF_PADLOCK_RNG (1 << 0)
#define HWF_PADLOCK_AES (1 << 1)
#define HWF_PADLOCK_SHA (1 << 2)
#define HWF_PADLOCK_MMUL (1 << 3)
#define HWF_INTEL_CPU (1 << 4)
#define HWF_INTEL_FAST_SHLD (1 << 5)
#define HWF_INTEL_BMI2 (1 << 6)
#define HWF_INTEL_SSSE3 (1 << 7)
#define HWF_INTEL_SSE4_1 (1 << 8)
#define HWF_INTEL_PCLMUL (1 << 9)
#define HWF_INTEL_AESNI (1 << 10)
#define HWF_INTEL_RDRAND (1 << 11)
#define HWF_INTEL_AVX (1 << 12)
#define HWF_INTEL_AVX2 (1 << 13)
#define HWF_INTEL_FAST_VPGATHER (1 << 14)
#define HWF_INTEL_RDTSC (1 << 15)
#define HWF_INTEL_SHAEXT (1 << 16)
#define HWF_INTEL_VAES_VPCLMUL (1 << 17)
#define HWF_INTEL_AVX512 (1 << 18)
#define HWF_INTEL_GFNI (1 << 19)
#elif defined(HAVE_CPU_ARCH_ARM)
#define HWF_ARM_NEON (1 << 0)
#define HWF_ARM_AES (1 << 1)
#define HWF_ARM_SHA1 (1 << 2)
#define HWF_ARM_SHA2 (1 << 3)
#define HWF_ARM_PMULL (1 << 4)
#define HWF_ARM_SHA3 (1 << 5)
#define HWF_ARM_SM3 (1 << 6)
#define HWF_ARM_SM4 (1 << 7)
#define HWF_ARM_SHA512 (1 << 8)
#define HWF_ARM_SVE (1 << 9)
#define HWF_ARM_SVE2 (1 << 10)
#define HWF_ARM_SVEAES (1 << 11)
#define HWF_ARM_SVEPMULL (1 << 12)
#define HWF_ARM_SVESHA3 (1 << 13)
#define HWF_ARM_SVESM4 (1 << 14)
#elif defined(HAVE_CPU_ARCH_PPC)
#define HWF_PPC_VCRYPTO (1 << 0)
#define HWF_PPC_ARCH_3_00 (1 << 1)
#define HWF_PPC_ARCH_2_07 (1 << 2)
#define HWF_PPC_ARCH_3_10 (1 << 3)
#elif defined(HAVE_CPU_ARCH_S390X)
#define HWF_S390X_MSA (1 << 0)
#define HWF_S390X_MSA_4 (1 << 1)
#define HWF_S390X_MSA_8 (1 << 2)
#define HWF_S390X_MSA_9 (1 << 3)
#define HWF_S390X_VX (1 << 4)
#endif
gpg_err_code_t _gcry_disable_hw_feature (const char *name);
void _gcry_detect_hw_features (void);
unsigned int _gcry_get_hw_features (void);
const char *_gcry_enum_hw_features (int idx, unsigned int *r_feature);
/*-- mpi/mpiutil.c --*/
const char *_gcry_mpi_get_hw_config (void);
/*-- cipher/pubkey.c --*/
/* FIXME: shouldn't this go into mpi.h? */
#ifndef mpi_powm
#define mpi_powm(w,b,e,m) gcry_mpi_powm( (w), (b), (e), (m) )
#endif
/*-- primegen.c --*/
gcry_err_code_t _gcry_primegen_init (void);
gcry_mpi_t _gcry_generate_secret_prime (unsigned int nbits,
gcry_random_level_t random_level,
int (*extra_check)(void*, gcry_mpi_t),
void *extra_check_arg);
gcry_mpi_t _gcry_generate_public_prime (unsigned int nbits,
gcry_random_level_t random_level,
int (*extra_check)(void*, gcry_mpi_t),
void *extra_check_arg);
gcry_err_code_t _gcry_generate_elg_prime (int mode,
unsigned int pbits,
unsigned int qbits,
gcry_mpi_t g,
gcry_mpi_t *r_prime,
gcry_mpi_t **factors);
gcry_mpi_t _gcry_derive_x931_prime (const gcry_mpi_t xp,
const gcry_mpi_t xp1, const gcry_mpi_t xp2,
const gcry_mpi_t e,
gcry_mpi_t *r_p1, gcry_mpi_t *r_p2);
gpg_err_code_t _gcry_generate_fips186_2_prime
(unsigned int pbits, unsigned int qbits,
const void *seed, size_t seedlen,
gcry_mpi_t *r_q, gcry_mpi_t *r_p,
int *r_counter,
void **r_seed, size_t *r_seedlen);
gpg_err_code_t _gcry_generate_fips186_3_prime
(unsigned int pbits, unsigned int qbits,
const void *seed, size_t seedlen,
gcry_mpi_t *r_q, gcry_mpi_t *r_p,
int *r_counter,
void **r_seed, size_t *r_seedlen, int *r_hashalgo);
gpg_err_code_t _gcry_fips186_4_prime_check (const gcry_mpi_t x,
unsigned int bits);
/* Replacements of missing functions (missing-string.c). */
#ifndef HAVE_STPCPY
char *stpcpy (char *a, const char *b);
#endif
#ifndef HAVE_STRCASECMP
int strcasecmp (const char *a, const char *b) _GCRY_GCC_ATTR_PURE;
#endif
#include "../compat/libcompat.h"
/* Macros used to rename missing functions. */
#ifndef HAVE_STRTOUL
#define strtoul(a,b,c) ((unsigned long)strtol((a),(b),(c)))
#endif
#ifndef HAVE_MEMMOVE
#define memmove(d, s, n) bcopy((s), (d), (n))
#endif
#ifndef HAVE_STRICMP
#define stricmp(a,b) strcasecmp( (a), (b) )
#endif
#ifndef HAVE_ATEXIT
#define atexit(a) (on_exit((a),0))
#endif
#ifndef HAVE_RAISE
#define raise(a) kill(getpid(), (a))
#endif
/* Stack burning. */
#ifdef HAVE_GCC_ASM_VOLATILE_MEMORY
#define __gcry_burn_stack_dummy() asm volatile ("":::"memory")
#else
void __gcry_burn_stack_dummy (void);
#endif
void __gcry_burn_stack (unsigned int bytes);
#define _gcry_burn_stack(bytes) \
do { __gcry_burn_stack (bytes); \
__gcry_burn_stack_dummy (); } while(0)
/* To avoid that a compiler optimizes certain memset calls away, this
macro may be used instead. For constant length buffers, memory
wiping is inlined. Dead store elimination of inlined memset is
avoided here by using assembly block after memset. For non-constant
length buffers, memory is wiped through _gcry_fast_wipememory. */
#ifdef HAVE_GCC_ASM_VOLATILE_MEMORY
#define fast_wipememory2_inline(_ptr,_set,_len) do { \
memset((_ptr), (_set), (_len)); \
asm volatile ("\n" :: "r" (_ptr) : "memory"); \
} while(0)
#else
#define fast_wipememory2_inline(_ptr,_set,_len) \
_gcry_fast_wipememory2((void *)_ptr, _set, _len)
#endif
#define wipememory2(_ptr,_set,_len) do { \
if (!CONSTANT_P(_len) || !CONSTANT_P(_set)) { \
if (CONSTANT_P(_set) && (_set) == 0) \
_gcry_fast_wipememory((void *)(_ptr), (_len)); \
else \
_gcry_fast_wipememory2((void *)(_ptr), (_set), (_len)); \
} else { \
fast_wipememory2_inline((void *)(_ptr), (_set), (_len)); \
} \
} while(0)
#define wipememory(_ptr,_len) wipememory2((_ptr),0,(_len))
void _gcry_fast_wipememory(void *ptr, size_t len);
void _gcry_fast_wipememory2(void *ptr, int set, size_t len);
/* Digit predicates. */
#define digitp(p) (*(p) >= '0' && *(p) <= '9')
#define octdigitp(p) (*(p) >= '0' && *(p) <= '7')
#define alphap(a) ( (*(a) >= 'A' && *(a) <= 'Z') \
|| (*(a) >= 'a' && *(a) <= 'z'))
#define hexdigitp(a) (digitp (a) \
|| (*(a) >= 'A' && *(a) <= 'F') \
|| (*(a) >= 'a' && *(a) <= 'f'))
/* Init functions. */
gcry_err_code_t _gcry_cipher_init (void);
gcry_err_code_t _gcry_md_init (void);
gcry_err_code_t _gcry_mac_init (void);
gcry_err_code_t _gcry_pk_init (void);
gcry_err_code_t _gcry_secmem_module_init (void);
gcry_err_code_t _gcry_mpi_init (void);
/* Memory management. */
#define GCRY_ALLOC_FLAG_SECURE (1 << 0)
#define GCRY_ALLOC_FLAG_XHINT (1 << 1) /* Called from xmalloc. */
/*-- sexp.c --*/
gcry_err_code_t _gcry_sexp_vbuild (gcry_sexp_t *retsexp, size_t *erroff,
const char *format, va_list arg_ptr);
char *_gcry_sexp_nth_string (const gcry_sexp_t list, int number);
gpg_err_code_t _gcry_sexp_vextract_param (gcry_sexp_t sexp, const char *path,
const char *list, va_list arg_ptr);
void *_gcry_hex2buffer (const char *string, size_t *r_length);
/*-- fips.c --*/
extern int _gcry_no_fips_mode_required;
void _gcry_initialize_fips_mode (int force);
int _gcry_fips_to_activate (void);
/* This macro returns true if fips mode is enabled. This is
independent of the fips required finite state machine and only used
to enable fips specific code.
No locking is required because we have the requirement that this
variable is only initialized once with no other threads
existing. */
#define fips_mode() (!_gcry_no_fips_mode_required)
void _gcry_fips_signal_error (const char *srcfile,
int srcline,
const char *srcfunc,
int is_fatal,
const char *description);
#ifdef JNLIB_GCC_M_FUNCTION
# define fips_signal_error(a) \
_gcry_fips_signal_error (__FILE__, __LINE__, __FUNCTION__, 0, (a))
# define fips_signal_fatal_error(a) \
_gcry_fips_signal_error (__FILE__, __LINE__, __FUNCTION__, 1, (a))
#else
# define fips_signal_error(a) \
_gcry_fips_signal_error (__FILE__, __LINE__, NULL, 0, (a))
# define fips_signal_fatal_error(a) \
_gcry_fips_signal_error (__FILE__, __LINE__, NULL, 1, (a))
#endif
gpg_err_code_t _gcry_fips_indicator (unsigned long *);
int _gcry_fips_indicator_cipher (va_list arg_ptr);
int _gcry_fips_indicator_mac (va_list arg_ptr);
int _gcry_fips_indicator_md (va_list arg_ptr);
int _gcry_fips_indicator_kdf (va_list arg_ptr);
int _gcry_fips_indicator_function (va_list arg_ptr);
int _gcry_fips_indicator_pk_flags (va_list arg_ptr);
int _gcry_fips_is_operational (void);
/* Return true if the library is in the operational state. */
#define fips_is_operational() \
(!_gcry_global_any_init_done ? \
_gcry_global_is_operational() : \
(!fips_mode () || _gcry_global_is_operational ()))
#define fips_not_operational() (GPG_ERR_NOT_OPERATIONAL)
int _gcry_fips_test_operational (void);
int _gcry_fips_test_error_or_operational (void);
gpg_err_code_t _gcry_fips_run_selftests (int extended);
void _gcry_fips_noreturn (void);
#define fips_noreturn() (_gcry_fips_noreturn ())
+void _gcry_fips_check_kdf_compliant (int algo, int subalgo);
#endif /* G10LIB_H */
diff --git a/tests/t-kdf.c b/tests/t-kdf.c
index 10f64a7c..836cf74a 100644
--- a/tests/t-kdf.c
+++ b/tests/t-kdf.c
@@ -1,2015 +1,2170 @@
/* 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, see <https://www.gnu.org/licenses/>.
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <assert.h>
#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 </dev/null -o /dev/null -c --passphrase "$pass" \
--s2k-mode $mode --s2k-digest $digest --s2k-count $count \
--cipher-algo $cipher ; done >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,
1,
10, /* too short dklen for FIPS */
"\x0c\x60\xc8\x0f\x96\x1f\x0e\x71\xf3\xa9"
},
{
"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 > 7)
{
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 || tv[tvidx].dklen < 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 <pthread.h>
#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]);
}
}
}
+static void
+check_fips_gcry_kdf_derive(void)
+{
+ static struct {
+ const char *p; /* Passphrase. */
+ size_t plen; /* Length of P. */
+ int algo;
+ int subalgo;
+ const char *salt;
+ size_t saltlen;
+ unsigned long iterations;
+ int dklen; /* Requested key length. */
+ const char *dk; /* Derived key. */
+ int expect_error;
+ } tv[] = {
+ {
+ "passwordPASSWORDpassword", 24,
+ GCRY_KDF_PBKDF2, GCRY_MD_SHA1,
+ "saltSALTsaltSALTsaltSALTsaltSALTsalt", 36,
+ 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",
+ 0
+ },
+ {
+ "pleaseletmein", 13,
+ GCRY_KDF_SCRYPT, 16384,
+ "SodiumChloride", 14,
+ 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",
+ 1 /* By checking service indicator, forbidden because unallowed algo */
+ },
+ {
+ "passwor", 7,
+ GCRY_KDF_PBKDF2, GCRY_MD_SHA1,
+ "saltSALTsaltSALTsaltSALTsaltSALTsalt", 36,
+ 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", /* this is wrong but we don't care because
+ it should fail anyway */
+ 2 /* forbidden because passphrase len is too small */
+ },
+ {
+ "passwordPASSWORDpassword", 24,
+ GCRY_KDF_PBKDF2, GCRY_MD_SHA1,
+ "saltSALTsaltSAL", 15,
+ 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", /* this is wrong but we don't care because
+ it should fail anyway */
+ 2 /* forbidden because salt len is too small */
+ },
+ {
+ "passwordPASSWORDpassword", 24,
+ GCRY_KDF_PBKDF2, GCRY_MD_SHA1,
+ "saltSALTsaltSALTsaltSALTsaltSALTsalt", 36,
+ 999,
+ 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", /* this is wrong but we don't care because
+ it should fail anyway */
+ 2 /* forbidden because too few iterations */
+ },
+ {
+ "passwordPASSWORDpassword", 24,
+ GCRY_KDF_PBKDF2, GCRY_MD_SHA1,
+ "saltSALTsaltSALTsaltSALTsaltSALTsalt", 36,
+ 4096,
+ 13,
+ "\x3d\x2e\xec\x4f\xe4\x1c\x84\x9b\x80\xc8"
+ "\xd8\x36\x62", /* this is wrong but we don't care because
+ it should fail anyway */
+ 2 /* forbidden because key size too small */
+ },
+ };
+
+ int tvidx;
+ gpg_error_t err;
+ unsigned char outbuf[100];
+ int i;
+
+ for (tvidx=0; tvidx < DIM(tv); tvidx++)
+ {
+ if (verbose)
+ fprintf (stderr, "checking gcry_kdf_derive test vector %d algo %d for fips\n",
+ tvidx, tv[tvidx].algo);
+ assert (tv[tvidx].dklen <= sizeof outbuf);
+ err = gcry_kdf_derive (tv[tvidx].p, tv[tvidx].plen,
+ tv[tvidx].algo, tv[tvidx].subalgo,
+ tv[tvidx].salt, tv[tvidx].saltlen,
+ tv[tvidx].iterations, tv[tvidx].dklen, outbuf);
+
+ if (err)
+ {
+ if (tv[tvidx].expect_error == 0 || tv[tvidx].expect_error == 1)
+ fail ("gcry_kdf_derive test %d unexpectedly returned an error in FIPS mode: %s\n",
+ tvidx, gpg_strerror (err));
+ }
+ else
+ {
+ unsigned long fips_service_indicator;
+
+ gcry_get_fips_service_indicator (&fips_service_indicator);
+
+ /* Failure by an error expected. Something goes wrong. */
+ if (tv[tvidx].expect_error == 2)
+ {
+ fail ("gcry_kdf_derive test %d unexpectedly succeed in FIPS mode.\n",
+ tvidx);
+ continue;
+ }
+
+ /* Success with fips_service_indicator == 0 expected. */
+ if (tv[tvidx].expect_error == 0 && fips_service_indicator)
+ {
+ fail ("gcry_kdf_derive test %d unexpectedly set %08lx in FIPS mode.\n",
+ tvidx, fips_service_indicator);
+ continue;
+ }
+
+#define COMPUTATION_MAY_NOT_BE_DONE_IN_THIS_CASE 0 /* Yes, it computes. */
+#if COMPUTATION_MAY_NOT_BE_DONE_IN_THIS_CASE
+ /* Failure with fips_service_indicator != 0 expected. */
+ if (fips_service_indicator && tv[tvidx].expect_error == 1)
+ continue;
+#else
+ /* It's a failure, but let us check the value. */
+#endif
+
+ if (memcmp (outbuf, tv[tvidx].dk, tv[tvidx].dklen))
+ {
+ fail ("gcry_kdf_derive 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);
+ }
+ }
+ }
+}
+
+
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();
+ check_fips_indicators ();
+ if (in_fips_mode)
+ check_fips_gcry_kdf_derive ();
}
return error_count ? 1 : 0;
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Apr 14, 9:28 PM (1 d, 12 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
9e/5d/e63137ca4bcab67bdae43c74e4fb

Event Timeline