summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNIIBE Yutaka <gniibe@fsij.org>2022-01-26 00:22:50 +0900
committerNIIBE Yutaka <gniibe@fsij.org>2022-01-26 00:22:50 +0900
commitbafdb90d97b65db541ea917088ca956e6a364f6b (patch)
treeb2041913245036b27a31212f292f3c6d0e94851b
parent3d353782d84b9720262d7b05adfae3aef7ff843b (diff)
downloadlibgcrypt-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.c679
-rw-r--r--src/gcrypt-int.h19
-rw-r--r--src/gcrypt.h.in31
-rw-r--r--src/libgcrypt.def7
-rw-r--r--src/libgcrypt.vers3
-rw-r--r--src/visibility.c55
-rw-r--r--src/visibility.h12
-rw-r--r--tests/Makefile.am2
-rw-r--r--tests/t-kdf.c189
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;