aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNIIBE Yutaka <[email protected]>2022-02-21 05:23:43 +0000
committerNIIBE Yutaka <[email protected]>2022-02-21 05:23:43 +0000
commita8a65240fb6eb678c1c7ebe5186f7da2b04a751e (patch)
treea520b13b98a6ea9fad0e2d8f37ca938d4277a0cc
parentaccept Argon2 as S2K specifier. (diff)
downloadgnupg-a8a65240fb6eb678c1c7ebe5186f7da2b04a751e.tar.gz
gnupg-a8a65240fb6eb678c1c7ebe5186f7da2b04a751e.zip
experiment with Argon2id.
Signed-off-by: NIIBE Yutaka <[email protected]>
-rw-r--r--g10/dek.h2
-rw-r--r--g10/mainproc.c2
-rw-r--r--g10/packet.h1
-rw-r--r--g10/parse-packet.c10
-rw-r--r--g10/passphrase.c230
5 files changed, 213 insertions, 32 deletions
diff --git a/g10/dek.h b/g10/dek.h
index 88f8bc5f7..e21ed0734 100644
--- a/g10/dek.h
+++ b/g10/dek.h
@@ -45,7 +45,7 @@ typedef struct
byte key[32];
/* The cacheid for the S2K. */
- char s2k_cacheid[1+16+1];
+ char s2k_cacheid[1+32+1];
} DEK;
diff --git a/g10/mainproc.c b/g10/mainproc.c
index dfc582a17..082846812 100644
--- a/g10/mainproc.c
+++ b/g10/mainproc.c
@@ -388,7 +388,7 @@ proc_symkey_enc (CTX c, PACKET *pkt)
s = NULL; /* Force a goto leave. */
}
- if (openpgp_md_test_algo (enc->s2k.u.s.hash_algo))
+ if (enc->s2k.mode != 4 && openpgp_md_test_algo (enc->s2k.u.s.hash_algo))
{
log_error(_("passphrase generated with unknown digest"
" algorithm %d\n"),enc->s2k.u.s.hash_algo);
diff --git a/g10/packet.h b/g10/packet.h
index 6cbe22ec7..8f69a247a 100644
--- a/g10/packet.h
+++ b/g10/packet.h
@@ -94,7 +94,6 @@ typedef struct {
} S2K_openpgp;
typedef struct {
- byte hash_algo;
byte t;
byte m;
byte p;
diff --git a/g10/parse-packet.c b/g10/parse-packet.c
index ec2cfe3c2..7e1c8f959 100644
--- a/g10/parse-packet.c
+++ b/g10/parse-packet.c
@@ -1287,10 +1287,12 @@ parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
{
for (i = 0; i < 16 && pktlen; i++, pktlen--)
k->s2k.u.a.salt[i] = iobuf_get_noeof (inp);
- k->s2k.u.a.t = iobuf_get_noeof (inp);;
- k->s2k.u.a.m = iobuf_get_noeof (inp);;
- k->s2k.u.a.p = iobuf_get_noeof (inp);;
- pktlen -=3;
+ k->s2k.u.a.t = iobuf_get_noeof (inp);
+ pktlen--;
+ k->s2k.u.a.p = iobuf_get_noeof (inp);
+ pktlen--;
+ k->s2k.u.a.m = iobuf_get_noeof (inp);
+ pktlen--;
}
else
k->s2k.u.s.hash_algo = hash_algo;
diff --git a/g10/passphrase.c b/g10/passphrase.c
index cb951424d..c7d31324c 100644
--- a/g10/passphrase.c
+++ b/g10/passphrase.c
@@ -266,6 +266,153 @@ passphrase_clear_cache (const char *cacheid)
log_error (_("problem with the agent: %s\n"), gpg_strerror (rc));
}
+#define HAVE_PTHREAD 1
+#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)
+{
+ 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;
+}
+
+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)
+ {
+ /* 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;
+}
+#endif
+
+static gpg_error_t
+gnupg_kdf_derive (int algo, int subalgo,
+ const unsigned long *param, unsigned int paramlen,
+ 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)
+{
+ gpg_error_t err;
+ gcry_kdf_hd_t hd;
+
+ err = gcry_kdf_open (&hd, algo, subalgo, param, paramlen,
+ pass, passlen, salt, saltlen, key, keylen,
+ ad, adlen);
+ if (err)
+ return err;
+
+ if (HAVE_PTHREAD)
+ {
+ 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
+ err = gcry_kdf_compute (hd, NULL);
+
+ if (!err)
+ err = gcry_kdf_final (hd, outlen, out);
+
+ gcry_kdf_close (hd);
+ return err;
+}
/* Return a new DEK object using the string-to-key specifier S2K.
* Returns NULL if the user canceled the passphrase entry and if
@@ -286,7 +433,7 @@ passphrase_to_dek (int cipher_algo, STRING2KEY *s2k,
DEK *dek;
STRING2KEY help_s2k;
int dummy_canceled;
- char s2k_cacheidbuf[1+16+1];
+ char s2k_cacheidbuf[1+32+1];
char *s2k_cacheid = NULL;
if (!canceled)
@@ -308,19 +455,24 @@ passphrase_to_dek (int cipher_algo, STRING2KEY *s2k,
/* Create a new salt or what else to be filled into the s2k for a
new key. */
- if (create && (s2k->mode == 1 || s2k->mode == 3))
+ if (create)
{
- gcry_randomize (s2k->u.s.salt, 8, GCRY_STRONG_RANDOM);
- if ( s2k->mode == 3 )
+ if (s2k->mode == 1 || s2k->mode == 3)
{
- /* We delay the encoding until it is really needed. This is
- if we are going to dynamically calibrate it, we need to
- call out to gpg-agent and that should not be done during
- option processing in main(). */
- if (!opt.s2k_count)
- opt.s2k_count = encode_s2k_iterations (agent_get_s2k_count ());
- s2k->u.s.count = opt.s2k_count;
+ gcry_randomize (s2k->u.s.salt, 8, GCRY_STRONG_RANDOM);
+ if ( s2k->mode == 3 )
+ {
+ /* We delay the encoding until it is really needed. This is
+ if we are going to dynamically calibrate it, we need to
+ call out to gpg-agent and that should not be done during
+ option processing in main(). */
+ if (!opt.s2k_count)
+ opt.s2k_count = encode_s2k_iterations (agent_get_s2k_count ());
+ s2k->u.s.count = opt.s2k_count;
+ }
}
+ else if (s2k->mode == 4)
+ gcry_randomize (s2k->u.a.salt, 16, GCRY_STRONG_RANDOM);
}
/* If we do not have a passphrase available in NEXT_PW and status
@@ -348,13 +500,23 @@ passphrase_to_dek (int cipher_algo, STRING2KEY *s2k,
}
else
{
- if (!nocache && (s2k->mode == 1 || s2k->mode == 3))
- {
- memset (s2k_cacheidbuf, 0, sizeof s2k_cacheidbuf);
- *s2k_cacheidbuf = 'S';
- bin2hex (s2k->u.s.salt, 8, s2k_cacheidbuf + 1);
- s2k_cacheid = s2k_cacheidbuf;
- }
+ if (!nocache)
+ {
+ if (s2k->mode == 1 || s2k->mode == 3)
+ {
+ memset (s2k_cacheidbuf, 0, sizeof s2k_cacheidbuf);
+ *s2k_cacheidbuf = 'S';
+ bin2hex (s2k->u.s.salt, 8, s2k_cacheidbuf + 1);
+ s2k_cacheid = s2k_cacheidbuf;
+ }
+ else if (s2k->mode == 4)
+ {
+ memset (s2k_cacheidbuf, 0, sizeof s2k_cacheidbuf);
+ *s2k_cacheidbuf = 'S';
+ bin2hex (s2k->u.a.salt, 16, s2k_cacheidbuf + 1);
+ s2k_cacheid = s2k_cacheidbuf;
+ }
+ }
if (opt.pinentry_mode == PINENTRY_MODE_LOOPBACK)
{
@@ -393,13 +555,31 @@ passphrase_to_dek (int cipher_algo, STRING2KEY *s2k,
dek->keylen = openpgp_cipher_get_algo_keylen (dek->algo);
if (!(dek->keylen > 0 && dek->keylen <= DIM(dek->key)))
BUG ();
- err = gcry_kdf_derive (pw, strlen (pw),
- s2k->mode == 3? GCRY_KDF_ITERSALTED_S2K :
- s2k->mode == 1? GCRY_KDF_SALTED_S2K :
- /* */ GCRY_KDF_SIMPLE_S2K,
- s2k->u.s.hash_algo, s2k->u.s.salt, 8,
- S2K_DECODE_COUNT(s2k->u.s.count),
- dek->keylen, dek->key);
+ if (s2k->mode == 4)
+ {
+ unsigned long param[4];
+ unsigned char ad[4];
+
+ param[0] = dek->keylen + 1;
+ param[1] = s2k->u.a.t;
+ param[2] = (1UL << ((s2k->u.a.m & 0x1f) - 10));
+ param[3] = s2k->u.a.p;
+ ad[0] = 0xc3;
+ ad[1] = 0x04;
+ ad[2] = dek->algo;
+ err = gnupg_kdf_derive (GCRY_KDF_ARGON2, GCRY_KDF_ARGON2ID,
+ param, 4, pw, strlen (pw),
+ s2k->u.a.salt, 16, NULL, 0, ad, 3,
+ dek->keylen + 1, dek->key);
+ }
+ else
+ err = gcry_kdf_derive (pw, strlen (pw),
+ s2k->mode == 3? GCRY_KDF_ITERSALTED_S2K :
+ s2k->mode == 1? GCRY_KDF_SALTED_S2K :
+ /* */ GCRY_KDF_SIMPLE_S2K,
+ s2k->u.s.hash_algo, s2k->u.s.salt, 8,
+ S2K_DECODE_COUNT(s2k->u.s.count),
+ dek->keylen, dek->key);
if (err)
{
log_error ("gcry_kdf_derive failed: %s", gpg_strerror (err));