From e9760eb9e70b9804c988dafe01851f6600869d9e Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Tue, 9 Jun 2020 14:56:50 +0900 Subject: [PATCH] gpg: Add X448 support. * common/openpgp-oid.c (oidtable): Add X448. (oid_x448,openpgp_oidbuf_is_x448,openpgp_oid_is_x448): New. * common/util.h (openpgp_oid_is_x448): New. * g10/ecdh.c (gen_k): Add handling of opaque MPI and support endianness. (pk_ecdh_generate_ephemeral_key): X448 requires opaque MPI. * g10/keygen.c (gen_ecc): Add support for X448. (ask_algo, parse_key_parameter_part): Likewise. Signed-off-by: NIIBE Yutaka --- common/openpgp-oid.c | 28 ++++++++++++++++++++++++++++ common/util.h | 1 + g10/ecdh.c | 29 +++++++++++++++++++++++++++-- g10/keygen.c | 14 ++++++++++++++ 4 files changed, 70 insertions(+), 2 deletions(-) diff --git a/common/openpgp-oid.c b/common/openpgp-oid.c index 55f8f432d..8404b01a5 100644 --- a/common/openpgp-oid.c +++ b/common/openpgp-oid.c @@ -48,6 +48,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, "x448", PUBKEY_ALGO_ECDH }, { "NIST P-256", "1.2.840.10045.3.1.7", 256, "nistp256" }, { "NIST P-384", "1.3.132.0.34", 384, "nistp384" }, @@ -71,6 +72,9 @@ static const char oid_ed25519[] = static const char oid_cv25519[] = { 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01 }; +/* The OID for X448 in OpenPGP format. */ +static const char oid_x448[] = { 0x03, 0x2b, 0x65, 0x6f }; + /* A table to store keyalgo strings like "rsa2048 or "ed25519" so that * we do not need to allocate them. This is currently a simple array * but may eventually be changed to a fast data structure. Noet that @@ -334,6 +338,15 @@ openpgp_oidbuf_is_cv25519 (const void *buf, size_t len) } +/* Return true if (BUF,LEN) represents the OID for X448. */ +static int +openpgp_oidbuf_is_x448 (const void *buf, size_t len) +{ + return (buf && len == DIM (oid_x448) + && !memcmp (buf, oid_x448, DIM (oid_x448))); +} + + /* Return true if the MPI A represents the OID for Curve25519. */ int openpgp_oid_is_cv25519 (gcry_mpi_t a) @@ -349,6 +362,21 @@ openpgp_oid_is_cv25519 (gcry_mpi_t a) } +/* Return true if the MPI A represents the OID for X448. */ +int +openpgp_oid_is_x448 (gcry_mpi_t a) +{ + const unsigned char *buf; + unsigned int nbits; + + if (!a || !gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) + return 0; + + buf = gcry_mpi_get_opaque (a, &nbits); + return openpgp_oidbuf_is_x448 (buf, (nbits+7)/8); +} + + /* 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. If R_ALGO is not NULL and a specific ECC algorithm is diff --git a/common/util.h b/common/util.h index 834438448..d8914772d 100644 --- a/common/util.h +++ b/common/util.h @@ -238,6 +238,7 @@ int openpgp_oidbuf_is_ed25519 (const void *buf, size_t len); int openpgp_oid_is_ed25519 (gcry_mpi_t a); int openpgp_oidbuf_is_cv25519 (const void *buf, size_t len); int openpgp_oid_is_cv25519 (gcry_mpi_t a); +int openpgp_oid_is_x448 (gcry_mpi_t a); const char *openpgp_curve_to_oid (const char *name, unsigned int *r_nbits, int *r_algo); const char *openpgp_oid_to_curve (const char *oid, int canon); diff --git a/g10/ecdh.c b/g10/ecdh.c index dd9989bca..d6c30c1ca 100644 --- a/g10/ecdh.c +++ b/g10/ecdh.c @@ -422,10 +422,27 @@ pk_ecdh_encrypt_with_shared_point (gcry_mpi_t shared_mpi, static gcry_mpi_t -gen_k (unsigned nbits) +gen_k (unsigned nbits, int little_endian, int is_opaque) { gcry_mpi_t k; + if (is_opaque) + { + unsigned char *p; + size_t nbytes = (nbits+7)/8; + + p = gcry_random_bytes_secure (nbytes, GCRY_STRONG_RANDOM); + if ((nbits % 8)) + { + if (little_endian) + p[nbytes-1] &= ((1 << (nbits % 8)) - 1); + else + p[0] &= ((1 << (nbits % 8)) - 1); + } + k = gcry_mpi_set_opaque (NULL, p, nbits); + return k; + } + k = gcry_mpi_snew (nbits); if (DBG_CRYPTO) log_debug ("choosing a random k of %u bits\n", nbits); @@ -453,13 +470,21 @@ pk_ecdh_generate_ephemeral_key (gcry_mpi_t *pkey, gcry_mpi_t *r_k) { unsigned int nbits; gcry_mpi_t k; + int is_little_endian = 0; + int require_opaque = 0; + + if (openpgp_oid_is_x448 (pkey[0])) + { + is_little_endian = 1; + require_opaque = 1; + } *r_k = NULL; nbits = pubkey_nbits (PUBKEY_ALGO_ECDH, pkey); if (!nbits) return gpg_error (GPG_ERR_TOO_SHORT); - k = gen_k (nbits); + k = gen_k (nbits, is_little_endian, require_opaque); if (!k) BUG (); diff --git a/g10/keygen.c b/g10/keygen.c index e25d4d925..aab5929ef 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -1748,6 +1748,8 @@ gen_ecc (int algo, const char *curve, kbnode_t pub_root, curve = "Curve25519"; else if (!ascii_strcasecmp (curve, "ed25519")) curve = "Ed25519"; + else if (!ascii_strcasecmp (curve, "x448")) + curve = "X448"; /* Note that we use the "comp" flag with EdDSA to request the use of a 0x40 compression prefix octet. */ @@ -1765,6 +1767,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, "X448")) + 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 keyparms = xtryasprintf ("(genkey(ecc(curve %zu:%s)(flags nocomp%s)))", @@ -2319,6 +2328,8 @@ ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage, kpi->algo = PUBKEY_ALGO_EDDSA; else if (!strcmp (algostr, "cv25519")) kpi->algo = PUBKEY_ALGO_ECDH; + else if (!strcmp (algostr, "x448")) + kpi->algo = PUBKEY_ALGO_ECDH; else if ((kpi->usage & GCRY_PK_USAGE_ENCR)) kpi->algo = PUBKEY_ALGO_ECDH; else @@ -3472,6 +3483,8 @@ parse_key_parameter_part (ctrl_t ctrl, algo = PUBKEY_ALGO_EDDSA; else if (!strcmp (algostr, "cv25519")) algo = PUBKEY_ALGO_ECDH; + else if (!strcmp (algostr, "x448")) + algo = PUBKEY_ALGO_ECDH; else if ((kpi->usage & GCRY_PK_USAGE_ENCR)) algo = PUBKEY_ALGO_ECDH; else @@ -3599,6 +3612,7 @@ parse_key_parameter_part (ctrl_t ctrl, * elg2048 := Elgamal with 2048 bit. * ed25519 := EDDSA using curve Ed25519. * cv25519 := ECDH using curve Curve25519. + * x448 := ECDH using curve X448. * nistp256:= ECDSA or ECDH using curve NIST P-256 * * All strings with an unknown prefix are considered an elliptic