From a763bb2580b0d586a80b8ccd3654f41e49604f4f Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Wed, 24 Jun 2020 10:05:03 +0900 Subject: [PATCH] gpg,agent: Support Ed448 signing. * agent/pksign.c (do_encode_eddsa): First argument is NBITs, so that it can support Ed448, as well as Ed25519. (agent_pksign_do): Follow the change. * agent/sexp-secret.c (fixup_when_ecc_private_key): No fix-up needed for Ed448, it's only for classic curves. * common/openpgp-oid.c (oidtable): Add Ed448. * common/sexputil.c (get_pk_algo_from_key): Ed448 is only for EdDSA. * g10/export.c (match_curve_skey_pk): Ed448 is for EdDSA. * g10/keygen.c (gen_ecc): Support Ed448 with the name of "ed448". (ask_algo, parse_key_parameter_part): Handle "ed448". * g10/pkglue.c (pk_verify): Support Ed448. (pk_check_secret_key): Support Ed448. * g10/sign.c (hash_for): Defaults to SHA512 for Ed448. (make_keysig_packet): Likewise. Signed-off-by: NIIBE Yutaka --- agent/pksign.c | 15 ++-- agent/sexp-secret.c | 1 + common/openpgp-oid.c | 1 + common/sexputil.c | 9 ++- g10/export.c | 2 + g10/keygen.c | 16 +++- g10/pkglue.c | 178 +++++++++++++++++++++++-------------------- g10/sign.c | 23 +++--- 8 files changed, 147 insertions(+), 98 deletions(-) diff --git a/agent/pksign.c b/agent/pksign.c index a2d5362be..0640b04ef 100644 --- a/agent/pksign.c +++ b/agent/pksign.c @@ -129,15 +129,20 @@ 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) +do_encode_eddsa (size_t nbits, const byte *md, size_t mdlen, + gcry_sexp_t *r_hash) { gpg_error_t err; gcry_sexp_t hash; + const char *fmt; + + if (nbits == 448) + fmt = "(data(value %b))"; + else + fmt = "(data(flags eddsa)(hash-algo sha512)(value %b))"; *r_hash = NULL; - err = gcry_sexp_build (&hash, NULL, - "(data(flags eddsa)(hash-algo sha512)(value %b))", - (int)mdlen, md); + err = gcry_sexp_build (&hash, NULL, fmt, (int)mdlen, md); if (!err) *r_hash = hash; return err; @@ -482,7 +487,7 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce, /* Put the hash into a sexp */ if (algo == GCRY_PK_EDDSA) - err = do_encode_eddsa (data, datalen, + err = do_encode_eddsa (gcry_pk_get_nbits (s_skey), data, datalen, &s_hash); else if (ctrl->digest.algo == MD_USER_TLS_MD5SHA1) err = do_encode_raw_pkcs1 (data, datalen, diff --git a/agent/sexp-secret.c b/agent/sexp-secret.c index d67836769..b539659e2 100644 --- a/agent/sexp-secret.c +++ b/agent/sexp-secret.c @@ -83,6 +83,7 @@ fixup_when_ecc_private_key (unsigned char *buf, size_t *buflen_p) return gpg_error (GPG_ERR_INV_SEXP); else if (!*s /* Leading 0x00 added at the front for classic curve */ && strcmp (curve_name, "Ed25519") + && strcmp (curve_name, "Ed448") && strcmp (curve_name, "X448")) { size_t numsize; diff --git a/common/openpgp-oid.c b/common/openpgp-oid.c index 605caa679..4e53a74fd 100644 --- a/common/openpgp-oid.c +++ b/common/openpgp-oid.c @@ -49,6 +49,7 @@ static struct { { "Curve25519", "1.3.6.1.4.1.3029.1.5.1", 255, "cv25519", PUBKEY_ALGO_ECDH }, { "Ed25519", "1.3.6.1.4.1.11591.15.1", 255, "ed25519", PUBKEY_ALGO_EDDSA }, { "X448", "1.3.101.111", 448, "cv448", PUBKEY_ALGO_ECDH }, + { "Ed448", "1.3.101.113", 448, "ed448", PUBKEY_ALGO_EDDSA }, { "NIST P-256", "1.2.840.10045.3.1.7", 256, "nistp256" }, { "NIST P-384", "1.3.132.0.34", 384, "nistp384" }, diff --git a/common/sexputil.c b/common/sexputil.c index b007b71f6..981a06664 100644 --- a/common/sexputil.c +++ b/common/sexputil.c @@ -624,9 +624,10 @@ get_pk_algo_from_key (gcry_sexp_t key) algo = gcry_pk_map_name (algoname); if (algo == GCRY_PK_ECC) { - gcry_sexp_t l1 = gcry_sexp_find_token (list, "flags", 0); + gcry_sexp_t l1; int i; + l1 = gcry_sexp_find_token (list, "flags", 0); for (i = l1 ? gcry_sexp_length (l1)-1 : 0; i > 0; i--) { s = gcry_sexp_nth_data (l1, i, &n); @@ -640,6 +641,12 @@ get_pk_algo_from_key (gcry_sexp_t key) } } gcry_sexp_release (l1); + + l1 = gcry_sexp_find_token (list, "curve", 0); + s = gcry_sexp_nth_data (l1, 1, &n); + if (n == 5 && !memcmp (s, "Ed448", 5)) + algo = GCRY_PK_EDDSA; + gcry_sexp_release (l1); } out: diff --git a/g10/export.c b/g10/export.c index d06896026..909b98701 100644 --- a/g10/export.c +++ b/g10/export.c @@ -568,6 +568,8 @@ match_curve_skey_pk (gcry_sexp_t s_key, PKT_public_key *pk) log_error ("no curve name\n"); return gpg_error (GPG_ERR_UNKNOWN_CURVE); } + if (!strcmp (curve_str, "Ed448")) + is_eddsa = 1; oidstr = openpgp_curve_to_oid (curve_str, NULL, NULL); if (!oidstr) { diff --git a/g10/keygen.c b/g10/keygen.c index 590536726..d87b8480a 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -1750,16 +1750,25 @@ gen_ecc (int algo, const char *curve, kbnode_t pub_root, curve = "Ed25519"; else if (!ascii_strcasecmp (curve, "cv448")) curve = "X448"; + else if (!ascii_strcasecmp (curve, "ed448")) + curve = "Ed448"; /* Note that we use the "comp" flag with EdDSA to request the use of a 0x40 compression prefix octet. */ - if (algo == PUBKEY_ALGO_EDDSA) + if (algo == PUBKEY_ALGO_EDDSA && !strcmp (curve, "Ed25519")) keyparms = xtryasprintf ("(genkey(ecc(curve %zu:%s)(flags eddsa comp%s)))", strlen (curve), curve, (((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? " transient-key" : "")); + else if (algo == PUBKEY_ALGO_EDDSA && !strcmp (curve, "Ed448")) + keyparms = xtryasprintf + ("(genkey(ecc(curve %zu:%s)(flags comp%s)))", + strlen (curve), curve, + (((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)))", @@ -2326,6 +2335,8 @@ ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage, { if (!strcmp (algostr, "ed25519")) kpi->algo = PUBKEY_ALGO_EDDSA; + else if (!strcmp (algostr, "ed448")) + kpi->algo = PUBKEY_ALGO_EDDSA; else if (!strcmp (algostr, "cv25519")) kpi->algo = PUBKEY_ALGO_ECDH; else if (!strcmp (algostr, "cv448")) @@ -3481,6 +3492,8 @@ parse_key_parameter_part (ctrl_t ctrl, { if (!strcmp (algostr, "ed25519")) algo = PUBKEY_ALGO_EDDSA; + else if (!strcmp (algostr, "ed448")) + kpi->algo = PUBKEY_ALGO_EDDSA; else if (!strcmp (algostr, "cv25519")) algo = PUBKEY_ALGO_ECDH; else if (!strcmp (algostr, "cv448")) @@ -3611,6 +3624,7 @@ parse_key_parameter_part (ctrl_t ctrl, * dsa2048 := DSA with 2048 bit. * elg2048 := Elgamal with 2048 bit. * ed25519 := EDDSA using curve Ed25519. + * ed448 := EDDSA using curve Ed448. * cv25519 := ECDH using curve Curve25519. * cv448 := ECDH using curve X448. * nistp256:= ECDSA or ECDH using curve NIST P-256 diff --git a/g10/pkglue.c b/g10/pkglue.c index eda13009b..cd6e90838 100644 --- a/g10/pkglue.c +++ b/g10/pkglue.c @@ -131,7 +131,6 @@ pk_verify (pubkey_algo_t pkalgo, gcry_mpi_t hash, { gcry_sexp_t s_sig, s_hash, s_pkey; int rc; - unsigned int neededfixedlen = 0; /* Make a sexp from pkey. */ if (pkalgo == PUBKEY_ALGO_DSA) @@ -171,15 +170,16 @@ pk_verify (pubkey_algo_t pkalgo, gcry_mpi_t hash, rc = gpg_error_from_syserror (); else { - rc = gcry_sexp_build (&s_pkey, NULL, - "(public-key(ecc(curve %s)" - "(flags eddsa)(q%m)))", - curve, pkey[1]); + const char *fmt; + + if (openpgp_oid_is_ed25519 (pkey[0])) + fmt = "(public-key(ecc(curve %s)(flags eddsa)(q%m)))"; + else + fmt = "(public-key(ecc(curve %s)(q%m)))"; + + rc = gcry_sexp_build (&s_pkey, NULL, fmt, curve, pkey[1]); xfree (curve); } - - if (openpgp_oid_is_ed25519 (pkey[0])) - neededfixedlen = 256 / 8; } else return GPG_ERR_PUBKEY_ALGO; @@ -190,9 +190,14 @@ pk_verify (pubkey_algo_t pkalgo, gcry_mpi_t hash, /* Put hash into a S-Exp s_hash. */ if (pkalgo == PUBKEY_ALGO_EDDSA) { - if (gcry_sexp_build (&s_hash, NULL, - "(data(flags eddsa)(hash-algo sha512)(value %m))", - hash)) + const char *fmt; + + if (openpgp_oid_is_ed25519 (pkey[0])) + fmt = "(data(flags eddsa)(hash-algo sha512)(value %m))"; + else + fmt = "(data(value %m))"; + + if (gcry_sexp_build (&s_hash, NULL, fmt, hash)) BUG (); /* gcry_sexp_build should never fail. */ } else @@ -223,80 +228,87 @@ pk_verify (pubkey_algo_t pkalgo, gcry_mpi_t hash, { gcry_mpi_t r = data[0]; gcry_mpi_t s = data[1]; - size_t rlen, slen, n; /* (bytes) */ - char buf[64]; - unsigned int nbits; - log_assert (neededfixedlen <= sizeof buf); - - if (!r || !s) - rc = gpg_error (GPG_ERR_BAD_MPI); - else if ((rlen = (gcry_mpi_get_nbits (r)+7)/8) > neededfixedlen || !rlen) - rc = gpg_error (GPG_ERR_BAD_MPI); - else if ((slen = (gcry_mpi_get_nbits (s)+7)/8) > neededfixedlen || !slen) - rc = gpg_error (GPG_ERR_BAD_MPI); - else + if (openpgp_oid_is_ed25519 (pkey[0])) { - /* We need to fixup the length in case of leading zeroes. - * OpenPGP does not allow leading zeroes and the parser for - * the signature packet has no information on the use curve, - * thus we need to do it here. We won't do it for opaque - * MPIs under the assumption that they are known to be fine; - * we won't see them here anyway but the check is anyway - * required. Fixme: A nifty feature for gcry_sexp_build - * would be a format to left pad the value (e.g. "%*M"). */ - rc = 0; + size_t rlen, slen, n; /* (bytes) */ + char buf[64]; + unsigned int nbits; + unsigned int neededfixedlen = 256 / 8; - if (rlen < neededfixedlen - && !gcry_mpi_get_flag (r, GCRYMPI_FLAG_OPAQUE) - && !(rc=gcry_mpi_print (GCRYMPI_FMT_USG, buf, sizeof buf, &n, r))) + log_assert (neededfixedlen <= sizeof buf); + + if (!r || !s) + rc = gpg_error (GPG_ERR_BAD_MPI); + else if ((rlen = (gcry_mpi_get_nbits (r)+7)/8) > neededfixedlen || !rlen) + rc = gpg_error (GPG_ERR_BAD_MPI); + else if ((slen = (gcry_mpi_get_nbits (s)+7)/8) > neededfixedlen || !slen) + rc = gpg_error (GPG_ERR_BAD_MPI); + else { - log_assert (n < neededfixedlen); - memmove (buf + (neededfixedlen - n), buf, n); - memset (buf, 0, neededfixedlen - n); - r = gcry_mpi_set_opaque_copy (NULL, buf, neededfixedlen * 8); - } - else if (rlen < neededfixedlen - && gcry_mpi_get_flag (r, GCRYMPI_FLAG_OPAQUE)) - { - const unsigned char *p; + /* We need to fixup the length in case of leading zeroes. + * OpenPGP does not allow leading zeroes and the parser for + * the signature packet has no information on the use curve, + * thus we need to do it here. We won't do it for opaque + * MPIs under the assumption that they are known to be fine; + * we won't see them here anyway but the check is anyway + * required. Fixme: A nifty feature for gcry_sexp_build + * would be a format to left pad the value (e.g. "%*M"). */ + rc = 0; - p = gcry_mpi_get_opaque (r, &nbits); - n = (nbits+7)/8; - memcpy (buf + (neededfixedlen - n), p, n); - memset (buf, 0, neededfixedlen - n); - gcry_mpi_set_opaque_copy (r, buf, neededfixedlen * 8); - } - if (slen < neededfixedlen - && !gcry_mpi_get_flag (s, GCRYMPI_FLAG_OPAQUE) - && !(rc=gcry_mpi_print (GCRYMPI_FMT_USG, buf, sizeof buf, &n, s))) - { - log_assert (n < neededfixedlen); - memmove (buf + (neededfixedlen - n), buf, n); - memset (buf, 0, neededfixedlen - n); - s = gcry_mpi_set_opaque_copy (NULL, buf, neededfixedlen * 8); - } - else if (slen < neededfixedlen - && gcry_mpi_get_flag (s, GCRYMPI_FLAG_OPAQUE)) - { - const unsigned char *p; + if (rlen < neededfixedlen + && !gcry_mpi_get_flag (r, GCRYMPI_FLAG_OPAQUE) + && !(rc=gcry_mpi_print (GCRYMPI_FMT_USG, buf, sizeof buf, &n, r))) + { + log_assert (n < neededfixedlen); + memmove (buf + (neededfixedlen - n), buf, n); + memset (buf, 0, neededfixedlen - n); + r = gcry_mpi_set_opaque_copy (NULL, buf, neededfixedlen * 8); + } + else if (rlen < neededfixedlen + && gcry_mpi_get_flag (r, GCRYMPI_FLAG_OPAQUE)) + { + const unsigned char *p; - p = gcry_mpi_get_opaque (s, &nbits); - n = (nbits+7)/8; - memcpy (buf + (neededfixedlen - n), p, n); - memset (buf, 0, neededfixedlen - n); - gcry_mpi_set_opaque_copy (s, buf, neededfixedlen * 8); + p = gcry_mpi_get_opaque (r, &nbits); + n = (nbits+7)/8; + memcpy (buf + (neededfixedlen - n), p, n); + memset (buf, 0, neededfixedlen - n); + gcry_mpi_set_opaque_copy (r, buf, neededfixedlen * 8); + } + if (slen < neededfixedlen + && !gcry_mpi_get_flag (s, GCRYMPI_FLAG_OPAQUE) + && !(rc=gcry_mpi_print (GCRYMPI_FMT_USG, buf, sizeof buf, &n, s))) + { + log_assert (n < neededfixedlen); + memmove (buf + (neededfixedlen - n), buf, n); + memset (buf, 0, neededfixedlen - n); + s = gcry_mpi_set_opaque_copy (NULL, buf, neededfixedlen * 8); + } + else if (slen < neededfixedlen + && gcry_mpi_get_flag (s, GCRYMPI_FLAG_OPAQUE)) + { + const unsigned char *p; + + p = gcry_mpi_get_opaque (s, &nbits); + n = (nbits+7)/8; + memcpy (buf + (neededfixedlen - n), p, n); + memset (buf, 0, neededfixedlen - n); + gcry_mpi_set_opaque_copy (s, buf, neededfixedlen * 8); + } } - - if (!rc) - rc = gcry_sexp_build (&s_sig, NULL, - "(sig-val(eddsa(r%M)(s%M)))", r, s); - - if (r != data[0]) - gcry_mpi_release (r); - if (s != data[1]) - gcry_mpi_release (s); } + else + rc = 0; + + if (!rc) + rc = gcry_sexp_build (&s_sig, NULL, + "(sig-val(eddsa(r%M)(s%M)))", r, s); + + if (r != data[0]) + gcry_mpi_release (r); + if (s != data[1]) + gcry_mpi_release (s); } else if (pkalgo == PUBKEY_ALGO_ELGAMAL || pkalgo == PUBKEY_ALGO_ELGAMAL_E) { @@ -506,10 +518,14 @@ pk_check_secret_key (pubkey_algo_t pkalgo, gcry_mpi_t *skey) rc = gpg_error_from_syserror (); else { - rc = gcry_sexp_build (&s_skey, NULL, - "(private-key(ecc(curve %s)" - "(flags eddsa)(q%m)(d%m)))", - curve, skey[1], skey[2]); + const char *fmt; + + if (openpgp_oid_is_ed25519 (skey[0])) + fmt = "(private-key(ecc(curve %s)(flags eddsa)(q%m)(d%m)))"; + else + fmt = "(private-key(ecc(curve %s)(q%m)(d%m)))"; + + rc = gcry_sexp_build (&s_skey, NULL, fmt, curve, skey[1], skey[2]); xfree (curve); } } diff --git a/g10/sign.c b/g10/sign.c index 8f72d8102..d6c938acc 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -630,7 +630,7 @@ match_dsa_hash (unsigned int qbytes) usable for the pubkey algorithm. If --personal-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 + Note that 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. @@ -649,13 +649,15 @@ hash_for (PKT_public_key *pk) { return recipient_digest_algo; } - else if (pk->pubkey_algo == PUBKEY_ALGO_EDDSA - && openpgp_oid_is_ed25519 (pk->pkey[0])) + else if (pk->pubkey_algo == PUBKEY_ALGO_EDDSA) { if (opt.personal_digest_prefs) return opt.personal_digest_prefs[0].value; else - return DIGEST_ALGO_SHA256; + if (gcry_mpi_get_nbits (pk->pkey[1]) > 256) + return DIGEST_ALGO_SHA512; + else + return DIGEST_ALGO_SHA256; } else if (pk->pubkey_algo == PUBKEY_ALGO_DSA || pk->pubkey_algo == PUBKEY_ALGO_ECDSA) @@ -1742,14 +1744,15 @@ make_keysig_packet (ctrl_t ctrl, digest_algo = opt.cert_digest_algo; else if (pksk->pubkey_algo == PUBKEY_ALGO_DSA) /* Meet DSA requirements. */ digest_algo = match_dsa_hash (gcry_mpi_get_nbits (pksk->pkey[1])/8); - else if (pksk->pubkey_algo == PUBKEY_ALGO_ECDSA /* Meet ECDSA requirements. */ - || pksk->pubkey_algo == PUBKEY_ALGO_EDDSA) + else if (pksk->pubkey_algo == PUBKEY_ALGO_ECDSA) /* Meet ECDSA requirements. */ + digest_algo = match_dsa_hash + (ecdsa_qbits_from_Q (gcry_mpi_get_nbits (pksk->pkey[1]))/8); + else if (pksk->pubkey_algo == PUBKEY_ALGO_EDDSA) { - if (openpgp_oid_is_ed25519 (pksk->pkey[0])) - digest_algo = DIGEST_ALGO_SHA256; + if (gcry_mpi_get_nbits (pksk->pkey[1]) > 256) + digest_algo = DIGEST_ALGO_SHA512; else - digest_algo = match_dsa_hash - (ecdsa_qbits_from_Q (gcry_mpi_get_nbits (pksk->pkey[1]))/8); + digest_algo = DIGEST_ALGO_SHA256; } else /* Use the default. */ digest_algo = DEFAULT_DIGEST_ALGO;