mirror of
git://git.gnupg.org/gnupg.git
synced 2025-07-03 22:56:33 +02:00
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
This commit is contained in:
parent
6c1dd3afd1
commit
97f5159495
9 changed files with 461 additions and 110 deletions
249
g10/keygen.c
249
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"))
|
||||
else if (!ascii_strcasecmp (string, "kyber"))
|
||||
{
|
||||
algo = PUBKEY_ALGO_KY768_25519;
|
||||
is_pqc = 1;
|
||||
}
|
||||
else if (!ascii_strcasecmp (string, "ky1024"))
|
||||
{
|
||||
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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue