From 402aa0f94854bb00475c934be5ca6043a4632126 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 15 Nov 2013 08:59:45 +0100 Subject: [PATCH] gpg: Rework ECC support and add experimental support for Ed25519. * agent/findkey.c (key_parms_from_sexp): Add algo name "ecc". (agent_is_dsa_key): Ditto. (agent_is_eddsa_key): New. Not finished, though. * agent/pksign.c (do_encode_eddsa): New. (agent_pksign_do): Use gcry_log_debug functions. * agent/protect.c (agent_protect): Parse a flags parameter. * g10/keygen.c (gpg_curve_to_oid): Move to ... * common/openpgp-oid.c (openpgp_curve_to_oid): here and rename. (oid_ed25519): New. (openpgp_oid_is_ed25519): New. (openpgp_oid_to_curve): New. * common/t-openpgp-oid.c (test_openpgp_oid_is_ed25519): New. * g10/build-packet.c (gpg_mpi_write): Write the length header also for opaque MPIs. (gpg_mpi_write_nohdr): New. (do_key): Use gpg_mpi_write_nohdr depending on algorithm. (do_pubkey_enc): Ditto. * g10/ecdh.c (pk_ecdh_encrypt_with_shared_point): Use gpg_mpi_write_nohdr. * g10/export.c (transfer_format_to_openpgp): * g10/keygen.c (ecckey_from_sexp): Return the error. (gen_ecc): Repalce arg NBITS by CURVE. (read_parameter_file): Add keywords "Key-Curve" and "Subkey-Curve". (ask_curve): New. (generate_keypair, generate_subkeypair): Use ask_curve. (do_generate_keypair): Also pass curve name. * g10/keylist.c (list_keyblock_print, list_keyblock_colon): Print curve name. * g10/parse-packet.c (mpi_read): Remove workaround for Libcgrypt < 1.5. (parse_key): Fix ECC case. Print the curve name. * g10/pkglue.c (mpi_from_sexp): Rename to get_mpi_from_sexp. (pk_verify, pk_check_secret_key): Add special case for Ed25519. * g10/seskey.c (encode_md_value): Ditto. * g10/sign.c (do_sign, hash_for, sign_file): Ditto. -- Be warned that this code is subject to further changes and that the format will very likely change before a release. There are also known bugs and missing code. Signed-off-by: Werner Koch --- agent/agent.h | 1 + agent/findkey.c | 29 ++++++ agent/pksign.c | 35 +++++-- agent/protect.c | 15 +++ common/openpgp-oid.c | 91 ++++++++++++++++ common/t-openpgp-oid.c | 38 ++++++- common/util.h | 3 + doc/DETAILS | 9 +- g10/build-packet.c | 43 +++++++- g10/ecdh.c | 4 +- g10/export.c | 2 +- g10/keygen.c | 232 +++++++++++++++++++++++++++++------------ g10/keylist.c | 61 +++++++++-- g10/main.h | 1 - g10/packet.h | 1 + g10/parse-packet.c | 27 +++-- g10/pkglue.c | 77 ++++++++++---- g10/pkglue.h | 2 +- g10/seskey.c | 7 +- g10/sign.c | 35 +++++-- 20 files changed, 574 insertions(+), 139 deletions(-) diff --git a/agent/agent.h b/agent/agent.h index ae4e4686f..d40930018 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -324,6 +324,7 @@ gpg_error_t agent_public_key_from_file (ctrl_t ctrl, const unsigned char *grip, gcry_sexp_t *result); int agent_is_dsa_key (gcry_sexp_t s_key); +int agent_is_eddsa_key (gcry_sexp_t s_key); int agent_key_available (const unsigned char *grip); gpg_error_t agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip, int *r_keytype, diff --git a/agent/findkey.c b/agent/findkey.c index d11f0888a..aa2c6a2c1 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -729,6 +729,11 @@ key_parms_from_sexp (gcry_sexp_t s_key, gcry_sexp_t *r_list, algoname = "dsa"; elems = "pqgy"; } + else if (n==3 && !memcmp (name, "ecc", 3)) + { + algoname = "ecc"; + elems = "pabgnq"; + } else if (n==5 && !memcmp (name, "ecdsa", 5)) { algoname = "ecdsa"; @@ -788,6 +793,8 @@ agent_is_dsa_key (gcry_sexp_t s_key) if (!strcmp (algoname, "dsa")) return GCRY_PK_DSA; + else if (!strcmp (algoname, "ecc")) + return GCRY_PK_ECDSA; /* FIXME: Check for the EdDSA flag. */ else if (!strcmp (algoname, "ecdsa")) return GCRY_PK_ECDSA; else @@ -795,6 +802,28 @@ agent_is_dsa_key (gcry_sexp_t s_key) } +/* Return true if S_KEY is an EdDSA key as used with curve Ed25519. */ +int +agent_is_eddsa_key (gcry_sexp_t s_key) +{ + char algoname[6]; + + if (!s_key) + return 0; + + if (key_parms_from_sexp (s_key, NULL, algoname, sizeof algoname, NULL, 0)) + return 0; /* Error - assume it is not an DSA key. */ + + if (!strcmp (algoname, "dsa")) + return GCRY_PK_DSA; + else if (!strcmp (algoname, "ecc")) + return GCRY_PK_ECDSA; /* FIXME: Check for the EdDSA flag. */ + else if (!strcmp (algoname, "ecdsa")) + return GCRY_PK_ECDSA; + else + return 0; +} + /* Return the key for the keygrip GRIP. The result is stored at RESULT. This function extracts the key from the private key diff --git a/agent/pksign.c b/agent/pksign.c index 9c7341a7f..b2ee28f22 100644 --- a/agent/pksign.c +++ b/agent/pksign.c @@ -131,6 +131,24 @@ rfc6979_hash_algo_string (size_t mdlen) } +/* Encode a message digest for use with the EdDSA algorithm + (i.e. curve Ed25519). */ +static gpg_error_t +do_encode_eddsa (const byte *md, size_t mdlen, gcry_sexp_t *r_hash) +{ + gpg_error_t err; + gcry_sexp_t hash; + + *r_hash = NULL; + err = gcry_sexp_build (&hash, NULL, + "(data(flags eddsa)(hash-algo sha512)(value %b))", + (int)mdlen, md); + if (!err) + *r_hash = hash; + return err; +} + + /* Encode a message digest for use with an DSA algorithm. */ static gpg_error_t do_encode_dsa (const byte *md, size_t mdlen, int dsaalgo, gcry_sexp_t pkey, @@ -400,7 +418,11 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce, int dsaalgo; /* Put the hash into a sexp */ - if (ctrl->digest.algo == MD_USER_TLS_MD5SHA1) + if (agent_is_eddsa_key (s_skey)) + rc = do_encode_eddsa (ctrl->digest.value, + ctrl->digest.valuelen, + &s_hash); + else if (ctrl->digest.algo == MD_USER_TLS_MD5SHA1) rc = do_encode_raw_pkcs1 (ctrl->digest.value, ctrl->digest.valuelen, gcry_pk_get_nbits (s_skey), @@ -421,10 +443,8 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce, if (DBG_CRYPTO) { - log_debug ("skey:\n"); - gcry_sexp_dump (s_skey); - log_debug ("hash:\n"); - gcry_sexp_dump (s_hash); + gcry_log_debugsxp ("skey", s_skey); + gcry_log_debugsxp ("hash", s_hash); } /* sign */ @@ -437,10 +457,7 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce, } if (DBG_CRYPTO) - { - log_debug ("result:\n"); - gcry_sexp_dump (s_sig); - } + gcry_log_debugsxp ("rslt", s_sig); } leave: diff --git a/agent/protect.c b/agent/protect.c index b29f494fb..749867cc1 100644 --- a/agent/protect.c +++ b/agent/protect.c @@ -467,6 +467,7 @@ agent_protect (const unsigned char *plainkey, const char *passphrase, int depth = 0; unsigned char *p; gcry_md_hd_t md; + int have_curve = 0; /* Create an S-expression with the protected-at timestamp. */ memcpy (timestamp_exp, "(12:protected-at15:", 19); @@ -499,6 +500,11 @@ agent_protect (const unsigned char *plainkey, const char *passphrase, if (!protect_info[infidx].algo) return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); + /* The parser below is a complete mess: To make it robust for ECC + use we should reorder the s-expression to include only what we + really need and thus guarantee the right order for saving stuff. + This should be done before calling this function and maybe with + the help of the new gcry_sexp_extract_param. */ parmlist = protect_info[infidx].parmlist; prot_from_idx = protect_info[infidx].prot_from; prot_to_idx = protect_info[infidx].prot_to; @@ -522,10 +528,19 @@ agent_protect (const unsigned char *plainkey, const char *passphrase, /* This is a private ECC key but the first parameter is the name of the curve. We change the parameter list here to the one we expect in this case. */ + have_curve = 1; parmlist = "?qd"; prot_from_idx = 2; prot_to_idx = 2; } + else if (n == 5 && !memcmp (s, "flags", 5) + && i == 1 && have_curve) + { + /* "curve" followed by "flags": Change again. */ + parmlist = "??qd"; + prot_from_idx = 3; + prot_to_idx = 3; + } else return gpg_error (GPG_ERR_INV_SEXP); } diff --git a/common/openpgp-oid.c b/common/openpgp-oid.c index 19fadd3f2..a1ceba4ef 100644 --- a/common/openpgp-oid.c +++ b/common/openpgp-oid.c @@ -1,5 +1,6 @@ /* openpgp-oids.c - OID helper for OpenPGP * Copyright (C) 2011 Free Software Foundation, Inc. + * Copyright (C) 2013 Werner Koch * * This file is part of GnuPG. * @@ -36,6 +37,11 @@ #include "util.h" +/* The OID for Curve Ed25519 in OpenPGP format. */ +static const char oid_ed25519[] = + { 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01 }; + + /* Helper for openpgp_oid_from_str. */ static size_t make_flagged_int (unsigned long value, char *buf, size_t buflen) @@ -236,3 +242,88 @@ openpgp_oid_to_str (gcry_mpi_t a) xfree (string); return xtrystrdup ("1.3.6.1.4.1.11591.2.12242973"); } + + + +/* Return true if A represents the OID for Ed25519. */ +int +openpgp_oid_is_ed25519 (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_ed25519) + && !memcmp (buf, oid_ed25519, DIM (oid_ed25519))); +} + + + +/* 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 + curve names. */ +const char * +openpgp_curve_to_oid (const char *name, unsigned int *r_nbits) +{ + unsigned int nbits = 0; + const char *oidstr; + + if (!name) + oidstr = NULL; + else if (!strcmp (name, "Ed25519")) + { + oidstr = "1.3.6.1.4.1.3029.1.5.1"; + nbits = 255; + } + else if (!strcmp (name, "nistp256")) + { + oidstr = "1.2.840.10045.3.1.7"; + nbits = 256; + } + else if (!strcmp (name, "nistp384")) + { + oidstr = "1.3.132.0.34"; + nbits = 384; + } + else if (!strcmp (name, "nistp521")) + { + oidstr = "1.3.132.0.35"; + nbits = 521; + } + else + oidstr = NULL; + + if (r_nbits) + *r_nbits = nbits; + return oidstr; +} + + +/* Map an OpenPGP OID to the Libgcrypt curve NAME. If R_NBITS is not + NULL store the bit size of the curve there. Returns "?" for + unknown curve names. */ +const char * +openpgp_oid_to_curve (const char *oid) +{ + const char *name; + + if (!oid) + name = ""; + else if (!strcmp (oid, "1.3.6.1.4.1.3029.1.5.1")) + name = "Ed25519"; + else if (!strcmp (oid, "1.2.840.10045.3.1.7")) + name = "NIST P-256"; + else if (!strcmp (oid, "1.3.132.0.34")) + name = "NIST P-384"; + else if (!strcmp (oid, "1.3.132.0.35")) + name = "NIST P-521"; + else /* FIXME: Lookup via Libgcrypt. */ + name = "?"; + + return name; +} diff --git a/common/t-openpgp-oid.c b/common/t-openpgp-oid.c index 80e576309..d101b7597 100644 --- a/common/t-openpgp-oid.c +++ b/common/t-openpgp-oid.c @@ -35,7 +35,7 @@ static void test_openpgp_oid_from_str (void) { - static char *sample_oids[] = + static char *sample_oids[] = { "0.0", "1.0", @@ -134,6 +134,41 @@ test_openpgp_oid_to_str (void) } +static void +test_openpgp_oid_is_ed25519 (void) +{ + static struct + { + int yes; + const char *oidstr; + } samples[] = { + { 0, "0.0" }, + { 0, "1.3.132.0.35" }, + { 0, "1.3.6.1.4.1.3029.1.5.0" }, + { 1, "1.3.6.1.4.1.3029.1.5.1" }, + { 0, "1.3.6.1.4.1.3029.1.5.2" }, + { 0, "1.3.6.1.4.1.3029.1.5.1.0" }, + { 0, "1.3.6.1.4.1.3029.1.5" }, + { 0, NULL }, + }; + gpg_error_t err; + gcry_mpi_t a; + int idx; + + for (idx=0; samples[idx].oidstr; idx++) + { + err = openpgp_oid_from_str (samples[idx].oidstr, &a); + if (err) + fail (idx, err); + + if (openpgp_oid_is_ed25519 (a) != samples[idx].yes) + fail (idx, 0); + + gcry_mpi_release (a); + } + +} + int main (int argc, char **argv) @@ -143,6 +178,7 @@ main (int argc, char **argv) test_openpgp_oid_from_str (); test_openpgp_oid_to_str (); + test_openpgp_oid_is_ed25519 (); return 0; } diff --git a/common/util.h b/common/util.h index 13b702ce5..f93888837 100644 --- a/common/util.h +++ b/common/util.h @@ -215,6 +215,9 @@ size_t percent_unescape_inplace (char *string, int nulrepl); /*-- openpgp-oid.c --*/ 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); +const char *openpgp_curve_to_oid (const char *name, unsigned int *r_nbits); +const char *openpgp_oid_to_curve (const char *oid); diff --git a/doc/DETAILS b/doc/DETAILS index 100755a67..a52f51cec 100644 --- a/doc/DETAILS +++ b/doc/DETAILS @@ -32,8 +32,8 @@ fpr:::::::::AB059359A3B81F410FCFF97F5CE086B5B5A18FF4: #+end_example The double =--with-fingerprint= prints the fingerprint for the subkeys -too. Old versions of gpg used a lighly different format and required -the use of the option =--fixed-list-mode= to conform to format +too. Old versions of gpg used a slighrly different format and required +the use of the option =--fixed-list-mode= to conform to the format described here. ** Description of the fields @@ -201,6 +201,11 @@ described here. For sig records, this is the used hash algorithm. For example: 2 = SHA-1, 8 = SHA-256. +*** Field 17 - Curve name + + For pub, sub, sec, and sbb records this field is used for the ECC + curve name. + ** Special fields *** PKD - Public key data diff --git a/g10/build-packet.c b/g10/build-packet.c index 159b783ed..6681b3429 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -166,9 +166,14 @@ gpg_mpi_write (iobuf_t out, gcry_mpi_t a) { unsigned int nbits; const void *p; + unsigned int lenhdr[2]; p = gcry_mpi_get_opaque (a, &nbits); - rc = iobuf_write (out, p, (nbits+7)/8); + lenhdr[0] = nbits >> 8; + lenhdr[1] = nbits; + rc = iobuf_write (out, lenhdr, 2); + if (!rc) + rc = iobuf_write (out, p, (nbits+7)/8); } else { @@ -191,6 +196,29 @@ gpg_mpi_write (iobuf_t out, gcry_mpi_t a) } +/* + * Write an opaque MPI to the output stream without length info. + */ +gpg_error_t +gpg_mpi_write_nohdr (iobuf_t out, gcry_mpi_t a) +{ + int rc; + + if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) + { + unsigned int nbits; + const void *p; + + p = gcry_mpi_get_opaque (a, &nbits); + rc = iobuf_write (out, p, (nbits+7)/8); + } + else + rc = gpg_error (GPG_ERR_BAD_MPI); + + return rc; +} + + /* Calculate the length of a packet described by PKT. */ u32 calc_packet_length( PACKET *pkt ) @@ -302,7 +330,11 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk) for (i=0; i < npkey; i++ ) { - err = gpg_mpi_write (a, pk->pkey[i]); + if ((pk->pubkey_algo == PUBKEY_ALGO_ECDSA && (i == 0)) + || (pk->pubkey_algo == PUBKEY_ALGO_ECDH) && (i == 0 || i == 2)) + err = gpg_mpi_write_nohdr (a, pk->pkey[i]); + else + err = gpg_mpi_write (a, pk->pkey[i]); if (err) goto leave; } @@ -473,7 +505,12 @@ do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc ) write_fake_data( a, enc->data[0] ); for (i=0; i < n && !rc ; i++ ) - rc = gpg_mpi_write (a, enc->data[i]); + { + if (enc->pubkey_algo == PUBKEY_ALGO_ECDH && i == 1) + rc = gpg_mpi_write_nohdr (a, enc->data[i]); + else + rc = gpg_mpi_write (a, enc->data[i]); + } if (!rc) { diff --git a/g10/ecdh.c b/g10/ecdh.c index 8b1949c48..752181ee5 100644 --- a/g10/ecdh.c +++ b/g10/ecdh.c @@ -197,11 +197,11 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, obuf = iobuf_temp(); /* variable-length field 1, curve name OID */ - err = gpg_mpi_write (obuf, pkey[0]); + err = gpg_mpi_write_nohdr (obuf, pkey[0]); /* fixed-length field 2 */ iobuf_put (obuf, PUBKEY_ALGO_ECDH); /* variable-length field 3, KDF params */ - err = (err ? err : gpg_mpi_write (obuf, pkey[2])); + err = (err ? err : gpg_mpi_write_nohdr (obuf, pkey[2])); /* fixed-length field 4 */ iobuf_write (obuf, "Anonymous Sender ", 20); /* fixed-length field 5, recipient fp */ diff --git a/g10/export.c b/g10/export.c index 7fbcb346b..01bdd5e82 100644 --- a/g10/export.c +++ b/g10/export.c @@ -583,7 +583,7 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk) goto leave; curvename = gcry_pk_get_curve (s_pubkey, 0, NULL); gcry_sexp_release (s_pubkey); - curveoidstr = gpg_curve_to_oid (curvename, NULL); + curveoidstr = openpgp_curve_to_oid (curvename, NULL); if (!curveoidstr) { log_error ("no OID known for curve '%s'\n", curvename); diff --git a/g10/keygen.c b/g10/keygen.c index 3b02f043d..9c371bd1a 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -60,9 +60,11 @@ enum para_name { pKEYTYPE, pKEYLENGTH, + pKEYCURVE, pKEYUSAGE, pSUBKEYTYPE, pSUBKEYLENGTH, + pSUBKEYCURVE, pSUBKEYUSAGE, pAUTHKEYTYPE, pNAMEREAL, @@ -1071,40 +1073,6 @@ write_keybinding (KBNODE root, PKT_public_key *pri_psk, PKT_public_key *sub_psk, return err; } -/* 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 - curve names. */ -const char * -gpg_curve_to_oid (const char *name, unsigned int *r_nbits) -{ - unsigned int nbits = 0; - const char *oidstr; - - if (!name) - oidstr = NULL; - else if (!strcmp (name, "NIST P-256")) - { - oidstr = "1.2.840.10045.3.1.7"; - nbits = 256; - } - else if (!strcmp (name, "NIST P-384")) - { - oidstr = "1.3.132.0.34"; - nbits = 384; - } - else if (!strcmp (name, "NIST P-521")) - { - oidstr = "1.3.132.0.35"; - nbits = 521; - } - else - oidstr = NULL; - - if (r_nbits) - *r_nbits = nbits; - return oidstr; -} - static gpg_error_t ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo) @@ -1142,7 +1110,7 @@ ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo) goto leave; } gcry_sexp_release (l2); - oidstr = gpg_curve_to_oid (curve, &nbits); + oidstr = openpgp_curve_to_oid (curve, &nbits); if (!oidstr) { /* That can't happen because we used one of the curves @@ -1188,7 +1156,7 @@ ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo) array[i] = NULL; } } - return 0; + return err; } @@ -1534,31 +1502,24 @@ gen_dsa (unsigned int nbits, KBNODE pub_root, * Generate an ECC key */ static gpg_error_t -gen_ecc (int algo, unsigned int nbits, kbnode_t pub_root, +gen_ecc (int algo, const char *curve, kbnode_t pub_root, u32 timestamp, u32 expireval, int is_subkey, int keygen_flags, char **cache_nonce_addr) { gpg_error_t err; - const char *curve; char *keyparms; assert (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH); - /* For now we may only use one of the 3 NIST curves. See also - gpg_curve_to_oid. */ - if (nbits <= 256) - curve = "NIST P-256"; - else if (nbits <= 384) - curve = "NIST P-384"; - else - curve = "NIST P-521"; + if (!curve || !*curve) + return gpg_error (GPG_ERR_UNKNOWN_CURVE); - keyparms = xtryasprintf ("(genkey(%s(curve %zu:%s)%s))", - algo == PUBKEY_ALGO_ECDSA ? "ecdsa" : "ecdh", + keyparms = xtryasprintf ("(genkey(ecc(curve %zu:%s)(flags nocomp%s%s)))", strlen (curve), curve, - ((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) - && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? - "(transient-key)" : "" ); + (((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) + && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? + " transient-key" : ""), + (!strcmp (curve, "Ed25519")? " eddsa":"")); if (!keyparms) err = gpg_error_from_syserror (); else @@ -2082,6 +2043,98 @@ 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. */ +static char * +ask_curve (void) +{ + struct { + const char *name; + int available; + int expert_only; + const char *pretty_name; + } curves[] = { + { "Ed25519", 0, 0, "Curve 25519" }, + { "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" }, + }; + int idx; + char *answer; + char *result = NULL; + gcry_sexp_t keyparms; + + tty_printf (_("Please select which elliptic curve you want:\n")); + + keyparms = NULL; + for (idx=0; idx < DIM(curves); idx++) + { + int rc; + + curves[idx].available = 0; + if (!opt.expert && curves[idx].expert_only) + continue; + + gcry_sexp_release (keyparms); + rc = gcry_sexp_build (&keyparms, NULL, + "(public-key(ecc(curve %s)))", curves[idx].name); + if (rc) + continue; + if (!gcry_pk_get_curve (keyparms, 0, NULL)) + continue; + + curves[idx].available = 1; + tty_printf (_(" (%d) %s\n"), idx + 1, + curves[idx].pretty_name? + curves[idx].pretty_name:curves[idx].name); + } + gcry_sexp_release (keyparms); + + + for (;;) + { + answer = cpr_get ("keygen.curve", _("Your selection? ")); + cpr_kill_prompt (); + idx = *answer? atoi (answer) : 1; + if (*answer && !idx) + { + /* See whether the user entered the name of the curve. */ + for (idx=0; idx < DIM(curves); idx++) + { + if (!opt.expert && curves[idx].expert_only) + continue; + if (!stricmp (curves[idx].name, answer) + || (curves[idx].pretty_name + && !stricmp (curves[idx].pretty_name, answer))) + break; + } + if (idx == DIM(curves)) + idx = -1; + } + else + idx--; + xfree(answer); + answer = NULL; + if (idx < 0 || idx >= DIM (curves) || !curves[idx].available) + tty_printf (_("Invalid selection.\n")); + else + { + result = xstrdup (curves[idx].name); + break; + } + } + + if (!result) + result = xstrdup (curves[0].name); + + return result; +} + + /**************** * Parse an expire string and return its value in seconds. * Returns (u32)-1 on error. @@ -2539,7 +2592,7 @@ do_ask_passphrase (STRING2KEY **ret_s2k, int mode, int *r_canceled) /* Basic key generation. Here we divert to the actual generation routines based on the requested algorithm. */ static int -do_create (int algo, unsigned int nbits, KBNODE pub_root, +do_create (int algo, unsigned int nbits, const char *curve, KBNODE pub_root, u32 timestamp, u32 expiredate, int is_subkey, int keygen_flags, char **cache_nonce_addr) { @@ -2561,7 +2614,7 @@ do_create (int algo, unsigned int nbits, KBNODE pub_root, err = gen_dsa (nbits, pub_root, timestamp, expiredate, is_subkey, keygen_flags, cache_nonce_addr); else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH) - err = gen_ecc (algo, nbits, pub_root, timestamp, expiredate, is_subkey, + err = gen_ecc (algo, curve, pub_root, timestamp, expiredate, is_subkey, keygen_flags, cache_nonce_addr); else if (algo == PUBKEY_ALGO_RSA) err = gen_rsa (algo, nbits, pub_root, timestamp, expiredate, is_subkey, @@ -2974,7 +3027,6 @@ proc_parameter_file( struct para_data_s *para, const char *fname, * but because we do this always, why not here. */ STRING2KEY *s2k; DEK *dek; - static int count; s2k = xmalloc ( sizeof *s2k ); s2k->mode = opt.s2k_mode; @@ -3058,9 +3110,11 @@ read_parameter_file( const char *fname ) } keywords[] = { { "Key-Type", pKEYTYPE}, { "Key-Length", pKEYLENGTH }, + { "Key-Curve", pKEYCURVE }, { "Key-Usage", pKEYUSAGE }, { "Subkey-Type", pSUBKEYTYPE }, { "Subkey-Length", pSUBKEYLENGTH }, + { "Subkey-Curve", pSUBKEYCURVE }, { "Subkey-Usage", pSUBKEYUSAGE }, { "Name-Real", pNAMEREAL }, { "Name-Email", pNAMEEMAIL }, @@ -3340,6 +3394,7 @@ generate_keypair (ctrl_t ctrl, const char *fname, const char *card_serialno, else { int subkey_algo; + char *curve = NULL; /* Fixme: To support creating a primary key by keygrip we better also define the keyword for the parameter file. Note that @@ -3355,12 +3410,24 @@ generate_keypair (ctrl_t ctrl, const char *fname, const char *card_serialno, 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; - sprintf( r->u.value, "%u", nbits); - r->next = para; - para = r; + if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH) + { + curve = ask_curve (); + r = xmalloc_clear (sizeof *r + strlen (curve)); + r->key = pKEYCURVE; + strcpy (r->u.value, curve); + r->next = para; + para = r; + } + else + { + nbits = ask_keysize (algo, 0); + r = xmalloc_clear( sizeof *r + 20 ); + r->key = pKEYLENGTH; + sprintf( r->u.value, "%u", nbits); + r->next = para; + para = r; + } r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYUSAGE; strcpy( r->u.value, "sign" ); @@ -3400,12 +3467,27 @@ generate_keypair (ctrl_t ctrl, const char *fname, const char *card_serialno, nbits = 0; } - nbits = ask_keysize (both? subkey_algo : algo, nbits); - r = xmalloc_clear( sizeof *r + 20 ); - r->key = both? pSUBKEYLENGTH : pKEYLENGTH; - sprintf( r->u.value, "%u", nbits); - r->next = para; - para = r; + if (algo == PUBKEY_ALGO_ECDSA || 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; + } + else + { + nbits = ask_keysize (both? subkey_algo : algo, nbits); + r = xmalloc_clear( sizeof *r + 20 ); + r->key = both? pSUBKEYLENGTH : pKEYLENGTH; + sprintf( r->u.value, "%u", nbits); + r->next = para; + para = r; + } + + xfree (curve); } expire = ask_expire_interval(0,NULL); @@ -3630,6 +3712,7 @@ do_generate_keypair (struct para_data_s *para, if (!card) err = do_create (get_parameter_algo( para, pKEYTYPE, NULL ), get_parameter_uint( para, pKEYLENGTH ), + get_parameter_value (para, pKEYCURVE), pub_root, timestamp, get_parameter_u32( para, pKEYEXPIRE ), 0, @@ -3681,6 +3764,7 @@ do_generate_keypair (struct para_data_s *para, { err = do_create (get_parameter_algo (para, pSUBKEYTYPE, NULL), get_parameter_uint (para, pSUBKEYLENGTH), + get_parameter_value (para, pSUBKEYCURVE), pub_root, timestamp, get_parameter_u32 (para, pSUBKEYEXPIRE), 1, @@ -3827,7 +3911,8 @@ generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock) int algo; unsigned int use; u32 expire; - unsigned int nbits; + unsigned int nbits = 0; + char *curve = NULL; u32 cur_time; char *hexgrip = NULL; char *serialno = NULL; @@ -3881,7 +3966,14 @@ generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock) hexgrip = NULL; algo = ask_algo (ctrl, 1, NULL, &use, &hexgrip); assert (algo); - nbits = hexgrip? 0 : ask_keysize (algo, 0); + + if (hexgrip) + nbits = 0; + else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH) + curve = ask_curve (); + else + nbits = ask_keysize (algo, 0); + expire = ask_expire_interval (0, NULL); if (!cpr_enabled() && !cpr_get_answer_is_yes("keygen.sub.okay", _("Really create? (y/N) "))) @@ -3894,7 +3986,8 @@ generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock) err = do_create_from_keygrip (ctrl, algo, hexgrip, keyblock, cur_time, expire, 1); else - err = do_create (algo, nbits, keyblock, cur_time, expire, 1, 0, NULL); + err = do_create (algo, nbits, curve, + keyblock, cur_time, expire, 1, 0, NULL); if (err) goto leave; @@ -3911,6 +4004,7 @@ generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock) write_status_text (STATUS_KEY_CREATED, "S"); leave: + xfree (curve); xfree (hexgrip); xfree (serialno); if (err) diff --git a/g10/keylist.c b/g10/keylist.c index d45aed672..356fac320 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -817,6 +817,17 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr, void *opaque) nbits_from_pk (pk), pubkey_letter (pk->pubkey_algo), keystr_from_pk (pk), datestr_from_pk (pk)); + if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA + || pk->pubkey_algo == PUBKEY_ALGO_ECDH) + { + char *curve = openpgp_oid_to_str (pk->pkey[0]); + const char *name = openpgp_oid_to_curve (curve); + if (!*name || *name == '?') + name = curve; + es_fprintf (es_stdout, " %s", name); + xfree (curve); + } + if (pk->flags.revoked) { es_fprintf (es_stdout, " ["); @@ -940,6 +951,18 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr, void *opaque) s2k_char, nbits_from_pk (pk2), pubkey_letter (pk2->pubkey_algo), keystr_from_pk (pk2), datestr_from_pk (pk2)); + + if (pk2->pubkey_algo == PUBKEY_ALGO_ECDSA + || pk2->pubkey_algo == PUBKEY_ALGO_ECDH) + { + char *curve = openpgp_oid_to_str (pk2->pkey[0]); + const char *name = openpgp_oid_to_curve (curve); + if (!*name || *name == '?') + name = curve; + es_fprintf (es_stdout, " %s", name); + xfree (curve); + } + if (pk2->flags.revoked) { es_fprintf (es_stdout, " ["); @@ -1172,16 +1195,28 @@ list_keyblock_colon (KBNODE keyblock, int secret, int fpr) es_putc (':', es_stdout); es_putc (':', es_stdout); print_capabilities (pk, keyblock); + es_putc (':', es_stdout); /* End of field 13. */ + es_putc (':', es_stdout); /* End of field 14. */ if (secret) { - es_putc (':', es_stdout); /* End of field 13. */ - es_putc (':', es_stdout); /* End of field 14. */ if (stubkey) es_putc ('#', es_stdout); else if (serialno) - es_fputs(serialno, es_stdout); - es_putc (':', es_stdout); /* End of field 15. */ + es_fputs (serialno, es_stdout); } + es_putc (':', es_stdout); /* End of field 15. */ + es_putc (':', es_stdout); /* End of field 16. */ + if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA + || pk->pubkey_algo == PUBKEY_ALGO_ECDH) + { + char *curve = openpgp_oid_to_str (pk->pkey[0]); + const char *name = openpgp_oid_to_curve (curve); + if (!*name || *name == '?') + name = curve; + es_fputs (name, es_stdout); + xfree (curve); + } + es_putc (':', es_stdout); /* End of field 17. */ es_putc ('\n', es_stdout); print_revokers (pk); @@ -1285,16 +1320,28 @@ list_keyblock_colon (KBNODE keyblock, int secret, int fpr) /* fixme: add LID and ownertrust here */ ); print_capabilities (pk2, NULL); + es_putc (':', es_stdout); /* End of field 13. */ + es_putc (':', es_stdout); /* End of field 14. */ if (secret) { - es_putc (':', es_stdout); /* End of field 13. */ - es_putc (':', es_stdout); /* End of field 14. */ if (stubkey) es_putc ('#', es_stdout); else if (serialno) es_fputs (serialno, es_stdout); - es_putc (':', es_stdout); /* End of field 15. */ } + es_putc (':', es_stdout); /* End of field 15. */ + es_putc (':', es_stdout); /* End of field 16. */ + if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA + || pk->pubkey_algo == PUBKEY_ALGO_ECDH) + { + char *curve = openpgp_oid_to_str (pk->pkey[0]); + const char *name = openpgp_oid_to_curve (curve); + if (!*name || *name == '?') + name = curve; + es_fputs (name, es_stdout); + xfree (curve); + } + es_putc (':', es_stdout); /* End of field 17. */ es_putc ('\n', es_stdout); if (fpr > 1) print_fingerprint (pk2, 0); diff --git a/g10/main.h b/g10/main.h index 15d3b7627..fd4e5e9ec 100644 --- a/g10/main.h +++ b/g10/main.h @@ -230,7 +230,6 @@ void keyedit_passwd (ctrl_t ctrl, const char *username); void show_basic_key_info (KBNODE keyblock); /*-- keygen.c --*/ -const char *gpg_curve_to_oid (const char *name, unsigned int *r_nbits); u32 parse_expire_string(const char *string); u32 ask_expire_interval(int object,const char *def_expire); u32 ask_expiredate(void); diff --git a/g10/packet.h b/g10/packet.h index fa32ab194..b3956efb2 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -445,6 +445,7 @@ PACKET *create_gpg_control ( ctrlpkttype_t type, /*-- build-packet.c --*/ int build_packet( iobuf_t inp, PACKET *pkt ); gpg_error_t gpg_mpi_write (iobuf_t out, gcry_mpi_t a); +gpg_error_t gpg_mpi_write_nohdr (iobuf_t out, gcry_mpi_t a); u32 calc_packet_length( PACKET *pkt ); void build_sig_subpkt( PKT_signature *sig, sigsubpkttype_t type, const byte *buffer, size_t buflen ); diff --git a/g10/parse-packet.c b/g10/parse-packet.c index 9c0436231..3b2698fcb 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -140,22 +140,13 @@ mpi_read (iobuf_t inp, unsigned int *ret_nread, int secure) nread++; } - if (nread >= 2 && !(buf[0] << 8 | buf[1])) - { - /* Libgcrypt < 1.5.0 accidently rejects zero-length (i.e. zero) - MPIs. We fix this here. */ - a = gcry_mpi_new (0); - } - else - { - if (gcry_mpi_scan (&a, GCRYMPI_FMT_PGP, buf, nread, &nread)) - a = NULL; - } + if (gcry_mpi_scan (&a, GCRYMPI_FMT_PGP, buf, nread, &nread)) + a = NULL; leave: gcry_free (buf); if (nread > *ret_nread) - log_bug ("mpi larger than packet"); + log_bug ("mpi larger than packet (%zu/%u)", nread, *ret_nread); else *ret_nread = nread; return a; @@ -1999,8 +1990,8 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, { for (i = 0; i < npkey; i++) { - if ((algorithm == PUBKEY_ALGO_ECDSA - || algorithm == PUBKEY_ALGO_ECDH) && (i==0 || i == 2)) + if ((algorithm == PUBKEY_ALGO_ECDSA && (i == 0)) + || (algorithm == PUBKEY_ALGO_ECDH) && (i == 0 || i == 2)) { size_t n; err = read_size_body (inp, pktlen, &n, pk->pkey+i); @@ -2020,6 +2011,14 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, { es_fprintf (listfp, "\tpkey[%d]: ", i); mpi_print (listfp, pk->pkey[i], mpi_print_mode); + if ((algorithm == PUBKEY_ALGO_ECDSA + || algorithm == PUBKEY_ALGO_ECDH) && i==0) + { + char *curve = openpgp_oid_to_str (pk->pkey[0]); + es_fprintf (listfp, " %s (%s)", + openpgp_oid_to_curve (curve), curve); + xfree (curve); + } es_putc ('\n', listfp); } } diff --git a/g10/pkglue.c b/g10/pkglue.c index 3a078bd3f..7e50a1c3d 100644 --- a/g10/pkglue.c +++ b/g10/pkglue.c @@ -33,14 +33,14 @@ /* FIXME: Better chnage the fucntion name because mpi_ is used by gcrypt macros. */ gcry_mpi_t -mpi_from_sexp (gcry_sexp_t sexp, const char * item) +get_mpi_from_sexp (gcry_sexp_t sexp, const char *item, int mpifmt) { gcry_sexp_t list; gcry_mpi_t data; list = gcry_sexp_find_token (sexp, item, 0); assert (list); - data = gcry_sexp_nth_mpi (list, 1, GCRYMPI_FMT_USG); + data = gcry_sexp_nth_mpi (list, 1, mpifmt); assert (data); gcry_sexp_release (list); return data; @@ -58,6 +58,7 @@ pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey) gcry_sexp_t s_sig, s_hash, s_pkey; int rc; const int pkalgo = map_pk_openpgp_to_gcry (algo); + int is_ed25519 = 0; /* Make a sexp from pkey. */ if (pkalgo == GCRY_PK_DSA) @@ -79,15 +80,24 @@ pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey) } else if (pkalgo == GCRY_PK_ECDSA) /* Same as GCRY_PK_ECDH */ { - char *curve = openpgp_oid_to_str (pkey[0]); - if (!curve) - rc = gpg_error_from_syserror (); + is_ed25519 = openpgp_oid_is_ed25519 (pkey[0]); + if (is_ed25519) + rc = gcry_sexp_build (&s_pkey, NULL, + "(public-key(ecc(curve Ed25519)" + "(flags eddsa)(q%m)))", + pkey[1]); else { - rc = gcry_sexp_build (&s_pkey, NULL, - "(public-key(ecdsa(curve %s)(q%m)))", - curve, pkey[1]); - xfree (curve); + char *curve = openpgp_oid_to_str (pkey[0]); + if (!curve) + rc = gpg_error_from_syserror (); + else + { + rc = gcry_sexp_build (&s_pkey, NULL, + "(public-key(ecdsa(curve %s)(q%m)))", + curve, pkey[1]); + xfree (curve); + } } } else @@ -97,8 +107,18 @@ pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey) BUG (); /* gcry_sexp_build should never fail. */ /* Put hash into a S-Exp s_hash. */ - if (gcry_sexp_build (&s_hash, NULL, "%m", hash)) - BUG (); /* gcry_sexp_build should never fail. */ + if (is_ed25519) + { + if (gcry_sexp_build (&s_hash, NULL, + "(data(flags eddsa)(hash-algo sha512)(value %m))", + hash)) + BUG (); /* gcry_sexp_build should never fail. */ + } + else + { + if (gcry_sexp_build (&s_hash, NULL, "%m", hash)) + BUG (); /* gcry_sexp_build should never fail. */ + } /* Put data into a S-Exp s_sig. */ s_sig = NULL; @@ -114,6 +134,9 @@ pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey) { if (!data[0] || !data[1]) rc = gpg_error (GPG_ERR_BAD_MPI); + else if (is_ed25519) + rc = gcry_sexp_build (&s_sig, NULL, + "(sig-val(eddsa(r%M)(s%M)))", data[0], data[1]); else rc = gcry_sexp_build (&s_sig, NULL, "(sig-val(ecdsa(r%m)(s%m)))", data[0], data[1]); @@ -223,8 +246,8 @@ pk_encrypt (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, size_t fpn; /* Get the shared point and the ephemeral public key. */ - shared = mpi_from_sexp (s_ciph, "s"); - public = mpi_from_sexp (s_ciph, "e"); + shared = get_mpi_from_sexp (s_ciph, "s", GCRYMPI_FMT_USG); + public = get_mpi_from_sexp (s_ciph, "e", GCRYMPI_FMT_USG); gcry_sexp_release (s_ciph); s_ciph = NULL; if (DBG_CIPHER) @@ -256,9 +279,9 @@ pk_encrypt (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, else /* Elgamal or RSA case. */ { /* Fixme: Add better error handling or make gnupg use S-expressions directly. */ - resarr[0] = mpi_from_sexp (s_ciph, "a"); + resarr[0] = get_mpi_from_sexp (s_ciph, "a", GCRYMPI_FMT_USG); if (algo != GCRY_PK_RSA && algo != GCRY_PK_RSA_E) - resarr[1] = mpi_from_sexp (s_ciph, "b"); + resarr[1] = get_mpi_from_sexp (s_ciph, "b", GCRYMPI_FMT_USG); } gcry_sexp_release (s_ciph); @@ -296,15 +319,25 @@ pk_check_secret_key (int algo, gcry_mpi_t *skey) } else if (gcry_pkalgo == GCRY_PK_ECDSA || gcry_pkalgo == GCRY_PK_ECDH) { - char *curve = openpgp_oid_to_str (skey[0]); - if (!curve) - rc = gpg_error_from_syserror (); - else + if (openpgp_oid_is_ed25519 (skey[0])) { rc = gcry_sexp_build (&s_skey, NULL, - "(private-key(ecdsa(curve%s)(q%m)(d%m)))", - curve, skey[1], skey[2]); - xfree (curve); + "(private-key(ecc(curve Ed25519)" + "(flags eddsa)(q%m)(d%m)))", + skey[1], skey[2]); + } + else + { + char *curve = openpgp_oid_to_str (skey[0]); + if (!curve) + rc = gpg_error_from_syserror (); + else + { + rc = gcry_sexp_build (&s_skey, NULL, + "(private-key(ecdsa(curve%s)(q%m)(d%m)))", + curve, skey[1], skey[2]); + xfree (curve); + } } } else diff --git a/g10/pkglue.h b/g10/pkglue.h index e5165f73b..48bfbb5e0 100644 --- a/g10/pkglue.h +++ b/g10/pkglue.h @@ -23,7 +23,7 @@ #include "packet.h" /* For PKT_public_key. */ /*-- pkglue.c --*/ -gcry_mpi_t mpi_from_sexp (gcry_sexp_t sexp, const char * item); +gcry_mpi_t get_mpi_from_sexp (gcry_sexp_t sexp, const char *item, int mpifmt); int pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey); diff --git a/g10/seskey.c b/g10/seskey.c index ac6e6d6e9..e7f499731 100644 --- a/g10/seskey.c +++ b/g10/seskey.c @@ -264,7 +264,12 @@ encode_md_value (PKT_public_key *pk, gcry_md_hd_t md, int hash_algo) pkalgo = map_pk_openpgp_to_gcry (pk->pubkey_algo); - if (pkalgo == GCRY_PK_DSA || pkalgo == GCRY_PK_ECDSA) + if (pkalgo == GCRY_PK_ECDSA && openpgp_oid_is_ed25519 (pk->pkey[0])) + { + frame = gcry_mpi_set_opaque_copy (NULL, gcry_md_read (md, hash_algo), + 8*gcry_md_get_algo_dlen (hash_algo)); + } + else if (pkalgo == GCRY_PK_DSA || pkalgo == GCRY_PK_ECDSA) { /* It's a DSA signature, so find out the size of q. */ diff --git a/g10/sign.c b/g10/sign.c index 8944067d7..cfac5de76 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -281,11 +281,16 @@ do_sign (PKT_public_key *pksk, PKT_signature *sig, ; else if (pksk->pubkey_algo == GCRY_PK_RSA || pksk->pubkey_algo == GCRY_PK_RSA_S) - sig->data[0] = mpi_from_sexp (s_sigval, "s"); + sig->data[0] = get_mpi_from_sexp (s_sigval, "s", GCRYMPI_FMT_USG); + else if (openpgp_oid_is_ed25519 (pksk->pkey[0])) + { + sig->data[0] = get_mpi_from_sexp (s_sigval, "r", GCRYMPI_FMT_OPAQUE); + sig->data[1] = get_mpi_from_sexp (s_sigval, "s", GCRYMPI_FMT_OPAQUE); + } else { - sig->data[0] = mpi_from_sexp (s_sigval, "r"); - sig->data[1] = mpi_from_sexp (s_sigval, "s"); + sig->data[0] = get_mpi_from_sexp (s_sigval, "r", GCRYMPI_FMT_USG); + sig->data[1] = get_mpi_from_sexp (s_sigval, "s", GCRYMPI_FMT_USG); } gcry_sexp_release (s_sigval); @@ -422,6 +427,10 @@ match_dsa_hash (unsigned int qbytes) usable for the pubkey algorithm. If --preferred-digest-prefs isn't set, then take the OpenPGP default (i.e. SHA-1). + Note that Ed25519+EdDSA takes an input of arbitrary length and thus + we don't enforce any particular algorithm like we do for standard + ECDSA. However, we use SHA256 as the default algorithm. + Possible improvement: Use the highest-ranked usable algorithm from the signing key prefs either before or after using the personal list? @@ -437,6 +446,14 @@ hash_for (PKT_public_key *pk) { return recipient_digest_algo; } + else if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA + && openpgp_oid_is_ed25519 (pk->pkey[0])) + { + if (opt.personal_digest_prefs) + return opt.personal_digest_prefs[0].value; + else + return DIGEST_ALGO_SHA256; + } else if (pk->pubkey_algo == PUBKEY_ALGO_DSA || pk->pubkey_algo == PUBKEY_ALGO_ECDSA) { @@ -927,7 +944,8 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) { if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_DSA - || sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA) + || (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA + && !openpgp_oid_is_ed25519 (sk_rover->pk->pkey[1]))) { int temp_hashlen = (gcry_mpi_get_nbits (sk_rover->pk->pkey[1])); @@ -1492,8 +1510,13 @@ make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk, else if(pksk->pubkey_algo == PUBKEY_ALGO_DSA) digest_algo = match_dsa_hash (gcry_mpi_get_nbits (pksk->pkey[1])/8); else if(pksk->pubkey_algo == PUBKEY_ALGO_ECDSA ) - digest_algo = match_dsa_hash (ecdsa_qbits_from_Q - (gcry_mpi_get_nbits (pksk->pkey[1]))/8); + { + if (openpgp_oid_is_ed25519 (pksk->pkey[0])) + digest_algo = DIGEST_ALGO_SHA256; + else + digest_algo = match_dsa_hash + (ecdsa_qbits_from_Q (gcry_mpi_get_nbits (pksk->pkey[1]))/8); + } else digest_algo = DIGEST_ALGO_SHA1; }