diff --git a/agent/cvt-openpgp.c b/agent/cvt-openpgp.c index ef34463d8..28f0380e4 100644 --- a/agent/cvt-openpgp.c +++ b/agent/cvt-openpgp.c @@ -36,6 +36,7 @@ struct try_do_unprotect_arg_s int is_v4; int is_protected; int pubkey_algo; + const char *curve; int protect_algo; char *iv; int ivlen; @@ -54,7 +55,8 @@ struct try_do_unprotect_arg_s /* Compute the keygrip from the public key and store it at GRIP. */ static gpg_error_t -get_keygrip (int pubkey_algo, gcry_mpi_t *pkey, unsigned char *grip) +get_keygrip (int pubkey_algo, const char *curve, gcry_mpi_t *pkey, + unsigned char *grip) { gpg_error_t err; gcry_sexp_t s_pkey = NULL; @@ -80,9 +82,8 @@ get_keygrip (int pubkey_algo, gcry_mpi_t *pkey, unsigned char *grip) case GCRY_PK_ECC: err = gcry_sexp_build (&s_pkey, NULL, - "(public-key(ecc(p%m)(a%m)(b%m)(g%m)(n%m)(q%m)))", - pkey[0], pkey[1], pkey[2], pkey[3], pkey[4], - pkey[5]); + "(public-key(ecc(curve %s)(q%m)))", + curve, pkey[0]); break; default: @@ -102,7 +103,8 @@ get_keygrip (int pubkey_algo, gcry_mpi_t *pkey, unsigned char *grip) parameters into our s-expression based format. Note that PUBKEY_ALGO has an gcrypt algorithm number. */ static gpg_error_t -convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey) +convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey, + const char *curve) { gpg_error_t err; gcry_sexp_t s_skey = NULL; @@ -135,11 +137,12 @@ convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey) break; case GCRY_PK_ECC: - err = gcry_sexp_build (&s_skey, NULL, - "(private-key(ecc(p%m)(a%m)(b%m)(g%m)(n%m)(q%m)" - "(d%m)))", - skey[0], skey[1], skey[2], skey[3], skey[4], - skey[5], skey[6]); + if (!curve) + err = gpg_error (GPG_ERR_BAD_SECKEY); + else + err = gcry_sexp_build (&s_skey, NULL, + "(private-key(ecc(curve%s)(q%m)(d%m)))", + curve, skey[0], skey[1]); break; default: @@ -160,7 +163,7 @@ convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey) mode. Note that PUBKEY_ALGO has an gcrypt algorithm number. */ static gpg_error_t convert_transfer_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey, - gcry_sexp_t transfer_key) + const char *curve, gcry_sexp_t transfer_key) { gpg_error_t err; gcry_sexp_t s_skey = NULL; @@ -197,9 +200,9 @@ convert_transfer_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey, case GCRY_PK_ECC: err = gcry_sexp_build (&s_skey, NULL, - "(protected-private-key(ecc(p%m)(a%m)(b%m)(g%m)(n%m)(q%m)" + "(protected-private-key(ecc(curve%s)(q%m)" "(protected openpgp-native%S)))", - skey[0], skey[1], skey[2], skey[3], skey[4], skey[5], transfer_key); + curve, skey[0], transfer_key); break; default: @@ -254,6 +257,22 @@ checksum (const unsigned char *p, unsigned int n) } +/* Return the number of expected key parameters. */ +static void +get_npkey_nskey (int pubkey_algo, size_t *npkey, size_t *nskey) +{ + switch (pubkey_algo) + { + case GCRY_PK_RSA: *npkey = 2; *nskey = 6; break; + case GCRY_PK_ELG: *npkey = 3; *nskey = 4; break; + case GCRY_PK_ELG_E: *npkey = 3; *nskey = 4; break; + case GCRY_PK_DSA: *npkey = 4; *nskey = 5; break; + case GCRY_PK_ECC: *npkey = 1; *nskey = 2; break; + default: *npkey = 0; *nskey = 0; break; + } +} + + /* Helper for do_unprotect. PUBKEY_ALOGO is the gcrypt algo number. On success R_NPKEY and R_NSKEY receive the number or parameters for the algorithm PUBKEY_ALGO and R_SKEYLEN the used length of @@ -264,7 +283,6 @@ prepare_unprotect (int pubkey_algo, gcry_mpi_t *skey, size_t skeysize, unsigned int *r_npkey, unsigned int *r_nskey, unsigned int *r_skeylen) { - gpg_error_t err; size_t npkey, nskey, skeylen; int i; @@ -293,12 +311,8 @@ prepare_unprotect (int pubkey_algo, gcry_mpi_t *skey, size_t skeysize, /* Get properties of the public key algorithm and do some consistency checks. Note that we need at least NPKEY+1 elements in the SKEY array. */ - if ( (err = gcry_pk_algo_info (pubkey_algo, GCRYCTL_GET_ALGO_NPKEY, - NULL, &npkey)) - || (err = gcry_pk_algo_info (pubkey_algo, GCRYCTL_GET_ALGO_NSKEY, - NULL, &nskey))) - return err; - if (!npkey || npkey >= nskey) + get_npkey_nskey (pubkey_algo, &npkey, &nskey); + if (!npkey || !nskey || npkey >= nskey) return gpg_error (GPG_ERR_INTERNAL); if (skeylen <= npkey) return gpg_error (GPG_ERR_MISSING_VALUE); @@ -309,7 +323,7 @@ prepare_unprotect (int pubkey_algo, gcry_mpi_t *skey, size_t skeysize, encrypted. */ for (i=0; i < npkey; i++) { - if (!skey[i] || gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_OPAQUE)) + if (!skey[i] || gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_USER1)) return gpg_error (GPG_ERR_BAD_SECKEY); } @@ -329,7 +343,7 @@ prepare_unprotect (int pubkey_algo, gcry_mpi_t *skey, size_t skeysize, static int do_unprotect (const char *passphrase, int pkt_version, int pubkey_algo, int is_protected, - gcry_mpi_t *skey, size_t skeysize, + const char *curve, gcry_mpi_t *skey, size_t skeysize, int protect_algo, void *protect_iv, size_t protect_ivlen, int s2k_mode, int s2k_algo, byte *s2k_salt, u32 s2k_count, u16 desired_csum, gcry_sexp_t *r_key) @@ -353,23 +367,26 @@ do_unprotect (const char *passphrase, merely verify the checksum. */ if (!is_protected) { - unsigned char *buffer; - actual_csum = 0; for (i=npkey; i < nskey; i++) { - if (!skey[i] || gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_OPAQUE)) + if (!skey[i] || gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_USER1)) return gpg_error (GPG_ERR_BAD_SECKEY); - err = gcry_mpi_print (GCRYMPI_FMT_PGP, NULL, 0, &nbytes, skey[i]); - if (!err) + if (gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_USER1)) { - buffer = (gcry_is_secure (skey[i])? - xtrymalloc_secure (nbytes) : xtrymalloc (nbytes)); - if (!buffer) - return gpg_error_from_syserror (); - err = gcry_mpi_print (GCRYMPI_FMT_PGP, buffer, nbytes, - NULL, skey[i]); + unsigned int nbits; + const unsigned char *buffer; + buffer = gcry_mpi_get_opaque (skey[i], &nbits); + nbytes = (nbits+7)/8; + actual_csum += checksum (buffer, nbytes); + } + else + { + unsigned char *buffer; + + err = gcry_mpi_aprint (GCRYMPI_FMT_PGP, &buffer, &nbytes, + skey[i]); if (!err) actual_csum += checksum (buffer, nbytes); xfree (buffer); @@ -428,7 +445,8 @@ do_unprotect (const char *passphrase, { int ndata; unsigned int ndatabits; - unsigned char *p, *data; + const unsigned char *p; + unsigned char *data; u16 csum_pgp7 = 0; if (!gcry_mpi_get_flag (skey[npkey], GCRYMPI_FLAG_OPAQUE )) @@ -527,7 +545,7 @@ do_unprotect (const char *passphrase, for (i = npkey; i < nskey; i++) { - unsigned char *p; + const unsigned char *p; size_t ndata; unsigned int ndatabits; @@ -580,7 +598,7 @@ do_unprotect (const char *passphrase, if (nskey != skeylen) err = gpg_error (GPG_ERR_BAD_SECKEY); else - err = convert_secret_key (r_key, pubkey_algo, skey); + err = convert_secret_key (r_key, pubkey_algo, skey, curve); if (err) return err; @@ -608,6 +626,7 @@ try_do_unprotect_cb (struct pin_entry_info_s *pi) err = do_unprotect (pi->pin, arg->is_v4? 4:3, arg->pubkey_algo, arg->is_protected, + arg->curve, arg->skey, arg->skeysize, arg->protect_algo, arg->iv, arg->ivlen, arg->s2k_mode, arg->s2k_algo, @@ -651,6 +670,7 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp, u32 s2k_count = 0; size_t npkey, nskey; gcry_mpi_t skey[10]; /* We support up to 9 parameters. */ + char *curve = NULL; u16 desired_csum; int skeyidx = 0; gcry_sexp_t s_skey = NULL; @@ -695,8 +715,6 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp, if (!string) goto bad_seckey; protect_algo = gcry_cipher_map_name (string); - if (!protect_algo && !!strcmp (string, "IDEA")) - protect_algo = GCRY_CIPHER_IDEA; xfree (string); value = gcry_sexp_nth_data (list, 3, &valuelen); @@ -739,11 +757,21 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp, pubkey_algo = gcry_pk_map_name (string); xfree (string); - if (gcry_pk_algo_info (pubkey_algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &npkey) - || gcry_pk_algo_info (pubkey_algo, GCRYCTL_GET_ALGO_NSKEY, NULL, &nskey) - || !npkey || npkey >= nskey) + get_npkey_nskey (pubkey_algo, &npkey, &nskey); + if (!npkey || !nskey || npkey >= nskey) goto bad_seckey; + if (npkey == 1) /* This is ECC */ + { + gcry_sexp_release (list); + list = gcry_sexp_find_token (top_list, "curve", 0); + if (!list) + goto bad_seckey; + curve = gcry_sexp_nth_string (list, 1); + if (!curve) + goto bad_seckey; + } + gcry_sexp_release (list); list = gcry_sexp_find_token (top_list, "skey", 0); if (!list) @@ -770,15 +798,15 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp, value = gcry_sexp_nth_data (list, ++idx, &valuelen); if (!value || !valuelen) goto bad_seckey; - if (is_enc) + if (is_enc || curve) { - void *p = xtrymalloc (valuelen); - if (!p) - goto outofmem; - memcpy (p, value, valuelen); - skey[skeyidx] = gcry_mpi_set_opaque (NULL, p, valuelen*8); + /* Encrypted parameters and ECC parameters need or can be + stored as opaque. */ + skey[skeyidx] = gcry_mpi_set_opaque_copy (NULL, value, valuelen*8); if (!skey[skeyidx]) goto outofmem; + if (is_enc) + gcry_mpi_set_flag (skey[skeyidx], GCRYMPI_FLAG_USER1); } else { @@ -807,33 +835,24 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp, gcry_sexp_release (list); list = NULL; gcry_sexp_release (top_list); top_list = NULL; - /* log_debug ("XXX is_v4=%d\n", is_v4); */ - /* log_debug ("XXX pubkey_algo=%d\n", pubkey_algo); */ - /* log_debug ("XXX is_protected=%d\n", is_protected); */ - /* log_debug ("XXX protect_algo=%d\n", protect_algo); */ - /* log_printhex ("XXX iv", iv, ivlen); */ - /* log_debug ("XXX ivlen=%d\n", ivlen); */ - /* log_debug ("XXX s2k_mode=%d\n", s2k_mode); */ - /* log_debug ("XXX s2k_algo=%d\n", s2k_algo); */ - /* log_printhex ("XXX s2k_salt", s2k_salt, sizeof s2k_salt); */ - /* log_debug ("XXX s2k_count=%lu\n", (unsigned long)s2k_count); */ - /* for (idx=0; skey[idx]; idx++) */ - /* { */ - /* int is_enc = gcry_mpi_get_flag (skey[idx], GCRYMPI_FLAG_OPAQUE); */ - /* log_info ("XXX skey[%d]%s:", idx, is_enc? " (enc)":""); */ - /* if (is_enc) */ - /* { */ - /* void *p; */ - /* unsigned int nbits; */ - /* p = gcry_mpi_get_opaque (skey[idx], &nbits); */ - /* log_printhex (NULL, p, (nbits+7)/8); */ - /* } */ - /* else */ - /* gcry_mpi_dump (skey[idx]); */ - /* log_printf ("\n"); */ - /* } */ +#if 0 + log_debug ("XXX is_v4=%d\n", is_v4); + log_debug ("XXX pubkey_algo=%d\n", pubkey_algo); + log_debug ("XXX is_protected=%d\n", is_protected); + log_debug ("XXX protect_algo=%d\n", protect_algo); + log_printhex ("XXX iv", iv, ivlen); + log_debug ("XXX ivlen=%d\n", ivlen); + log_debug ("XXX s2k_mode=%d\n", s2k_mode); + log_debug ("XXX s2k_algo=%d\n", s2k_algo); + log_printhex ("XXX s2k_salt", s2k_salt, sizeof s2k_salt); + log_debug ("XXX s2k_count=%lu\n", (unsigned long)s2k_count); + log_debug ("XXX curve='%s'\n", curve); + for (idx=0; skey[idx]; idx++) + gcry_log_debugmpi (gcry_mpi_get_flag (skey[idx], GCRYMPI_FLAG_USER1) + ? "skey(e)" : "skey(_)", skey[idx]); +#endif /*0*/ - err = get_keygrip (pubkey_algo, skey, grip); + err = get_keygrip (pubkey_algo, curve, skey, grip); if (err) goto leave; @@ -850,7 +869,7 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp, if (err) goto leave; - err = convert_transfer_key (&s_skey, pubkey_algo, skey, s_pgp); + err = convert_transfer_key (&s_skey, pubkey_algo, skey, curve, s_pgp); if (err) goto leave; } @@ -871,6 +890,7 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp, pi_arg.is_v4 = is_v4; pi_arg.is_protected = is_protected; pi_arg.pubkey_algo = pubkey_algo; + pi_arg.curve = curve; pi_arg.protect_algo = protect_algo; pi_arg.iv = iv; pi_arg.ivlen = ivlen; @@ -929,6 +949,7 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp, err = make_canon_sexp_pad (s_skey, 1, r_key, NULL); leave: + xfree (curve); gcry_sexp_release (s_skey); gcry_sexp_release (list); gcry_sexp_release (top_list); @@ -1223,11 +1244,9 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase, { char countbuf[35]; membuf_t mbuf; - void *format_args_buf_ptr[1]; - int format_args_buf_int[1]; void *format_args[10+2]; - unsigned int n; - gcry_sexp_t tmpkey, tmpsexp = NULL; + gcry_sexp_t tmpkey; + gcry_sexp_t tmpsexp = NULL; snprintf (countbuf, sizeof countbuf, "%lu", s2k_count); @@ -1238,11 +1257,8 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase, put_membuf_str (&mbuf, " _ %m"); format_args[j++] = array + i; } - put_membuf_str (&mbuf, " e %b"); - format_args_buf_ptr[0] = gcry_mpi_get_opaque (array[npkey], &n); - format_args_buf_int[0] = (n+7)/8; - format_args[j++] = format_args_buf_int; - format_args[j++] = format_args_buf_ptr; + put_membuf_str (&mbuf, " e %m"); + format_args[j++] = array + npkey; put_membuf_str (&mbuf, ")\n"); put_membuf (&mbuf, "", 1); diff --git a/agent/keyformat.txt b/agent/keyformat.txt index 3f95dae03..42c4b1f06 100644 --- a/agent/keyformat.txt +++ b/agent/keyformat.txt @@ -211,6 +211,7 @@ This format is used to transfer keys between gpg and gpg-agent. (openpgp-private-key (version V) (algo PUBKEYALGO) + (curve CURVENAME) (skey _ P1 _ P2 _ P3 ... e PN) (csum n) (protection PROTTYPE PROTALGO IV S2KMODE S2KHASH S2KSALT S2KCOUNT)) @@ -218,6 +219,7 @@ This format is used to transfer keys between gpg and gpg-agent. * V is the packet version number (3 or 4). * PUBKEYALGO is a Libgcrypt algo name +* CURVENAME is the name of the curve - only used with ECC. * P1 .. PN are the parameters; the public parameters are never encrypted the secrect key parameters are encrypted if the "protection" list is given. To make this more explicit each parameter is preceded by a diff --git a/common/openpgp-oid.c b/common/openpgp-oid.c index 94a2296b6..bcb9885bc 100644 --- a/common/openpgp-oid.c +++ b/common/openpgp-oid.c @@ -280,17 +280,20 @@ openpgp_curve_to_oid (const char *name, unsigned int *r_nbits) oidstr = "1.3.6.1.4.1.11591.15.1"; nbits = 255; } - else if (!strcmp (name, "nistp256")) + else if (!strcmp (name, "nistp256") || !strcmp (name, "NIST P-256")) { + /* Libgcrypt uses "NIST P-256" as standard name for this curve + and thus the key generation returns this value. Thus we + allow both strings. */ oidstr = "1.2.840.10045.3.1.7"; nbits = 256; } - else if (!strcmp (name, "nistp384")) + else if (!strcmp (name, "nistp384") || !strcmp (name, "NIST P-384")) { oidstr = "1.3.132.0.34"; nbits = 384; } - else if (!strcmp (name, "nistp521")) + else if (!strcmp (name, "nistp521") || !strcmp (name, "NIST P-521")) { oidstr = "1.3.132.0.35"; nbits = 521; diff --git a/g10/import.c b/g10/import.c index 8223041d1..2b219a28d 100644 --- a/g10/import.c +++ b/g10/import.c @@ -1128,37 +1128,6 @@ import_one (ctrl_t ctrl, } -/* Extract one MPI value from the S-expression PKEY which is expected - to hold a "public-key". Returns NULL on error. */ -static gcry_mpi_t -one_mpi_from_pkey (gcry_sexp_t pkey, const char *name, size_t namelen) -{ - gcry_sexp_t list, l2; - gcry_mpi_t a; - - list = gcry_sexp_find_token (pkey, "public-key", 0); - if (!list) - return NULL; - l2 = gcry_sexp_cadr (list); - gcry_sexp_release (list); - list = l2; - if (!list) - return NULL; - - l2 = gcry_sexp_find_token (list, name, namelen); - if (!l2) - { - gcry_sexp_release (list); - return NULL; - } - a = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); - gcry_sexp_release (l2); - gcry_sexp_release (list); - - return a; -} - - /* Transfer all the secret keys in SEC_KEYBLOCK to the gpg-agent. The function prints diagnostics and returns an error code. */ static gpg_error_t @@ -1174,18 +1143,15 @@ transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock) int nskey; membuf_t mbuf; int i, j; - unsigned int n; - void *format_args_buf_ptr[PUBKEY_MAX_NSKEY]; - int format_args_buf_int[PUBKEY_MAX_NSKEY]; void *format_args[2*PUBKEY_MAX_NSKEY]; gcry_sexp_t skey, prot, tmpsexp; + gcry_sexp_t curve = NULL; unsigned char *transferkey = NULL; size_t transferkeylen; gcry_cipher_hd_t cipherhd = NULL; unsigned char *wrappedkey = NULL; size_t wrappedkeylen; char *cache_nonce = NULL; - gcry_mpi_t ecc_params[5] = {NULL, NULL, NULL, NULL, NULL}; /* Get the current KEK. */ err = agent_keywrap_key (ctrl, 0, &kek, &keklen); @@ -1263,65 +1229,30 @@ transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock) || pk->pubkey_algo == PUBKEY_ALGO_EDDSA || pk->pubkey_algo == PUBKEY_ALGO_ECDH) { - /* We need special treatment for ECC algorithms. OpenPGP - stores only the curve name but the agent expects a full - key. This is so that we can keep all curve name - validation code out of gpg-agent. */ -#if PUBKEY_MAX_NSKEY < 7 -#error PUBKEY_MAX_NSKEY too low for ECC -#endif - char *curve = openpgp_oid_to_str (pk->pkey[0]); - if (!curve) + /* The ECC case. */ + char *curvestr = openpgp_oid_to_str (pk->pkey[0]); + if (!curvestr) err = gpg_error_from_syserror (); else { - gcry_sexp_t cparam = gcry_pk_get_param (GCRY_PK_ECC, curve); - - xfree (curve); - if (!cparam) - err = gpg_error (GPG_ERR_UNKNOWN_CURVE); - else + err = gcry_sexp_build (&curve, NULL, "(curve %s)", curvestr); + xfree (curvestr); + if (!err) { - const char *s; + j = 0; + /* Append the public key element Q. */ + put_membuf_str (&mbuf, " _ %m"); + format_args[j++] = pk->pkey + 1; - /* Append the curve parameters P, A, B, G and N. */ - for (i=j=0; !err && *(s = "pabgn"+i); i++) - { - ecc_params[i] = one_mpi_from_pkey (cparam, s, 1); - if (!ecc_params[i]) - err = gpg_error (GPG_ERR_INV_CURVE); - else - { - put_membuf_str (&mbuf, " _ %m"); - format_args[j++] = ecc_params+i; - } - } - gcry_sexp_release (cparam); - if (!err) - { - /* Append the public key element Q. */ - put_membuf_str (&mbuf, " _ %m"); - format_args[j++] = pk->pkey + 1; - - /* Append the secret key element D. Note that - for ECDH we need to skip PKEY[2] because this - holds the KEK which is not needed. */ - i = pk->pubkey_algo == PUBKEY_ALGO_ECDH? 3 : 2; - if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_OPAQUE)) - { - put_membuf_str (&mbuf, " e %b"); - format_args_buf_ptr[i] - = gcry_mpi_get_opaque (pk->pkey[i],&n); - format_args_buf_int[i] = (n+7)/8; - format_args[j++] = format_args_buf_int + i; - format_args[j++] = format_args_buf_ptr + i; - } - else - { - put_membuf_str (&mbuf, " _ %m"); - format_args[j++] = pk->pkey + i; - } - } + /* Append the secret key element D. For ECDH we + skip PKEY[2] because this holds the KEK which is + not needed by gpg-agent. */ + i = pk->pubkey_algo == PUBKEY_ALGO_ECDH? 3 : 2; + if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_USER1)) + put_membuf_str (&mbuf, " e %m"); + else + put_membuf_str (&mbuf, " _ %m"); + format_args[j++] = pk->pkey + i; } } } @@ -1331,23 +1262,16 @@ transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock) for (i=j=0; i < nskey; i++) { if (!pk->pkey[i]) - ; /* Protected keys only have NPKEY+1 elements. */ - else if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_OPAQUE)) - { - put_membuf_str (&mbuf, " e %b"); - format_args_buf_ptr[i] = gcry_mpi_get_opaque (pk->pkey[i],&n); - format_args_buf_int[i] = (n+7)/8; - format_args[j++] = format_args_buf_int + i; - format_args[j++] = format_args_buf_ptr + i; - } + continue; /* Protected keys only have NPKEY+1 elements. */ + + if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_USER1)) + put_membuf_str (&mbuf, " e %m"); else - { - put_membuf_str (&mbuf, " _ %m"); - format_args[j++] = pk->pkey + i; - } + put_membuf_str (&mbuf, " _ %m"); + format_args[j++] = pk->pkey + i; } } - put_membuf_str (&mbuf, ")\n"); + put_membuf_str (&mbuf, ")"); put_membuf (&mbuf, "", 1); if (err) xfree (get_membuf (&mbuf, NULL)); @@ -1398,12 +1322,13 @@ transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock) "(openpgp-private-key\n" " (version %d)\n" " (algo %s)\n" - " %S\n" + " %S%S\n" " (csum %d)\n" " %S)\n", pk->version, openpgp_pk_algo_name (pk->pubkey_algo), - skey, (int)(unsigned long)ski->csum, prot); + curve, skey, + (int)(unsigned long)ski->csum, prot); gcry_sexp_release (skey); gcry_sexp_release (prot); if (!err) @@ -1463,8 +1388,7 @@ transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock) } leave: - for (i=0; i < DIM (ecc_params); i++) - gcry_mpi_release (ecc_params[i]); + gcry_sexp_release (curve); xfree (cache_nonce); xfree (wrappedkey); xfree (transferkey); diff --git a/g10/keygen.c b/g10/keygen.c index f3052e492..314cf9bab 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -2086,29 +2086,30 @@ ask_keysize (int algo, unsigned int primary_keysize) } -/* Ask for the key size. ALGO is the algorithm. If PRIMARY_KEYSIZE - is not 0, the function asks for the size of the encryption - subkey. */ +/* Ask for the curve. ALGO is the selected algorithm which this + function may adjust. Returns a malloced string with the name of + the curve. */ static char * -ask_curve (void) +ask_curve (int *algo) { struct { const char *name; int available; int expert_only; + int fix_curve; const char *pretty_name; } curves[] = { #if GPG_USE_EDDSA - { "Ed25519", 0, 0, "Curve 25519" }, + { "Curve25519", 0, 0, 1, "Curve 25519" }, #endif #if GPG_USE_ECDSA || GPG_USE_ECDH - { "NIST P-256", 0, 1, }, - { "NIST P-384", 0, 0, }, - { "NIST P-521", 0, 1, }, - { "brainpoolP256r1", 0, 1, "Brainpool P-256" }, - { "brainpoolP384r1", 0, 1, "Brainpool P-384" }, - { "brainpoolP512r1", 0, 1, "Brainpool P-512" }, - { "secp256k1", 0, 1 }, + { "NIST P-256", 0, 1, 0, }, + { "NIST P-384", 0, 0, 0, }, + { "NIST P-521", 0, 1, 0, }, + { "brainpoolP256r1", 0, 1, 0, "Brainpool P-256" }, + { "brainpoolP384r1", 0, 1, 0, "Brainpool P-384" }, + { "brainpoolP512r1", 0, 1, 0, "Brainpool P-512" }, + { "secp256k1", 0, 1, 0 }, #endif }; int idx; @@ -2127,9 +2128,14 @@ ask_curve (void) if (!opt.expert && curves[idx].expert_only) continue; + /* FIXME: The strcmp below is a temporary hack during + development. It shall be removed as soon as we have proper + Curve25519 support in Libgcrypt. */ gcry_sexp_release (keyparms); rc = gcry_sexp_build (&keyparms, NULL, - "(public-key(ecc(curve %s)))", curves[idx].name); + "(public-key(ecc(curve %s)))", + (!strcmp (curves[idx].name, "Curve25519") + ? "Ed25519" : curves[idx].name)); if (rc) continue; if (!gcry_pk_get_curve (keyparms, 0, NULL)) @@ -2171,7 +2177,22 @@ ask_curve (void) tty_printf (_("Invalid selection.\n")); else { - result = xstrdup (curves[idx].name); + if (curves[idx].fix_curve) + log_info ("WARNING: Curve25519 is an experimental algorithm and" + " not yet specified by OpenPGP. The current" + " implementation may change with the next GnuPG release" + " and thus rendering the key unusable!\n"); + + /* If the user selected a signing algorithm and Curve25519 + we need to update the algo and and the curve name. */ + if ((*algo == PUBKEY_ALGO_ECDSA || *algo == PUBKEY_ALGO_EDDSA) + && curves[idx].fix_curve) + { + *algo = PUBKEY_ALGO_EDDSA; + result = xstrdup ("Ed25519"); + } + else + result = xstrdup (curves[idx].name); break; } } @@ -3459,16 +3480,16 @@ generate_keypair (ctrl_t ctrl, const char *fname, const char *card_serialno, { /* Create primary and subkey at once. */ both = 1; - r = xmalloc_clear( sizeof *r + 20 ); - r->key = pKEYTYPE; - sprintf( r->u.value, "%d", algo ); - r->next = para; - para = r; if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) { - curve = ask_curve (); + curve = ask_curve (&algo); + r = xmalloc_clear( sizeof *r + 20 ); + r->key = pKEYTYPE; + sprintf( r->u.value, "%d", algo); + r->next = para; + para = r; nbits = 0; r = xmalloc_clear (sizeof *r + strlen (curve)); r->key = pKEYCURVE; @@ -3478,6 +3499,11 @@ generate_keypair (ctrl_t ctrl, const char *fname, const char *card_serialno, } else { + r = xmalloc_clear( sizeof *r + 20 ); + r->key = pKEYTYPE; + sprintf( r->u.value, "%d", algo); + r->next = para; + para = r; nbits = ask_keysize (algo, 0); r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYLENGTH; @@ -3501,9 +3527,43 @@ generate_keypair (ctrl_t ctrl, const char *fname, const char *card_serialno, strcpy( r->u.value, "encrypt" ); r->next = para; para = r; + + if (algo == PUBKEY_ALGO_ECDSA + || algo == PUBKEY_ALGO_EDDSA + || algo == PUBKEY_ALGO_ECDH) + { + if (algo == PUBKEY_ALGO_EDDSA + && subkey_algo == PUBKEY_ALGO_ECDH) + { + /* Need to switch to a different curve for the + encryption key. */ + xfree (curve); + curve = xstrdup ("Curve25519"); + } + r = xmalloc_clear (sizeof *r + strlen (curve)); + r->key = pSUBKEYCURVE; + strcpy (r->u.value, curve); + r->next = para; + para = r; + } } - else + else /* Create only a single key. */ { + /* For ECC we need to ask for the curve before storing the + algo becuase ask_curve may change the algo. */ + if (algo == PUBKEY_ALGO_ECDSA + || algo == PUBKEY_ALGO_EDDSA + || algo == PUBKEY_ALGO_ECDH) + { + curve = ask_curve (&algo); + nbits = 0; + r = xmalloc_clear (sizeof *r + strlen (curve)); + r->key = pKEYCURVE; + strcpy (r->u.value, curve); + r->next = para; + para = r; + } + r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYTYPE; sprintf( r->u.value, "%d", algo ); @@ -3528,13 +3588,7 @@ generate_keypair (ctrl_t ctrl, const char *fname, const char *card_serialno, || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) { - if (!both) - curve = ask_curve (); - r = xmalloc_clear (sizeof *r + strlen (curve)); - r->key = both? pSUBKEYCURVE : pKEYCURVE; - strcpy (r->u.value, curve); - r->next = para; - para = r; + /* The curve has already been set. */ } else { @@ -4031,11 +4085,7 @@ generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock) else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) - { - curve = ask_curve (); - if (curve && !strcmp (curve, "Ed25519")) - algo = PUBKEY_ALGO_EDDSA; - } + curve = ask_curve (&algo); else nbits = ask_keysize (algo, 0); diff --git a/g10/parse-packet.c b/g10/parse-packet.c index f70878846..424b052b9 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -2240,6 +2240,11 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, pk->pkey[npkey] = gcry_mpi_set_opaque (NULL, read_rest (inp, pktlen), pktlen * 8); + /* Mark that MPI as protected - we need this information for + importing a key. The OPAQUE flag can't be used because + we also store public EdDSA values in opaque MPIs. */ + if (pk->pkey[npkey]) + gcry_mpi_set_flag (pk->pkey[npkey], GCRYMPI_FLAG_USER1); pktlen = 0; if (list_mode) es_fprintf (listfp, "\tskey[%d]: [v4 protected]\n", npkey); @@ -2252,6 +2257,8 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, if (ski->is_protected) { pk->pkey[i] = read_protected_v3_mpi (inp, &pktlen); + if (pk->pkey[i]) + gcry_mpi_set_flag (pk->pkey[i], GCRYMPI_FLAG_USER1); if (list_mode) es_fprintf (listfp, "\tskey[%d]: [v3 protected]\n", i); } diff --git a/tests/openpgp/ecc.test b/tests/openpgp/ecc.test index 062a1ae0f..cb0446728 100755 --- a/tests/openpgp/ecc.test +++ b/tests/openpgp/ecc.test @@ -188,7 +188,7 @@ echo 'This is one line' >z for msg in $tests; do info "checking: $msg" eval "(IFS=; echo \"\$$msg\")" >x - $GPG -o y --yes x || error "decryption of $msg failed" + PINENTRY_USER_DATA=ecc $GPG -o y --yes x || error "decryption of $msg failed" cmp y z || error "$msg: mismatch" done @@ -204,7 +204,7 @@ for i in $plain_files $data_files ; do for k in $mainkeyids ; do info "file: $i key: $k" $GPG ${opt_always} -e -o x --yes -r $k $i - $GPG -o y --yes x + PINENTRY_USER_DATA=ecc $GPG -o y --yes x cmp $i y || error "$i,$k: mismatch" done done @@ -217,7 +217,7 @@ info "Checking ECC signing and verifiction." for i in $plain_files $data_files ; do for k in $mainkeyids ; do info "file: $i key: $k" - $GPG -s -o x --yes -u $k $i + PINENTRY_USER_DATA=ecc $GPG -s -o x --yes -u $k $i $GPG -o y --yes x || error "verify of $i,$k failed" cmp $i y || error "$i,$k: mismatch" done