From 8fee6c1ce6d116fe7909dbe1184d95bc91305484 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 7 May 2014 13:16:32 +0200 Subject: gpg: Finish experimental support for Ed25519. * agent/cvt-openpgp.c (try_do_unprotect_arg_s): Add field "curve". (get_keygrip): Add and use arg CURVE. (convert_secret_key): Ditto. (convert_transfer_key): Ditto. (get_npkey_nskey): New. (prepare_unprotect): Replace gcrypt functions by get_npkey_nskey. Allow opaque MPIs. (do_unprotect): Use CURVE instead of parameters. (convert_from_openpgp_main): Ditto. (convert_to_openpgp): Simplify. * g10/import.c (one_mpi_from_pkey): Remove. (transfer_secret_keys): Rewrite to use the curve instead of the parameters. * g10/parse-packet.c (parse_key): Mark protected MPIs with USER1 flag. * common/openpgp-oid.c (openpgp_curve_to_oid): Allow the use of "NIST P-256" et al. * g10/keygen.c (ask_curve): Add arg ALGO. (generate_keypair): Rewrite the ECC key logic. * tests/openpgp/ecc.test: Provide the "ecc" passphrase. --- g10/import.c | 140 ++++++++++++----------------------------------------- g10/keygen.c | 116 +++++++++++++++++++++++++++++++------------- g10/parse-packet.c | 7 +++ 3 files changed, 122 insertions(+), 141 deletions(-) (limited to 'g10') 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; - - /* 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; - } - } + j = 0; + /* Append the public key element Q. */ + put_membuf_str (&mbuf, " _ %m"); + format_args[j++] = pk->pkey + 1; + + /* 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); } -- cgit v1.2.3