diff --git a/agent/pksign.c b/agent/pksign.c --- a/agent/pksign.c +++ b/agent/pksign.c @@ -129,15 +129,20 @@ /* 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 @@ /* 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 --- a/agent/sexp-secret.c +++ b/agent/sexp-secret.c @@ -83,6 +83,7 @@ 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 --- a/common/openpgp-oid.c +++ b/common/openpgp-oid.c @@ -49,6 +49,7 @@ { "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 --- a/common/sexputil.c +++ b/common/sexputil.c @@ -624,9 +624,10 @@ 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 @@ } } 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 --- a/g10/export.c +++ b/g10/export.c @@ -568,6 +568,8 @@ 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 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -1750,16 +1750,25 @@ 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 @@ { 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 @@ { 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 @@ * 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 --- a/g10/pkglue.c +++ b/g10/pkglue.c @@ -131,7 +131,6 @@ { 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 @@ 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 @@ /* 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 @@ { 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; - - 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; + size_t rlen, slen, n; /* (bytes) */ + char buf[64]; + unsigned int nbits; + unsigned int neededfixedlen = 256 / 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; + log_assert (neededfixedlen <= sizeof buf); - 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 (!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 + { + /* 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; + + 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 (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); + } } + } + else + rc = 0; - if (!rc) - rc = gcry_sexp_build (&s_sig, NULL, - "(sig-val(eddsa(r%M)(s%M)))", r, s); + 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); - } + 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 @@ 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 --- a/g10/sign.c +++ b/g10/sign.c @@ -630,7 +630,7 @@ 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 @@ { 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 @@ 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;