diff options
author | NIIBE Yutaka <gniibe@fsij.org> | 2022-01-26 00:22:50 +0900 |
---|---|---|
committer | NIIBE Yutaka <gniibe@fsij.org> | 2022-01-26 00:22:50 +0900 |
commit | bafdb90d97b65db541ea917088ca956e6a364f6b (patch) | |
tree | b2041913245036b27a31212f292f3c6d0e94851b | |
parent | 3d353782d84b9720262d7b05adfae3aef7ff843b (diff) | |
download | libgcrypt-bafdb90d97b65db541ea917088ca956e6a364f6b.tar.gz |
cipher: Add new API for modern KDF function.
* cipher/kdf.c (hash, argon2_genh0_first_blocks): New for Argon2.
(argon2_init, argon2_ctl, argon2_iterator): Likewise.
(argon2_compute_row, argon2_final, argon2_close): Likewise.
(argon2_open): Likewise.
(balloon_open): New for Balloon.
(_gcry_kdf_open, _gcry_kdf_ctl, _gcry_kdf_iterator): Add new API.
(_gcry_kdf_compute_row, _gcry_kdf_final, _gcry_kdf_close): Likewise.
* src/gcrypt-int.h: Add declarations for new API.
* src/gcrypt.h.in: Likewise.
(enum gcry_kdf_algos): Add GCRY_KDF_ARGON2 and GCRY_KDF_BALLOON.
(enum gcry_kdf_subalgo_argon2): Add GCRY_KDF_ARGON2D,
GCRY_KDF_ARGON2I, and GCRY_KDF_ARGON2ID.
* src/libgcrypt.def, src/libgcrypt.vers: Update.
* src/visibility.h: Likewise.
* src/visibility.c: Add new API.
* tests/Makefile.am (t_kdf_LDADD, t_kdf_CFLAGS): Enable use of pthread.
* tests/t-kdf.c (check_argon2): New, not enabled yet.
--
New API has been added, decoupling thread support. Implementation of
Argon2 is on-going. Test is not enabled yet.
GnuPG-bug-id: 5797
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
-rw-r--r-- | cipher/kdf.c | 679 | ||||
-rw-r--r-- | src/gcrypt-int.h | 19 | ||||
-rw-r--r-- | src/gcrypt.h.in | 31 | ||||
-rw-r--r-- | src/libgcrypt.def | 7 | ||||
-rw-r--r-- | src/libgcrypt.vers | 3 | ||||
-rw-r--r-- | src/visibility.c | 55 | ||||
-rw-r--r-- | src/visibility.h | 12 | ||||
-rw-r--r-- | tests/Makefile.am | 2 | ||||
-rw-r--r-- | tests/t-kdf.c | 189 |
9 files changed, 987 insertions, 10 deletions
diff --git a/cipher/kdf.c b/cipher/kdf.c index 475b97e1..e570282b 100644 --- a/cipher/kdf.c +++ b/cipher/kdf.c @@ -65,7 +65,7 @@ openpgp_s2k (const void *passphrase, size_t passphraselen, _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) { @@ -305,7 +305,684 @@ _gcry_kdf_derive (const void *passphrase, size_t passphraselen, 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; + void *user_data; + gpg_err_code_t ec; + + unsigned int pass; + unsigned int lane; + unsigned int slice; + unsigned int index; +}; + +/* Argon2 context */ +struct argon2_context { + int algo; + int hash_type; + + unsigned int outlen; + unsigned int n_threads; + + 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; + + unsigned int step; + + unsigned int r; + unsigned int s; + unsigned int l; + unsigned int t; + + gcry_md_hd_t hd; + unsigned char *block; + struct argon2_thread_data *thread_data; + + unsigned char out[1]; /* In future, we may use flexible array member. */ +}; + +enum argon2_iterator_step { + ARGON2_ITERATOR_STEP0, + ARGON2_ITERATOR_STEP1, + ARGON2_ITERATOR_STEP2, + ARGON2_ITERATOR_STEP3, + ARGON2_ITERATOR_STEP4 +}; + +#define ARGON2_VERSION 0x13 + +static gpg_err_code_t +hash (gcry_md_hd_t hd, const unsigned char *input, unsigned int inputlen, + unsigned char *output, unsigned int outputlen) +{ + gpg_err_code_t ec = 0; + unsigned char buf[4]; + const unsigned char *digest; + gcry_md_hd_t hd1; + int algo; + + _gcry_md_reset (hd); + + if (outputlen < 64) + { + if (outputlen == 48) + algo = GCRY_MD_BLAKE2B_384; + else if (outputlen == 32) + algo = GCRY_MD_BLAKE2B_256; + else if (outputlen == 20) + algo = GCRY_MD_BLAKE2B_160; + else + return GPG_ERR_NOT_IMPLEMENTED; + + ec = _gcry_md_open (&hd1, algo, 0); + if (ec) + return ec; + + buf_put_le32 (buf, outputlen); + _gcry_md_write (hd1, buf, 4); + _gcry_md_write (hd1, input, inputlen); + digest = _gcry_md_read (hd1, algo); + memcpy (output, digest, outputlen); + _gcry_md_close (hd1); + } + else if (outputlen == 64) + { + buf_put_le32 (buf, outputlen); + _gcry_md_write (hd, buf, 4); + _gcry_md_write (hd, input, inputlen); + digest = _gcry_md_read (hd, GCRY_MD_BLAKE2B_512); + memcpy (output, digest, 64); + } + else + { + int i, r; + unsigned int remained; + unsigned char d[64]; + + i = 0; + r = outputlen/32; + + buf_put_le32 (buf, outputlen); + _gcry_md_write (hd, buf, 4); + _gcry_md_write (hd, input, inputlen); + + do + { + digest = _gcry_md_read (hd, GCRY_MD_BLAKE2B_512); + memcpy (d, digest, 64); + memcpy (output+i*32, digest, 32); + i++; + + _gcry_md_reset (hd); + _gcry_md_write (hd, d, 64); + } + while (i < r); + + remained = outputlen - 32*r; + if (remained) + { + if (remained == 20) + algo = GCRY_MD_BLAKE2B_160; + else + return GPG_ERR_NOT_IMPLEMENTED; + + ec = _gcry_md_open (&hd1, algo, 0); + if (ec) + return ec; + + _gcry_md_write (hd1, d, 64); + digest = _gcry_md_read (hd1, algo); + memcpy (output+r*32, digest, remained); + _gcry_md_close (hd1); + } + } + + return 0; +} + +static gpg_err_code_t +argon2_genh0_first_blocks (argon2_ctx_t a) +{ + gpg_err_code_t ec = 0; + unsigned char h0_01_i[72]; + const unsigned char *digest; + unsigned char buf[4]; + int i; + + buf_put_le32 (buf, a->lanes); + _gcry_md_write (a->hd, buf, 4); + + buf_put_le32 (buf, a->outlen); + _gcry_md_write (a->hd, buf, 4); + + buf_put_le32 (buf, a->m_cost); + _gcry_md_write (a->hd, buf, 4); + + buf_put_le32 (buf, a->passes); + _gcry_md_write (a->hd, buf, 4); + + buf_put_le32 (buf, ARGON2_VERSION); + _gcry_md_write (a->hd, buf, 4); + + buf_put_le32 (buf, a->hash_type); + _gcry_md_write (a->hd, buf, 4); + + buf_put_le32 (buf, a->passwordlen); + _gcry_md_write (a->hd, buf, 4); + _gcry_md_write (a->hd, a->password, a->passwordlen); + + buf_put_le32 (buf, a->saltlen); + _gcry_md_write (a->hd, buf, 4); + _gcry_md_write (a->hd, a->salt, a->saltlen); + + buf_put_le32 (buf, a->keylen); + _gcry_md_write (a->hd, buf, 4); + if (a->key) + _gcry_md_write (a->hd, a->key, a->keylen); + + buf_put_le32 (buf, a->adlen); + _gcry_md_write (a->hd, buf, 4); + if (a->ad) + _gcry_md_write (a->hd, a->ad, a->adlen); + + digest = _gcry_md_read (a->hd, GCRY_MD_BLAKE2B_512); + + memcpy (h0_01_i, digest, 64); + + for (i = 0; i < a->lanes; i++) + { + /*FIXME*/ + memset (h0_01_i+64, 0, 4); + buf_put_le32 (h0_01_i+64+4, i); + ec = hash (a->hd, h0_01_i, 72, a->block+1024*i, 1024); + if (ec) + break; + + buf_put_le32 (h0_01_i+64, 1); + ec = hash (a->hd, h0_01_i, 72, a->block+1024*(i+a->lanes), 1024); + if (ec) + break; + } + + return ec; +} + +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; + gcry_md_hd_t hd; + 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->r = a->s = a->l = a->t = 0; + a->step = ARGON2_ITERATOR_STEP0; + + a->hd = NULL; + a->block = NULL; + a->thread_data = NULL; + + ec = _gcry_md_open (&hd, GCRY_MD_BLAKE2B_512, 0); + if (ec) + return ec; + + block = xtrymalloc (1024 * memory_blocks); + if (!block) + { + ec = gpg_err_code_from_errno (errno); + _gcry_md_close (hd); + return ec; + } + + thread_data = xtrymalloc (a->n_threads * sizeof (struct argon2_thread_data)); + if (!thread_data) + { + ec = gpg_err_code_from_errno (errno); + xfree (block); + _gcry_md_close (hd); + return ec; + } + + memset (thread_data, 0, a->n_threads * sizeof (struct argon2_thread_data)); + + a->hd = hd; + a->block = block; + a->thread_data = thread_data; + return 0; +} + +static gpg_err_code_t +argon2_ctl (argon2_ctx_t a, int cmd, void *buffer, size_t buflen) +{ + gpg_err_code_t ec = GPG_ERR_NOT_IMPLEMENTED; + + (void)a; + (void)cmd; + (void)buffer; + (void)buflen; + return ec; +} + +static gpg_err_code_t +argon2_iterator (argon2_ctx_t a, int *action, void **arg_p) +{ + switch (a->step) + { + case ARGON2_ITERATOR_STEP0: + argon2_genh0_first_blocks (a); + /* continue */ + *action = 3; + *arg_p = NULL; + a->step = ARGON2_ITERATOR_STEP1; + return 0; + + case ARGON2_ITERATOR_STEP1: + for (a->r = 0; a->r < a->passes; a->r++) + for (a->s = 0; a->s < 4; a->s++) + { + struct argon2_thread_data *thread_data; + + for (a->l = 0; a->l < a->lanes; a->l++) + { + if (a->l >= a->n_threads) + { + /* Join a thread. */ + thread_data = &a->thread_data[a->t]; + *action = 2; + *arg_p = thread_data; + a->step = ARGON2_ITERATOR_STEP2; + return 0; + + case ARGON2_ITERATOR_STEP2: + thread_data = &a->thread_data[a->t]; + if (thread_data->ec) + return thread_data->ec; + } + + /* Create a thread. */ + thread_data = &a->thread_data[a->t]; + thread_data->a = a; + thread_data->user_data = NULL; + thread_data->ec = 0; + thread_data->pass = a->r; + thread_data->lane = a->s; + thread_data->slice = a->l; + thread_data->index = a->t; + *action = 1; + *arg_p = thread_data; + a->step = ARGON2_ITERATOR_STEP3; + return 0; + + case ARGON2_ITERATOR_STEP3: + a->t = (a->t + 1) % a->n_threads; + } + + for (a->l = a->lanes - a->n_threads; a->l < a->lanes; a->l++) + { + thread_data = &a->thread_data[a->t]; + + /* Join a thread. */ + *action = 2; + *arg_p = thread_data; + a->step = ARGON2_ITERATOR_STEP4; + return 0; + + case ARGON2_ITERATOR_STEP4: + thread_data = &a->thread_data[a->t]; + if (thread_data->ec) + return thread_data->ec; + a->t = (a->t + 1) % a->n_threads; + } + } + } + + *action = 0; + *arg_p = NULL; + a->step = ARGON2_ITERATOR_STEP0; + return 0; +} + +static gpg_err_code_t +argon2_compute_row (argon2_ctx_t a, void *arg) +{ + gpg_err_code_t ec = GPG_ERR_NOT_IMPLEMENTED; + (void)a; + (void)arg; + return ec; +} + +static gpg_err_code_t +argon2_final (argon2_ctx_t a, size_t resultlen, void *result) +{ + gpg_err_code_t ec; + int i, j; + + if (resultlen != a->outlen) + return GPG_ERR_INV_VALUE; + + memset (a->block, 0, 1024); + for (i = 0; i < a->lanes; i++) + { + unsigned char *p0; + unsigned char *p1; /*FIXME*/ + + p0 = a->block; + p1 = p0 + a->lane_length * i + (a->segment_length - 1)*1024; + + for (j = 0; j < 1024; j++) + p0[j] ^= p1[j]; + } + + ec = hash (a->hd, a->block, 1024, result, a->outlen); + return ec; +} + +static void +argon2_close (argon2_ctx_t a) +{ + size_t n; + + n = offsetof (struct argon2_context, out) + a->outlen; + + if (a->hd) + _gcry_md_close (a->hd); + + 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; + unsigned int n_threads = 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, n_threads ] */ + if (paramlen < 3 || paramlen > 5) + 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 (paramlen == 5) + n_threads = (unsigned int)param[4]; + + if (!(taglen == 64 || taglen == 48 + || taglen % 32 == 0 || taglen % 32 == 20)) + /* + * FIXME: To support arbitrary taglen, we need to expose + * internal API of Blake2b. + */ + return GPG_ERR_NOT_IMPLEMENTED; + } + + 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->n_threads = n_threads; + + 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->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; +} + + +static gpg_err_code_t +balloon_open (gcry_kdf_hd_t *hd, int subalgo, + const unsigned long *param, unsigned int paramlen, + const void *passphrase, size_t passphraselen, + const void *salt, size_t saltlen) +{ + /* + * It should have space_cost and time_cost. + * Optionally, for parallelised version, it has parallelism. + */ + if (paramlen != 2 && paramlen != 3) + return GPG_ERR_INV_VALUE; + + (void)param; + (void)subalgo; + (void)passphrase; + (void)passphraselen; + (void)salt; + (void)saltlen; + *hd = NULL; + return GPG_ERR_NOT_IMPLEMENTED; +} + + +struct gcry_kdf_handle { + int algo; + /* And algo specific parts come. */ +}; + +gpg_err_code_t +_gcry_kdf_open (gcry_kdf_hd_t *hd, int algo, int subalgo, + const unsigned long *param, unsigned int paramlen, + const void *passphrase, size_t passphraselen, + const void *salt, size_t saltlen, + const void *key, size_t keylen, + const void *ad, size_t adlen) +{ + gpg_err_code_t ec; + + switch (algo) + { + case GCRY_KDF_ARGON2: + if (!passphraselen || !saltlen) + ec = GPG_ERR_INV_VALUE; + else + ec = argon2_open (hd, subalgo, param, paramlen, + passphrase, passphraselen, salt, saltlen, + key, keylen, ad, adlen); + break; + + case GCRY_KDF_BALLOON: + if (!passphraselen || !saltlen) + ec = GPG_ERR_INV_VALUE; + else + { + (void)key; + (void)keylen; + (void)ad; + (void)adlen; + ec = balloon_open (hd, subalgo, param, paramlen, + passphrase, passphraselen, salt, saltlen); + } + break; + + default: + ec = GPG_ERR_UNKNOWN_ALGORITHM; + break; + } + + return ec; +} +gpg_err_code_t +_gcry_kdf_ctl (gcry_kdf_hd_t h, int cmd, void *buffer, size_t buflen) +{ + gpg_err_code_t ec; + + switch (h->algo) + { + case GCRY_KDF_ARGON2: + ec = argon2_ctl ((argon2_ctx_t)h, cmd, buffer, buflen); + break; + + default: + ec = GPG_ERR_UNKNOWN_ALGORITHM; + break; + } + + return ec; +} + +gpg_err_code_t +_gcry_kdf_iterator (gcry_kdf_hd_t h, int *action, void **arg_p) +{ + gpg_err_code_t ec; + + switch (h->algo) + { + case GCRY_KDF_ARGON2: + ec = argon2_iterator ((argon2_ctx_t)h, action, arg_p); + break; + + default: + ec = GPG_ERR_UNKNOWN_ALGORITHM; + break; + } + + return ec; +} + +gpg_err_code_t +_gcry_kdf_compute_row (gcry_kdf_hd_t h, void *arg) +{ + gpg_err_code_t ec; + + switch (h->algo) + { + case GCRY_KDF_ARGON2: + ec = argon2_compute_row ((argon2_ctx_t)h, arg); + 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)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)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, diff --git a/src/gcrypt-int.h b/src/gcrypt-int.h index 62e8b699..af7e2e26 100644 --- a/src/gcrypt-int.h +++ b/src/gcrypt-int.h @@ -140,9 +140,9 @@ gpg_err_code_t _gcry_md_extract (gcry_md_hd_t hd, int algo, void *buffer, void _gcry_md_hash_buffer (int algo, void *digest, const void *buffer, size_t length); gpg_err_code_t _gcry_md_hash_buffers_extract (int algo, unsigned int flags, - void *digest, int digestlen, - const gcry_buffer_t *iov, - int iovcnt); + void *digest, int digestlen, + const gcry_buffer_t *iov, + int iovcnt); gpg_err_code_t _gcry_md_hash_buffers (int algo, unsigned int flags, void *digest, const gcry_buffer_t *iov, int iovcnt); @@ -207,6 +207,19 @@ gpg_err_code_t _gcry_kdf_derive (const void *passphrase, size_t passphraselen, unsigned long iterations, size_t keysize, void *keybuffer); +gpg_err_code_t _gcry_kdf_open (gcry_kdf_hd_t *hd, int algo, int subalgo, + const unsigned long *param, unsigned int paramlen, + const void *passphrase, size_t passphraselen, + const void *salt, size_t saltlen, + const void *key, size_t keylen, + const void *ad, size_t adlen); +gpg_err_code_t _gcry_kdf_ctl (gcry_kdf_hd_t h, int cmd, void *buffer, + size_t buflen); +gpg_err_code_t _gcry_kdf_iterator (gcry_kdf_hd_t h, int *action, void **arg_p); +gpg_err_code_t _gcry_kdf_compute_row (gcry_kdf_hd_t h, void *arg); +gpg_err_code_t _gcry_kdf_final (gcry_kdf_hd_t h, size_t resultlen, void *result); +void _gcry_kdf_close (gcry_kdf_hd_t h); + gpg_err_code_t _gcry_prime_generate (gcry_mpi_t *prime, unsigned int prime_bits, diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in index 6376b446..ea771db7 100644 --- a/src/gcrypt.h.in +++ b/src/gcrypt.h.in @@ -118,7 +118,7 @@ extern "C" { #ifdef _GCRYPT_IN_LIBGCRYPT #define _GCRY_ATTR_INTERNAL #else -#define _GCRY_ATTR_INTERNAL _GCRY_GCC_ATTR_DEPRECATED +#define _GCRY_ATTR_INTERNAL _GCRY_GCC_ATTR_DEPRECATED #endif /* Wrappers for the libgpg-error library. */ @@ -383,7 +383,7 @@ gcry_error_t gcry_sexp_build (gcry_sexp_t *retsexp, size_t *erroff, /* Like gcry_sexp_build, but uses an array instead of variable function arguments. */ gcry_error_t gcry_sexp_build_array (gcry_sexp_t *retsexp, size_t *erroff, - const char *format, void **arg_list); + const char *format, void **arg_list); /* Release the S-expression object SEXP */ void gcry_sexp_release (gcry_sexp_t sexp); @@ -1591,7 +1591,16 @@ enum gcry_kdf_algos GCRY_KDF_ITERSALTED_S2K = 19, GCRY_KDF_PBKDF1 = 33, GCRY_KDF_PBKDF2 = 34, - GCRY_KDF_SCRYPT = 48 + GCRY_KDF_SCRYPT = 48, + GCRY_KDF_ARGON2 = 64, + GCRY_KDF_BALLOON = 65 + }; + +enum gcry_kdf_subalgo_argon2 + { + GCRY_KDF_ARGON2D = 0, + GCRY_KDF_ARGON2I = 1, + GCRY_KDF_ARGON2ID = 2 }; /* Derive a key from a passphrase. */ @@ -1601,8 +1610,20 @@ gpg_error_t gcry_kdf_derive (const void *passphrase, size_t passphraselen, unsigned long iterations, size_t keysize, void *keybuffer); - - +/* Another API to derive a key from a passphrase. */ +typedef struct gcry_kdf_handle *gcry_kdf_hd_t; +gcry_error_t gcry_kdf_open (gcry_kdf_hd_t *hd, int algo, int subalgo, + const unsigned long *param, unsigned int paramlen, + const void *passphrase, size_t passphraselen, + const void *salt, size_t saltlen, + const void *key, size_t keylen, + const void *ad, size_t adlen); +gcry_error_t gcry_kdf_ctl (gcry_kdf_hd_t h, int cmd, void *buffer, + size_t buflen); +gcry_error_t gcry_kdf_iterator (gcry_kdf_hd_t h, int *action_p, void **arg_p); +gcry_error_t gcry_kdf_compute_row (gcry_kdf_hd_t h, void *arg); +gcry_error_t gcry_kdf_final (gcry_kdf_hd_t h, size_t resultlen, void *result); +void gcry_kdf_close (gcry_kdf_hd_t h); /************************************ * * diff --git a/src/libgcrypt.def b/src/libgcrypt.def index bd2f076b..1996481f 100644 --- a/src/libgcrypt.def +++ b/src/libgcrypt.def @@ -293,4 +293,11 @@ EXPORTS gcry_pk_hash_verify @256 gcry_pk_random_override_new @257 + gcry_kdf_open @258 + gcry_kdf_ctl @259 + gcry_kdf_iterator @260 + gcry_kdf_compute_row @261 + gcry_kdf_final @262 + gcry_kdf_close @263 + ;; end of file with public symbols for Windows. diff --git a/src/libgcrypt.vers b/src/libgcrypt.vers index 8fd89779..2b81caea 100644 --- a/src/libgcrypt.vers +++ b/src/libgcrypt.vers @@ -123,6 +123,9 @@ GCRYPT_1.6 { gcry_ctx_release; gcry_pk_hash_sign; gcry_pk_hash_verify; gcry_pk_random_override_new; + + gcry_kdf_open; gcry_kdf_ctl; gcry_kdf_iterator; + gcry_kdf_compute_row; gcry_kdf_final; gcry_kdf_close; local: *; diff --git a/src/visibility.c b/src/visibility.c index 563d3f3b..d7f71254 100644 --- a/src/visibility.c +++ b/src/visibility.c @@ -923,7 +923,7 @@ gcry_mac_get_algo_keylen (int algo) gcry_error_t gcry_mac_open (gcry_mac_hd_t *handle, int algo, unsigned int flags, - gcry_ctx_t ctx) + gcry_ctx_t ctx) { if (!fips_is_operational ()) { @@ -1359,6 +1359,59 @@ gcry_kdf_derive (const void *passphrase, size_t passphraselen, keysize, keybuffer)); } +gpg_error_t +gcry_kdf_open (gcry_kdf_hd_t *hd, int algo, int subalgo, + const unsigned long *param, unsigned int paramlen, + const void *passphrase, size_t passphraselen, + const void *salt, size_t saltlen, + const void *key, size_t keylen, + const void *ad, size_t adlen) +{ + if (!fips_is_operational ()) + return gpg_error (fips_not_operational ()); + return gpg_error (_gcry_kdf_open (hd, algo, subalgo, param, paramlen, + passphrase, passphraselen, salt, saltlen, + key, keylen, ad, adlen)); +} + +gcry_error_t +gcry_kdf_ctl (gcry_kdf_hd_t h, int cmd, void *buffer, size_t buflen) +{ + if (!fips_is_operational ()) + return gpg_error (fips_not_operational ()); + return gpg_error (_gcry_kdf_ctl (h, cmd, buffer, buflen)); +} + +gcry_error_t +gcry_kdf_iterator (gcry_kdf_hd_t h, int *action, void **arg_p) +{ + if (!fips_is_operational ()) + return gpg_error (fips_not_operational ()); + return gpg_error (_gcry_kdf_iterator (h, action, arg_p)); +} + +gcry_error_t +gcry_kdf_compute_row (gcry_kdf_hd_t h, void *arg) +{ + if (!fips_is_operational ()) + return gpg_error (fips_not_operational ()); + return gpg_error (_gcry_kdf_compute_row (h, arg)); +} + +gcry_error_t +gcry_kdf_final (gcry_kdf_hd_t h, size_t resultlen, void *result) +{ + if (!fips_is_operational ()) + return gpg_error (fips_not_operational ()); + return gpg_error (_gcry_kdf_final (h, resultlen, result)); +} + +void +gcry_kdf_close (gcry_kdf_hd_t h) +{ + _gcry_kdf_close (h); +} + void gcry_randomize (void *buffer, size_t length, enum gcry_random_level level) { diff --git a/src/visibility.h b/src/visibility.h index b48182d0..625d6f2b 100644 --- a/src/visibility.h +++ b/src/visibility.h @@ -172,6 +172,12 @@ MARK_VISIBLEX (gcry_pk_hash_verify) MARK_VISIBLEX (gcry_pk_random_override_new) MARK_VISIBLEX (gcry_kdf_derive) +MARK_VISIBLEX (gcry_kdf_open) +MARK_VISIBLEX (gcry_kdf_ctl) +MARK_VISIBLEX (gcry_kdf_iterator) +MARK_VISIBLEX (gcry_kdf_compute_row) +MARK_VISIBLEX (gcry_kdf_final) +MARK_VISIBLEX (gcry_kdf_close) MARK_VISIBLEX (gcry_prime_check) MARK_VISIBLEX (gcry_prime_generate) @@ -412,6 +418,12 @@ MARK_VISIBLEX (_gcry_mpi_get_const) #define gcry_mac_ctl _gcry_USE_THE_UNDERSCORED_FUNCTION #define gcry_kdf_derive _gcry_USE_THE_UNDERSCORED_FUNCTION +#define gcry_kdf_open _gcry_USE_THE_UNDERSCORED_FUNCTION +#define gcry_kdf_ctl _gcry_USE_THE_UNDERSCORED_FUNCTION +#define gcry_kdf_iterator _gcry_USE_THE_UNDERSCORED_FUNCTION +#define gcry_kdf_compute_row _gcry_USE_THE_UNDERSCORED_FUNCTION +#define gcry_kdf_final _gcry_USE_THE_UNDERSCORED_FUNCTION +#define gcry_kdf_close _gcry_USE_THE_UNDERSCORED_FUNCTION #define gcry_prime_check _gcry_USE_THE_UNDERSCORED_FUNCTION #define gcry_prime_generate _gcry_USE_THE_UNDERSCORED_FUNCTION diff --git a/tests/Makefile.am b/tests/Makefile.am index 41cf8aaf..b42156f0 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -92,6 +92,8 @@ xtestsuite_driver = .libs/testdrv.exe else xtestsuite_libs = ../src/.libs/libgcrypt.so* xtestsuite_driver = testdrv +t_kdf_LDADD = $(standard_ldadd) $(GPG_ERROR_MT_LIBS) @LDADD_FOR_TESTS_KLUDGE@ +t_kdf_CFLAGS = $(GPG_ERROR_MT_CFLAGS) endif # xcheck uses our new testdrv instead of the automake test runner. diff --git a/tests/t-kdf.c b/tests/t-kdf.c index 3ad18786..399e9702 100644 --- a/tests/t-kdf.c +++ b/tests/t-kdf.c @@ -1243,6 +1243,192 @@ check_scrypt (void) } +#ifdef HAVE_PTHREAD +#include <pthread.h> + +struct per_thread_data_head { + gcry_kdf_hd_t h; + void *user_data; + gpg_err_code_t ec; +}; + +static void * +start_thread (void *arg) +{ + struct per_thread_data_head *p = arg; + + p->ec = gcry_kdf_compute_row (p->h, arg); + pthread_exit (NULL); + return NULL; +} + +#define MAX_THREAD 8 +#endif + +static gcry_error_t +my_kdf_derive (int parallel, + int algo, int subalgo, + const unsigned long *params, unsigned int paramslen, + const unsigned char *pass, size_t passlen, + const unsigned char *salt, size_t saltlen, + const unsigned char *key, size_t keylen, + const unsigned char *ad, size_t adlen, + size_t outlen, unsigned char *out) +{ + gcry_error_t err; + gcry_kdf_hd_t hd; +#ifdef HAVE_PTHREAD + pthread_attr_t attr; + pthread_t thr[MAX_THREAD]; + int i; +#endif + + 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) + { + memset (thr, 0, sizeof (thr)); + + if (pthread_attr_init (&attr)) + { + gcry_kdf_close (hd); + return gpg_error_from_syserror (); + } + + if (pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE)) + { + pthread_attr_destroy (&attr); + gcry_kdf_close (hd); + return gpg_error_from_syserror (); + } + } +#endif + + i = 0; + while (1) + { + int action; + void *arg; + + err = gcry_kdf_iterator (hd, &action, &arg); + if (err) + break; + + if (action == 0) + /* DONE */ + break; + else if (action == 1) + { /* request to ask creating a thread */ +#ifdef HAVE_PTHREAD + struct per_thread_data_head *p = arg; + + if (parallel) + { + pthread_create (&thr[i], &attr, start_thread, arg); + p->user_data = &thr[i]; + i++; + } + else + { + gcry_kdf_compute_row (p->h, arg); + p->user_data = NULL; + } +#else + gcry_kdf_compute_row (p->h, arg); + p->user_data = NULL; +#endif + } + else if (action == 2) + { /* request to ask joining a thread */ +#ifdef HAVE_PTHREAD + if (parallel) + { + struct per_thread_data_head *p = arg; + pthread_t *user_data = (pthread_t *)p->user_data; + + pthread_join (*user_data, NULL); + memset (user_data, 0, sizeof (pthread_t)); + --i; + } +#endif + } + } + +#ifdef HAVE_PTHREAD + if (parallel) + pthread_attr_destroy (&attr); +#endif + + if (!err) + err = gcry_kdf_final (hd, outlen, out); + + gcry_kdf_close (hd); + return err; +} + + +static void +check_argon2 (void) +{ + gcry_error_t err; + const unsigned long param[5] = { 32, 3, 16, 4, 4 }; + const unsigned char pass[32] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + const unsigned char salt[16] = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + }; + const unsigned char key[8] = { 3, 3, 3, 3, 3, 3, 3, 3 }; + const unsigned char ad[12] = { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }; + unsigned char out[32]; + unsigned char expected[32] = { + 0xf8, 0x7c, 0x95, 0x96, 0xbd, 0xbf, 0x75, 0x0b, + 0xfb, 0x35, 0x3a, 0x89, 0x70, 0xe5, 0x44, 0x1a, + 0x70, 0x24, 0x3e, 0xb4, 0x90, 0x30, 0xdf, 0xe2, + 0x74, 0xd9, 0xad, 0x4e, 0x37, 0x0e, 0x38, 0x9b + }; + int i; + + err = my_kdf_derive (0, + GCRY_KDF_ARGON2, GCRY_KDF_ARGON2ID, param, 4, + pass, 32, salt, 16, key, 8, ad, 12, + 32, out); + if (err) + fail ("argon2 test %d failed: %s\n", 0, gpg_strerror (err)); + else if (memcmp (out, expected, 32)) + { + fail ("argon2 test %d failed: mismatch\n", 0); + fputs ("got:", stderr); + for (i=0; i < 32; i++) + fprintf (stderr, " %02x", out[i]); + putc ('\n', stderr); + } + +#ifdef HAVE_PTHREAD + err = my_kdf_derive (1, + GCRY_KDF_ARGON2, GCRY_KDF_ARGON2ID, param, 5, + pass, 32, salt, 16, key, 8, ad, 12, + 32, out); + if (err) + fail ("argon2 test %d failed: %s\n", 1, gpg_strerror (err)); + else if (memcmp (out, expected, 32)) + { + fail ("argon2 test %d failed: mismatch\n", 1); + fputs ("got:", stderr); + for (i=0; i < 32; i++) + fprintf (stderr, " %02x", out[i]); + putc ('\n', stderr); + } +#endif +} + + int main (int argc, char **argv) { @@ -1319,6 +1505,9 @@ main (int argc, char **argv) check_openpgp (); check_pbkdf2 (); check_scrypt (); +#if 0 + check_argon2 (); +#endif } return error_count ? 1 : 0; |