diff options
author | Werner Koch <[email protected]> | 2024-04-03 16:00:44 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2024-04-03 16:01:11 +0000 |
commit | 97f5159495146ce29d2011a07d493adc7c1b4086 (patch) | |
tree | 0a287f72cac848e9ddafa1870c68d4bdf1560fba /g10/keygen.c | |
parent | common: Extend openpgp_oid_to_curve to return an abbreviated name. (diff) | |
download | gnupg-97f5159495146ce29d2011a07d493adc7c1b4086.tar.gz gnupg-97f5159495146ce29d2011a07d493adc7c1b4086.zip |
gpg: Initial support for generating Kyber subkeys.
* common/openpgpdefs.h (PUBKEY_ALGO_KY768_25519): Remove.
(PUBKEY_ALGO_KY1024_448): Remove.
(PUBKEY_ALGO_KYBER): New. Use them everywhere instead of the removed.
* g10/build-packet.c (gpg_mpi_write_nohdr): Rename to
(gpg_mpi_write_opaque_nohdr): this. Change callers.
(gpg_mpi_write_opaque_32): New.
(do_key): Support Kyber keys using the revised format.
* g10/gpg.h (MAX_EXTERN_KEYPARM_BITS): New.
* g10/parse-packet.c (read_octet_string): Add arg nbytes so support
reading with a length prefix. Adjust callers.
(parse_key): Parse Kyber public keys.
* g10/misc.c (pubkey_get_npkey): Support Kyber.
(pubkey_get_nskey): Ditto.
* g10/keyid.c (pubkey_string): Support dual algorithms.
(do_hash_public_key): Support Kyber.
(nbits_from_pk): Ditto.
(keygrip_from_pk): Return the Kyber part for the ECC+Kyber dual algo.
* g10/keygen.c (struct common_gen_cb_parm_s): Add genkey_result2.
Note that this callback is not yet used.
(ecckey_from_sexp): Add optional arg sexp2 and use it for Kyber.
Change callers.
(ecckey_from_sexp): Do not leak LIST in case of an error.
(common_gen): Add arg keyparms2, change callers, and support Kyber.
(gen_kyber): New.
(get_keysize_range): Support Kyber.
(fixup_keysize): Simplify and support Kyber.
(do_create): Handle Kyber.
(parse_key_parameter_part): Remove algo strings "ky768" and "ky1024"
and add a generic "kyber" with default parameters.
--
This uses a revised format which is more aligned with the usual
OpenPGP structure. A lot of things are still missing. For example
support for handling two keygrips and checking both of them in a -K
listing. There is also only ky768_bp384 as fixed algorithm for now.
No passphrase for the Kyber part of the dual algorithm is on purpose.
A test was done using
gpg --quick-gen-key pqc1 nistp256
and then running
gpg -v --quick-add-key <fingerprint> kyber
which creates a v5 subkey on a v4 primary key. A second test using
gpg --quick-gen-key pqc2 Ed448
followed by a --quick-add-key created a v5 key with a v5 subkey.
GnuPG-bug-id: 6815
Diffstat (limited to 'g10/keygen.c')
-rw-r--r-- | g10/keygen.c | 249 |
1 files changed, 203 insertions, 46 deletions
diff --git a/g10/keygen.c b/g10/keygen.c index c98deb635..8df8294d6 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -139,6 +139,9 @@ struct common_gen_cb_parm_s * may take a copy of this so that the result can be used after we * are back from the deep key generation call stack. */ gcry_sexp_t genkey_result; + /* For a dual algorithms the result of the second algorithm + * (e.g. Kyber). */ + gcry_sexp_t genkey_result2; }; typedef struct common_gen_cb_parm_s *common_gen_cb_parm_t; @@ -1311,8 +1314,12 @@ curve_is_448 (gcry_sexp_t sexp) } +/* Extract the parameters in OpenPGP format from SEXP and put them + * into the caller provided ARRAY. SEXP2 is used to provide the + * parameters for dual algorithm (e.g. Kyber). */ static gpg_error_t -ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo) +ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, + gcry_sexp_t sexp2, int algo) { gpg_error_t err; gcry_sexp_t list, l2; @@ -1364,8 +1371,46 @@ ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo) goto leave; gcry_sexp_release (list); + list = NULL; - if (algo == PUBKEY_ALGO_ECDH) + if (algo == PUBKEY_ALGO_KYBER) + { + if (!sexp2) + { + err = gpg_error (GPG_ERR_MISSING_VALUE); + goto leave; + } + + list = gcry_sexp_find_token (sexp2, "public-key", 0); + if (!list) + { + err = gpg_error (GPG_ERR_INV_OBJ); + goto leave; + } + l2 = gcry_sexp_cadr (list); + gcry_sexp_release (list); + list = l2; + if (!list) + { + err = gpg_error (GPG_ERR_NO_OBJ); + goto leave; + } + + l2 = gcry_sexp_find_token (list, "p", 1); + if (!l2) + { + err = gpg_error (GPG_ERR_NO_OBJ); /* required parameter not found */ + goto leave; + } + array[2] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_OPAQUE); + gcry_sexp_release (l2); + if (!array[2]) + { + err = gpg_error (GPG_ERR_INV_OBJ); /* required parameter invalid */ + goto leave; + } + } + else if (algo == PUBKEY_ALGO_ECDH) { array[2] = pk_ecdh_default_params (nbits); if (!array[2]) @@ -1377,6 +1422,7 @@ ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo) leave: xfree (curve); + gcry_sexp_release (list); if (err) { for (i=0; i < 3; i++) @@ -1515,7 +1561,7 @@ do_create_from_keygrip (ctrl_t ctrl, int algo, if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH ) - err = ecckey_from_sexp (pk->pkey, s_key, algo); + err = ecckey_from_sexp (pk->pkey, s_key, NULL, algo); else err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem); if (err) @@ -1543,12 +1589,13 @@ do_create_from_keygrip (ctrl_t ctrl, int algo, } -/* Common code for the key generation function gen_xxx. The optinal +/* Common code for the key generation function gen_xxx. The optional * (COMMON_GEN_CB,COMMON_GEN_CB_PARM) can be used as communication - * object. + * object. A KEYPARMS2 forces the use of a dual key (e.g. Kyber+ECC). */ static int -common_gen (const char *keyparms, int algo, const char *algoelem, +common_gen (const char *keyparms, const char *keyparms2, + int algo, const char *algoelem, kbnode_t pub_root, u32 timestamp, u32 expireval, int is_subkey, int keygen_flags, const char *passphrase, char **cache_nonce_addr, char **passwd_nonce_addr, @@ -1559,6 +1606,7 @@ common_gen (const char *keyparms, int algo, const char *algoelem, PACKET *pkt; PKT_public_key *pk; gcry_sexp_t s_key; + gcry_sexp_t s_key2 = NULL; err = agent_genkey (NULL, cache_nonce_addr, passwd_nonce_addr, keyparms, !!(keygen_flags & KEYGEN_FLAG_NO_PROTECTION), @@ -1570,14 +1618,32 @@ common_gen (const char *keyparms, int algo, const char *algoelem, return err; } + if (keyparms2) + { + err = agent_genkey (NULL, NULL, NULL, keyparms2, + 1 /* No protection */, + NULL, timestamp, + &s_key2); + if (err) + { + log_error ("agent_genkey failed for second algo: %s\n", + gpg_strerror (err) ); + gcry_sexp_release (s_key); + return err; + } + } + if (common_gen_cb && common_gen_cb_parm) { common_gen_cb_parm->genkey_result = s_key; + common_gen_cb_parm->genkey_result2 = s_key2; err = common_gen_cb (common_gen_cb_parm); common_gen_cb_parm->genkey_result = NULL; + common_gen_cb_parm->genkey_result2 = NULL; if (err) { gcry_sexp_release (s_key); + gcry_sexp_release (s_key2); return err; } } @@ -1596,10 +1662,12 @@ common_gen (const char *keyparms, int algo, const char *algoelem, pk->expiredate = pk->timestamp + expireval; pk->pubkey_algo = algo; - if (algo == PUBKEY_ALGO_ECDSA - || algo == PUBKEY_ALGO_EDDSA - || algo == PUBKEY_ALGO_ECDH ) - err = ecckey_from_sexp (pk->pkey, s_key, algo); + if (algo == PUBKEY_ALGO_KYBER) + err = ecckey_from_sexp (pk->pkey, s_key, s_key2, algo); + else if (algo == PUBKEY_ALGO_ECDSA + || algo == PUBKEY_ALGO_EDDSA + || algo == PUBKEY_ALGO_ECDH ) + err = ecckey_from_sexp (pk->pkey, s_key, NULL, algo); else err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem); if (err) @@ -1610,6 +1678,7 @@ common_gen (const char *keyparms, int algo, const char *algoelem, return err; } gcry_sexp_release (s_key); + gcry_sexp_release (s_key2); pkt = xtrycalloc (1, sizeof *pkt); if (!pkt) @@ -1675,7 +1744,7 @@ gen_elg (int algo, unsigned int nbits, KBNODE pub_root, err = gpg_error_from_syserror (); else { - err = common_gen (keyparms, algo, "pgy", + err = common_gen (keyparms, NULL, algo, "pgy", pub_root, timestamp, expireval, is_subkey, keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr, @@ -1767,7 +1836,7 @@ gen_dsa (unsigned int nbits, KBNODE pub_root, err = gpg_error_from_syserror (); else { - err = common_gen (keyparms, PUBKEY_ALGO_DSA, "pqgy", + err = common_gen (keyparms, NULL, PUBKEY_ALGO_DSA, "pqgy", pub_root, timestamp, expireval, is_subkey, keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr, @@ -1868,7 +1937,7 @@ gen_ecc (int algo, const char *curve, kbnode_t pub_root, err = gpg_error_from_syserror (); else { - err = common_gen (keyparms, algo, "", + err = common_gen (keyparms, NULL, algo, "", pub_root, timestamp, expireval, is_subkey, *keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr, @@ -1880,6 +1949,79 @@ gen_ecc (int algo, const char *curve, kbnode_t pub_root, } +/* Generate a dual ECC+Kyber key. Note that KEYGEN_FLAGS will be + * updated by this function to indicate the forced creation of a v5 + * key. */ +static gpg_error_t +gen_kyber (int algo, unsigned int nbits, const char *curve, kbnode_t pub_root, + u32 timestamp, u32 expireval, int is_subkey, + int *keygen_flags, const char *passphrase, + char **cache_nonce_addr, char **passwd_nonce_addr, + gpg_error_t (*common_gen_cb)(common_gen_cb_parm_t), + common_gen_cb_parm_t common_gen_cb_parm) +{ + gpg_error_t err; + char *keyparms1; + const char *keyparms2; + + log_assert (algo == PUBKEY_ALGO_KYBER); + + if (nbits == 768) + keyparms2 = "(genkey(kyber768))"; + else if (nbits == 1024) + keyparms2 = "(genkey(kyber1024))"; + else + return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); + + if (!curve || !*curve) + return gpg_error (GPG_ERR_UNKNOWN_CURVE); + + *keygen_flags |= KEYGEN_FLAG_CREATE_V5_KEY; + + if (!strcmp (curve, "Curve25519")) + { + keyparms1 = 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 if (!strcmp (curve, "X448")) + { + keyparms1 = 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 /* Should we use the compressed format? Check smartcard support. */ + { + keyparms1 = xtryasprintf + ("(genkey(ecc(curve %zu:%s)(flags nocomp%s)))", + strlen (curve), curve, + (((*keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) + && (*keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? + " transient-key" : "")); + } + + if (!keyparms1) + err = gpg_error_from_syserror (); + else + { + err = common_gen (keyparms1, keyparms2, algo, "", + pub_root, timestamp, expireval, is_subkey, + *keygen_flags, passphrase, + cache_nonce_addr, passwd_nonce_addr, + common_gen_cb, common_gen_cb_parm); + xfree (keyparms1); + } + + return err; +} + + /* * Generate an RSA key. */ @@ -1928,7 +2070,7 @@ gen_rsa (int algo, unsigned int nbits, KBNODE pub_root, err = gpg_error_from_syserror (); else { - err = common_gen (keyparms, algo, "ne", + err = common_gen (keyparms, NULL, algo, "ne", pub_root, timestamp, expireval, is_subkey, keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr, @@ -2547,6 +2689,12 @@ get_keysize_range (int algo, unsigned int *min, unsigned int *max) def=255; break; + case PUBKEY_ALGO_KYBER: + *min = 768; + *max = 1024; + def = 768; + break; + default: *min = opt.compliance == CO_DE_VS ? 2048: 1024; *max = 4096; @@ -2562,45 +2710,44 @@ get_keysize_range (int algo, unsigned int *min, unsigned int *max) static unsigned int fixup_keysize (unsigned int nbits, int algo, int silent) { + unsigned int orig_nbits = nbits; + if (algo == PUBKEY_ALGO_DSA && (nbits % 64)) { nbits = ((nbits + 63) / 64) * 64; - if (!silent) - tty_printf (_("rounded up to %u bits\n"), nbits); } else if (algo == PUBKEY_ALGO_EDDSA) { - if (nbits != 255 && nbits != 441) - { - if (nbits < 256) - nbits = 255; - else - nbits = 441; - if (!silent) - tty_printf (_("rounded to %u bits\n"), nbits); - } + if (nbits < 256) + nbits = 255; + else + nbits = 441; } else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA) { - if (nbits != 256 && nbits != 384 && nbits != 521) - { - if (nbits < 256) - nbits = 256; - else if (nbits < 384) - nbits = 384; - else - nbits = 521; - if (!silent) - tty_printf (_("rounded to %u bits\n"), nbits); - } + if (nbits < 256) + nbits = 256; + else if (nbits < 384) + nbits = 384; + else + nbits = 521; + } + else if (algo == PUBKEY_ALGO_KYBER) + { + /* (in reality the numbers are not bits) */ + if (nbits < 768) + nbits = 768; + else if (nbits > 1024) + nbits = 1024; } else if ((nbits % 32)) { nbits = ((nbits + 31) / 32) * 32; - if (!silent) - tty_printf (_("rounded up to %u bits\n"), nbits ); } + if (!silent && orig_nbits != nbits) + tty_printf (_("rounded to %u bits\n"), nbits); + return nbits; } @@ -3330,6 +3477,12 @@ do_create (int algo, unsigned int nbits, const char *curve, kbnode_t pub_root, keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr, common_gen_cb, common_gen_cb_parm); + else if (algo == PUBKEY_ALGO_KYBER) + err = gen_kyber (algo, nbits, curve, + pub_root, timestamp, expiredate, is_subkey, + keygen_flags, passphrase, + cache_nonce_addr, passwd_nonce_addr, + common_gen_cb, common_gen_cb_parm); else if (algo == PUBKEY_ALGO_RSA) err = gen_rsa (algo, nbits, pub_root, timestamp, expiredate, is_subkey, *keygen_flags, passphrase, @@ -3434,6 +3587,7 @@ parse_key_parameter_part (ctrl_t ctrl, ; /* We need the flags before we can figure out the key to use. */ else if (algo) { + /* This is one of the algos parsed above (rsa, dsa, or elg). */ if (!string[3]) size = get_keysize_range (algo, NULL, NULL); else @@ -3443,14 +3597,15 @@ parse_key_parameter_part (ctrl_t ctrl, return gpg_error (GPG_ERR_INV_VALUE); } } - else if (!ascii_strcasecmp (string, "ky768")) - { - algo = PUBKEY_ALGO_KY768_25519; - is_pqc = 1; - } - else if (!ascii_strcasecmp (string, "ky1024")) + else if (!ascii_strcasecmp (string, "kyber")) { - algo = PUBKEY_ALGO_KY1024_448; + /* Get the curve and check that it can technically be used + * (i.e. everything except the EdXXXX curves. */ + curve = openpgp_is_curve_supported ("brainpoolP384r1", &algo, NULL); + if (!curve || algo == PUBKEY_ALGO_EDDSA) + return gpg_error (GPG_ERR_UNKNOWN_CURVE); + algo = PUBKEY_ALGO_KYBER; + size = 768; is_pqc = 1; } else if (!ascii_strcasecmp (string, "dil3")) @@ -3782,6 +3937,8 @@ parse_key_parameter_part (ctrl_t ctrl, * cv25519 := ECDH using curve Curve25519. * cv448 := ECDH using curve X448. * nistp256:= ECDSA or ECDH using curve NIST P-256 + * kyber := Kyber with the default parameters + * ky768_bp384 := Kyber-768 with BrainpoolP256r1 as second algo * * All strings with an unknown prefix are considered an elliptic * curve. Curves which have no implicit algorithm require that FLAGS @@ -6495,7 +6652,7 @@ gen_card_key (int keyno, int algo, int is_primary, kbnode_t pub_root, else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH ) - err = ecckey_from_sexp (pk->pkey, s_key, algo); + err = ecckey_from_sexp (pk->pkey, s_key, NULL, algo); else err = gpg_error (GPG_ERR_PUBKEY_ALGO); gcry_sexp_release (s_key); |