diff --git a/agent/cvt-openpgp.c b/agent/cvt-openpgp.c index 562179b26..39ccba290 100644 --- a/agent/cvt-openpgp.c +++ b/agent/cvt-openpgp.c @@ -83,14 +83,25 @@ get_keygrip (int pubkey_algo, const char *curve, gcry_mpi_t *pkey, case GCRY_PK_ECC: if (!curve) err = gpg_error (GPG_ERR_BAD_SECKEY); - else if (!strcmp (curve, openpgp_curve_to_oid ("Ed25519", NULL))) - err = gcry_sexp_build (&s_pkey, NULL, - "(public-key(ecc(curve %s)(flags eddsa)(q%m)))", - "Ed25519", pkey[0]); else - err = gcry_sexp_build (&s_pkey, NULL, - "(public-key(ecc(curve %s)(q%m)))", - curve, pkey[0]); + { + const char *format; + + if (!strcmp (curve, openpgp_curve_to_oid ("Ed25519", NULL))) + { + format = "(public-key(ecc(curve %s)(flags eddsa)(q%m)))"; + curve = "Ed25519"; + } + else if (!strcmp (curve, openpgp_curve_to_oid ("Curve25519", NULL))) + { + format = "(public-key(ecc(curve %s)(flags djb-tweak)(q%m)))"; + curve = "Curve25519"; + } + else + format = "(public-key(ecc(curve %s)(q%m)))"; + + err = gcry_sexp_build (&s_pkey, NULL, format, curve, pkey[0]); + } break; default: @@ -146,19 +157,27 @@ convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey, case GCRY_PK_ECC: if (!curve) err = gpg_error (GPG_ERR_BAD_SECKEY); - else if (!strcmp (curve, openpgp_curve_to_oid ("Ed25519", NULL))) - { - /* Do not store the OID as name but the real name and the - EdDSA flag. */ - err = gcry_sexp_build (&s_skey, NULL, - "(private-key(ecc(curve%s)(flags eddsa)" - "(q%m)(d%m)))", - "Ed25519", skey[0], skey[1]); - } else - err = gcry_sexp_build (&s_skey, NULL, - "(private-key(ecc(curve%s)(q%m)(d%m)))", - curve, skey[0], skey[1]); + { + const char *format; + + if (!strcmp (curve, openpgp_curve_to_oid ("Ed25519", NULL))) + { + /* Do not store the OID as name but the real name and the + EdDSA flag. */ + format = "(private-key(ecc(curve %s)(flags eddsa)(q%m)(d%m)))"; + curve = "Ed25519"; + } + else if (!strcmp (curve, openpgp_curve_to_oid ("Curve25519", NULL))) + { + format = "(private-key(ecc(curve %s)(flags djb-tweak)(q%m)(d%m)))"; + curve = "Curve25519"; + } + else + format = "(private-key(ecc(curve %s)(q%m)(d%m)))"; + + err = gcry_sexp_build (&s_skey, NULL, format, curve, skey[0], skey[1]); + } break; default: @@ -216,22 +235,30 @@ convert_transfer_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey, case GCRY_PK_ECC: if (!curve) err = gpg_error (GPG_ERR_BAD_SECKEY); - else if (!strcmp (curve, openpgp_curve_to_oid ("Ed25519", NULL))) - { - /* Do not store the OID as name but the real name and the - EdDSA flag. */ - err = gcry_sexp_build - (&s_skey, NULL, - "(protected-private-key(ecc(curve%s)(flags eddsa)(q%m)" - "(protected openpgp-native%S)))", - "Ed25519", skey[0], transfer_key); - } else - err = gcry_sexp_build - (&s_skey, NULL, - "(protected-private-key(ecc(curve%s)(q%m)" - "(protected openpgp-native%S)))", - curve, skey[0], transfer_key); + { + const char *format; + + if (!strcmp (curve, openpgp_curve_to_oid ("Ed25519", NULL))) + { + /* Do not store the OID as name but the real name and the + EdDSA flag. */ + format = "(protected-private-key(ecc(curve %s)(flags eddsa)(q%m)" + "(protected openpgp-native%S)))"; + curve = "Ed25519"; + } + else if (!strcmp (curve, openpgp_curve_to_oid ("Curve25519", NULL))) + { + format = "(protected-private-key(ecc(curve %s)(flags djb-tweak)(q%m)" + "(protected openpgp-native%S)))"; + curve = "Curve25519"; + } + else + format = "(protected-private-key(ecc(curve %s)(q%m)" + "(protected openpgp-native%S)))"; + + err = gcry_sexp_build (&s_skey, NULL, format, curve, skey[0], transfer_key); + } break; default: diff --git a/common/openpgp-oid.c b/common/openpgp-oid.c index af91bb1aa..afda37605 100644 --- a/common/openpgp-oid.c +++ b/common/openpgp-oid.c @@ -45,6 +45,7 @@ static struct { const char *alias; /* NULL or alternative name of the curve. */ } oidtable[] = { + { "Curve25519", "1.3.6.1.4.1.3029.1.5.1", 255, "crv25519" }, { "Ed25519", "1.3.6.1.4.1.11591.15.1", 255, "ed25519" }, { "NIST P-256", "1.2.840.10045.3.1.7", 256, "nistp256" }, @@ -65,6 +66,10 @@ static struct { static const char oid_ed25519[] = { 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, 0x47, 0x0f, 0x01 }; +/* The OID for Curve25519 in OpenPGP format. */ +static const char oid_crv25519[] = + { 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01 }; + /* Helper for openpgp_oid_from_str. */ static size_t @@ -291,6 +296,22 @@ openpgp_oid_is_ed25519 (gcry_mpi_t a) } +int +openpgp_oid_is_crv25519 (gcry_mpi_t a) +{ + const unsigned char *buf; + unsigned int nbits; + size_t n; + + if (!a || !gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) + return 0; + + buf = gcry_mpi_get_opaque (a, &nbits); + n = (nbits+7)/8; + return (n == DIM (oid_crv25519) + && !memcmp (buf, oid_crv25519, DIM (oid_crv25519))); +} + /* Map the Libgcrypt ECC curve NAME to an OID. If R_NBITS is not NULL store the bit size of the curve there. Returns NULL for unknown diff --git a/common/util.h b/common/util.h index df0f39290..66749eaaa 100644 --- a/common/util.h +++ b/common/util.h @@ -322,6 +322,7 @@ size_t percent_unescape_inplace (char *string, int nulrepl); gpg_error_t openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi); char *openpgp_oid_to_str (gcry_mpi_t a); int openpgp_oid_is_ed25519 (gcry_mpi_t a); +int openpgp_oid_is_crv25519 (gcry_mpi_t a); const char *openpgp_curve_to_oid (const char *name, unsigned int *r_nbits); const char *openpgp_oid_to_curve (const char *oid, int canon); const char *openpgp_enum_curves (int *idxp); diff --git a/g10/ecdh.c b/g10/ecdh.c index 9576a1c1a..a1b7ecfdc 100644 --- a/g10/ecdh.c +++ b/g10/ecdh.c @@ -134,9 +134,12 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, } secret_x_size = (nbits+7)/8; - assert (nbytes > secret_x_size); - memmove (secret_x, secret_x+1, secret_x_size); - memset (secret_x+secret_x_size, 0, nbytes-secret_x_size); + assert (nbytes >= secret_x_size); + if ((nbytes & 1)) + /* Remove the "04" prefix of non-compressed format. */ + memmove (secret_x, secret_x+1, secret_x_size); + if (nbytes - secret_x_size) + memset (secret_x+secret_x_size, 0, nbytes-secret_x_size); if (DBG_CRYPTO) log_printhex ("ECDH shared secret X is:", secret_x, secret_x_size ); diff --git a/g10/keygen.c b/g10/keygen.c index 796d18f8e..f03c148cf 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -1520,6 +1520,13 @@ gen_ecc (int algo, const char *curve, kbnode_t pub_root, (((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? " transient-key" : "")); + else if (algo == PUBKEY_ALGO_ECDH && !strcmp (curve, "Curve25519")) + keyparms = xtryasprintf + ("(genkey(ecc(curve %zu:%s)(flags djb-tweak comp%s)))", + strlen (curve), curve, + (((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) + && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? + " transient-key" : "")); else keyparms = xtryasprintf ("(genkey(ecc(curve %zu:%s)(flags nocomp%s)))", @@ -2125,7 +2132,7 @@ ask_keysize (int algo, unsigned int primary_keysize) function may adjust. Returns a malloced string with the name of the curve. BOTH tells that gpg creates a primary and subkey. */ static char * -ask_curve (int *algo, int both) +ask_curve (int *algo, int *subkey_algo) { struct { const char *name; @@ -2176,7 +2183,7 @@ ask_curve (int *algo, int both) continue; if (!gcry_pk_get_curve (keyparms, 0, NULL)) continue; - if (both && curves[idx].fix_curve) + if (subkey_algo && curves[idx].fix_curve) { /* Both Curve 25519 keys are to be created. Check that Libgcrypt also supports the real Curve25519. */ @@ -2241,6 +2248,11 @@ ask_curve (int *algo, int both) if ((*algo == PUBKEY_ALGO_ECDSA || *algo == PUBKEY_ALGO_EDDSA) && curves[idx].fix_curve) { + if (subkey_algo && *subkey_algo == PUBKEY_ALGO_ECDSA) + { + *subkey_algo = PUBKEY_ALGO_EDDSA; + result = xstrdup ("Ed25519"); + } *algo = PUBKEY_ALGO_EDDSA; result = xstrdup ("Ed25519"); } @@ -3672,7 +3684,7 @@ generate_keypair (ctrl_t ctrl, int full, const char *fname, || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) { - curve = ask_curve (&algo, both); + curve = ask_curve (&algo, &subkey_algo); r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYTYPE; sprintf( r->u.value, "%d", algo); @@ -3743,7 +3755,7 @@ generate_keypair (ctrl_t ctrl, int full, const char *fname, || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) { - curve = ask_curve (&algo, 0); + curve = ask_curve (&algo, NULL); nbits = 0; r = xmalloc_clear (sizeof *r + strlen (curve)); r->key = pKEYCURVE; @@ -4292,7 +4304,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 (&algo, 0); + curve = ask_curve (&algo, NULL); else nbits = ask_keysize (algo, 0); diff --git a/g10/keyid.c b/g10/keyid.c index 68990c8bd..42a5f9fe0 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -766,9 +766,12 @@ keygrip_from_pk (PKT_public_key *pk, unsigned char *array) else { err = gcry_sexp_build (&s_pkey, NULL, - pk->pubkey_algo == PUBKEY_ALGO_EDDSA ? - "(public-key(ecc(curve%s)(flags eddsa)(q%m)))" - : "(public-key(ecc(curve%s)(q%m)))", + pk->pubkey_algo == PUBKEY_ALGO_EDDSA? + "(public-key(ecc(curve%s)(flags eddsa)(q%m)))": + (pk->pubkey_algo == PUBKEY_ALGO_ECDH + && openpgp_oid_is_crv25519 (pk->pkey[0]))? + "(public-key(ecc(curve%s)(flags djb-tweak)(q%m)))": + "(public-key(ecc(curve%s)(q%m)))", curve, pk->pkey[1]); xfree (curve); } diff --git a/g10/pkglue.c b/g10/pkglue.c index d72275b81..a83462187 100644 --- a/g10/pkglue.c +++ b/g10/pkglue.c @@ -228,9 +228,13 @@ pk_encrypt (pubkey_algo_t algo, gcry_mpi_t *resarr, gcry_mpi_t data, rc = gpg_error_from_syserror (); else { + int with_djb_tweak_flag = openpgp_oid_is_crv25519 (pkey[0]); + /* Now use the ephemeral secret to compute the shared point. */ rc = gcry_sexp_build (&s_pkey, NULL, - "(public-key(ecdh(curve%s)(q%m)))", + with_djb_tweak_flag ? + "(public-key(ecdh(curve%s)(flags djb-tweak)(q%m)))" + : "(public-key(ecdh(curve%s)(q%m)))", curve, pkey[1]); xfree (curve); /* Put K into a simplified S-expression. */ diff --git a/g10/pubkey-enc.c b/g10/pubkey-enc.c index cb834afab..fd7f81277 100644 --- a/g10/pubkey-enc.c +++ b/g10/pubkey-enc.c @@ -250,8 +250,8 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid) if(err) goto leave; - /* Reuse NFRAME, which size is sufficient to include the session key. */ - err = gcry_mpi_print (GCRYMPI_FMT_USG, frame, nframe, &nframe, decoded); + xfree (frame); + err = gcry_mpi_aprint (GCRYMPI_FMT_USG, &frame, &nframe, decoded); mpi_release (decoded); if (err) goto leave; diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 637f6b19d..461c71033 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -235,7 +235,7 @@ struct app_local_s { } keyattr[3]; }; -#define ECC_FLAG_EDDSA (1 << 0) +#define ECC_FLAG_DJB_TWEAK (1 << 0) /***** Local prototypes *****/ @@ -909,8 +909,9 @@ send_key_attr (ctrl_t ctrl, app_t app, const char *keyword, int keyno) { snprintf (buffer, sizeof buffer, "%d %d %s", keyno+1, - app->app_local->keyattr[keyno].ecc.flags? PUBKEY_ALGO_EDDSA: - (keyno==1? PUBKEY_ALGO_ECDH: PUBKEY_ALGO_ECDSA), + keyno==1? PUBKEY_ALGO_ECDH : + app->app_local->keyattr[keyno].ecc.flags? + PUBKEY_ALGO_EDDSA : PUBKEY_ALGO_ECDSA, openpgp_oid_to_curve (app->app_local->keyattr[keyno].ecc.oid, 0)); } else @@ -1378,59 +1379,52 @@ get_public_key (app_t app, int keyno) } } - - mbuf = xtrymalloc ( mlen + 1); + mbuf = xtrymalloc (mlen + 1); if (!mbuf) { err = gpg_error_from_syserror (); goto leave; } - /* Prepend numbers with a 0 if needed. */ + if ((app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA || (app->app_local->keyattr[keyno].key_type == KEY_TYPE_ECC && !app->app_local->keyattr[keyno].ecc.flags)) && mlen && (*m & 0x80)) - { + { /* Prepend numbers with a 0 if needed for MPI. */ *mbuf = 0; memcpy (mbuf+1, m, mlen); mlen++; } + else if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_ECC + && app->app_local->keyattr[keyno].ecc.flags) + { /* Prepend 0x40 prefix. */ + *mbuf = 0x40; + memcpy (mbuf+1, m, mlen); + mlen++; + } else memcpy (mbuf, m, mlen); - ebuf = xtrymalloc ( elen + 1); - if (!ebuf) - { - err = gpg_error_from_syserror (); - goto leave; - } - /* Prepend numbers with a 0 if needed. */ - if (elen && (*e & 0x80)) - { - *ebuf = 0; - memcpy (ebuf+1, e, elen); - elen++; - } - else - memcpy (ebuf, e, elen); - if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA) { - err = gcry_sexp_build (&s_pkey, NULL, "(public-key(rsa(n%b)(e%b)))", - (int)mlen, mbuf, (int)elen, ebuf); - if (err) - goto leave; - - len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0); - keybuf = xtrymalloc (len); - if (!keybuf) + ebuf = xtrymalloc (elen + 1); + if (!ebuf) { - gcry_sexp_release (s_pkey); err = gpg_error_from_syserror (); goto leave; } - gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keybuf, len); - gcry_sexp_release (s_pkey); + /* Prepend numbers with a 0 if needed. */ + if (elen && (*e & 0x80)) + { + *ebuf = 0; + memcpy (ebuf+1, e, elen); + elen++; + } + else + memcpy (ebuf, e, elen); + + err = gcry_sexp_build (&s_pkey, NULL, "(public-key(rsa(n%b)(e%b)))", + (int)mlen, mbuf, (int)elen, ebuf); } else if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_ECC) { @@ -1438,32 +1432,32 @@ get_public_key (app_t app, int keyno) if (!app->app_local->keyattr[keyno].ecc.flags) format = "(public-key(ecc(curve%s)(q%b)))"; + else if (keyno == 1) + format = "(public-key(ecc(curve%s)(flags djb-tweak)(q%b)))"; else format = "(public-key(ecc(curve%s)(flags eddsa)(q%b)))"; err = gcry_sexp_build (&s_pkey, NULL, format, openpgp_oid_to_curve (app->app_local->keyattr[keyno].ecc.oid, 1), (int)mlen, mbuf); - if (err) - goto leave; - - len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0); - - keybuf = xtrymalloc (len); - if (!keybuf) - { - gcry_sexp_release (s_pkey); - err = gpg_error_from_syserror (); - goto leave; - } - gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keybuf, len); - gcry_sexp_release (s_pkey); } else + err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); + + if (err) + goto leave; + + len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0); + + keybuf = xtrymalloc (len); + if (!keybuf) { - err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); + gcry_sexp_release (s_pkey); + err = gpg_error_from_syserror (); goto leave; } + gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keybuf, len); + gcry_sexp_release (s_pkey); app->app_local->pk[keyno].key = (unsigned char*)keybuf; app->app_local->pk[keyno].keylen = len - 1; /* Decrement for trailing '\0' */ @@ -3171,7 +3165,7 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), size_t ecc_q_len, ecc_d_len; u32 created_at = 0; const char *oidstr = NULL; - int flag_eddsa = 0; + int flag_djb_tweak = 0; int algo; /* (private-key(ecc(curve%s)(q%m)(d%m))(created-at%d)): @@ -3216,8 +3210,12 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; - if (tok && toklen == 5 && !memcmp (tok, "eddsa", 5)) - flag_eddsa = 1; + if (tok) + { + if ((toklen == 5 && !memcmp (tok, "eddsa", 5)) + || (toklen == 9 && !memcmp (tok, "djb-tweak", 9))) + flag_djb_tweak = 1; + } } else if (tok && toklen == 1) { @@ -3237,7 +3235,7 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), } if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; - if (tok && buf2 && !flag_eddsa) + if (tok && buf2 && !flag_djb_tweak) /* It's MPI. Strip off leading zero bytes and save. */ for (;toklen && !*tok; toklen--, tok++) ; @@ -3300,7 +3298,7 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), err = gpg_error (GPG_ERR_INV_VALUE); goto leave; } - if (flag_eddsa && keyno != 1) + if (flag_djb_tweak && keyno != 1) algo = PUBKEY_ALGO_EDDSA; else if (keyno == 1) algo = PUBKEY_ALGO_ECDH; @@ -3309,7 +3307,7 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), if (app->app_local->keyattr[keyno].key_type != KEY_TYPE_ECC || app->app_local->keyattr[keyno].ecc.oid != oidstr - || app->app_local->keyattr[keyno].ecc.flags != flag_eddsa) + || app->app_local->keyattr[keyno].ecc.flags != flag_djb_tweak) { log_error ("key attribute on card doesn't match\n"); err = gpg_error (GPG_ERR_INV_VALUE); @@ -4469,11 +4467,18 @@ parse_algorithm_attribute (app_t app, int keyno) { app->app_local->keyattr[keyno].key_type = KEY_TYPE_ECC; app->app_local->keyattr[keyno].ecc.oid = oid; - app->app_local->keyattr[keyno].ecc.flags = (*buffer == PUBKEY_ALGO_EDDSA); + if (*buffer == PUBKEY_ALGO_EDDSA + || (*buffer == PUBKEY_ALGO_ECDH + && !strcmp (app->app_local->keyattr[keyno].ecc.oid, + "1.3.6.1.4.1.3029.1.5.1"))) + app->app_local->keyattr[keyno].ecc.flags = ECC_FLAG_DJB_TWEAK; + else + app->app_local->keyattr[keyno].ecc.flags = 0; if (opt.verbose) log_printf ("ECC, curve=%s%s\n", app->app_local->keyattr[keyno].ecc.oid, - app->app_local->keyattr[keyno].ecc.flags ? " (eddsa)": ""); + !app->app_local->keyattr[keyno].ecc.flags ? "": + keyno==1? " (djb-tweak)": " (eddsa)"); } } else if (opt.verbose)