From 8d552b279d8963dfaff910a55bcca55ac5e2f258 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 9 Apr 2024 15:49:00 +0200 Subject: [PATCH] gpg: Make Kyber creation more flexible. * common/openpgp-oid.c (openpgp_is_curve_supported): Allow the abbreviated curve name. * g10/pkglue.c (pk_encrypt): Add debug output. * g10/seskey.c (encode_session_key): Handle Kyber session key like ECDH. This is just a stub. * g10/keygen.c (ecckey_from_sexp): Use the modern OID for cv25519. (parse_key_parameter_part): Allow more Kyber variants. -- Test by creating an ed25519 key and using gpg --quick-add-key --batch --passphrase "" to create several subkeys. Tested with ALGOs: kyber768 kyber1024 ky768_cv25519 ky768_bp256 kyber768_nistp256 ky1024_cv448 All curves capable of encryption should work. GnuPG-bug-id: 6815 --- common/openpgp-oid.c | 4 +++- g10/encrypt.c | 2 +- g10/keygen.c | 51 +++++++++++++++++++++++++++++++++++++------- g10/pkglue.c | 5 +++++ g10/seskey.c | 4 +++- 5 files changed, 55 insertions(+), 11 deletions(-) diff --git a/common/openpgp-oid.c b/common/openpgp-oid.c index bc82cc6b0..4b59c1aeb 100644 --- a/common/openpgp-oid.c +++ b/common/openpgp-oid.c @@ -599,7 +599,9 @@ openpgp_is_curve_supported (const char *name, int *r_algo, { if ((!ascii_strcasecmp (name, oidtable[idx].name) || (oidtable[idx].alias - && !ascii_strcasecmp (name, (oidtable[idx].alias)))) + && !ascii_strcasecmp (name, (oidtable[idx].alias))) + || (oidtable[idx].abbr + && !ascii_strcasecmp (name, (oidtable[idx].abbr)))) && curve_supported_p (oidtable[idx].name)) { if (r_algo) diff --git a/g10/encrypt.c b/g10/encrypt.c index 62483fa16..aa0c3c6dd 100644 --- a/g10/encrypt.c +++ b/g10/encrypt.c @@ -758,7 +758,7 @@ write_symkey_enc (STRING2KEY *symkey_s2k, aead_algo_t aead_algo, * Encrypt the file with the given userids (or ask if none is * supplied). Either FILENAME or FILEFD must be given, but not both. * The caller may provide a checked list of public keys in - * PROVIDED_PKS; if not the function builds a list of keys on its own. + * PROVIDED_KEYS; if not the function builds a list of keys on its own. * * Note that FILEFD is currently only used by cmd_encrypt in the * not yet finished server.c. diff --git a/g10/keygen.c b/g10/keygen.c index 763d23ee4..119f7ca5d 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -1319,7 +1319,7 @@ curve_is_448 (gcry_sexp_t sexp) * parameters for dual algorithm (e.g. Kyber). */ static gpg_error_t ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, - gcry_sexp_t sexp2, int algo) + gcry_sexp_t sexp2, int algo, int pkversion) { gpg_error_t err; gcry_sexp_t list, l2; @@ -1362,6 +1362,10 @@ ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, err = gpg_error (GPG_ERR_INV_OBJ); goto leave; } + /* For v5 keys we prefer the modern OID for cv25519. */ + if (pkversion > 4 && !strcmp (oidstr, "1.3.6.1.4.1.3029.1.5.1")) + oidstr = "1.3.101.110"; + err = openpgp_oid_from_str (oidstr, &array[0]); if (err) goto leave; @@ -1605,11 +1609,11 @@ do_create_from_keygrip (ctrl_t ctrl, int algo, pk->pubkey_algo = algo; if (algo == PUBKEY_ALGO_KYBER) - err = ecckey_from_sexp (pk->pkey, s_key, s_key2, algo); + err = ecckey_from_sexp (pk->pkey, s_key, s_key2, algo, pk->version); else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH ) - err = ecckey_from_sexp (pk->pkey, s_key, NULL, algo); + err = ecckey_from_sexp (pk->pkey, s_key, NULL, algo, pk->version); else err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem); if (err) @@ -1716,11 +1720,11 @@ common_gen (const char *keyparms, const char *keyparms2, pk->pubkey_algo = algo; if (algo == PUBKEY_ALGO_KYBER) - err = ecckey_from_sexp (pk->pkey, s_key, s_key2, algo); + err = ecckey_from_sexp (pk->pkey, s_key, s_key2, algo, pk->version); else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH ) - err = ecckey_from_sexp (pk->pkey, s_key, NULL, algo); + err = ecckey_from_sexp (pk->pkey, s_key, NULL, algo, pk->version); else err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem); if (err) @@ -3682,7 +3686,8 @@ parse_key_parameter_part (ctrl_t ctrl, return gpg_error (GPG_ERR_INV_VALUE); } } - else if (!ascii_strcasecmp (string, "kyber")) + else if (!ascii_strcasecmp (string, "kyber") + || !ascii_strcasecmp (string, "kyber768")) { /* Get the curve and check that it can technically be used * (i.e. everything except the EdXXXX curves. */ @@ -3693,6 +3698,35 @@ parse_key_parameter_part (ctrl_t ctrl, size = 768; is_pqc = 1; } + else if (!ascii_strcasecmp (string, "kyber1024")) + { + /* Get the curve and check that it can technically be used + * (i.e. everything except the EdXXXX curves. */ + curve = openpgp_is_curve_supported ("brainpoolP512r1", &algo, NULL); + if (!curve || algo == PUBKEY_ALGO_EDDSA) + return gpg_error (GPG_ERR_UNKNOWN_CURVE); + algo = PUBKEY_ALGO_KYBER; + size = 1024; + is_pqc = 1; + } + else if (!ascii_strncasecmp (string, "ky768_", 6) + || !ascii_strncasecmp (string, "ky1024_", 7) + || !ascii_strncasecmp (string, "kyber768_", 9) + || !ascii_strncasecmp (string, "kyber1024_", 10) + ) + { + /* Get the curve and check that it can technically be used + * (i.e. everything except the EdXXXX curves. */ + s = strchr (string, '_'); + log_assert (s); + s++; + curve = openpgp_is_curve_supported (s, &algo, NULL); + if (!curve || algo == PUBKEY_ALGO_EDDSA) + return gpg_error (GPG_ERR_UNKNOWN_CURVE); + algo = PUBKEY_ALGO_KYBER; + size = strstr (string, "768_")? 768 : 1024; + is_pqc = 1; + } else if (!ascii_strcasecmp (string, "dil3")) { algo = PUBKEY_ALGO_DIL3_25519; @@ -6734,12 +6768,14 @@ gen_card_key (int keyno, int algo, int is_primary, kbnode_t pub_root, if (curve_is_448 (s_key)) *keygen_flags |= KEYGEN_FLAG_CREATE_V5_KEY; + pk->version = (*keygen_flags & KEYGEN_FLAG_CREATE_V5_KEY)? 5 : 4; + if (algo == PUBKEY_ALGO_RSA) err = key_from_sexp (pk->pkey, s_key, "public-key", "ne"); else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH ) - err = ecckey_from_sexp (pk->pkey, s_key, NULL, algo); + err = ecckey_from_sexp (pk->pkey, s_key, NULL, algo, pk->version); else err = gpg_error (GPG_ERR_PUBKEY_ALGO); gcry_sexp_release (s_key); @@ -6752,7 +6788,6 @@ gen_card_key (int keyno, int algo, int is_primary, kbnode_t pub_root, } pk->timestamp = *timestamp; - pk->version = (*keygen_flags & KEYGEN_FLAG_CREATE_V5_KEY)? 5 : 4; if (expireval) pk->expiredate = pk->timestamp + expireval; pk->pubkey_algo = algo; diff --git a/g10/pkglue.c b/g10/pkglue.c index f18313913..037e97a14 100644 --- a/g10/pkglue.c +++ b/g10/pkglue.c @@ -480,6 +480,11 @@ pk_encrypt (pubkey_algo_t algo, gcry_mpi_t *resarr, gcry_mpi_t data, gcry_mpi_release (k); } } + else if (algo == PUBKEY_ALGO_KYBER) + { + log_debug ("Implement Kyber encryption\n"); + rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED); + } else rc = gpg_error (GPG_ERR_PUBKEY_ALGO); diff --git a/g10/seskey.c b/g10/seskey.c index 15c210b78..e5397080d 100644 --- a/g10/seskey.c +++ b/g10/seskey.c @@ -92,7 +92,9 @@ encode_session_key (int openpgp_pk_algo, DEK *dek, unsigned int nbits) /* Shortcut for ECDH. It's padding is minimal to simply make the output be a multiple of 8 bytes. */ - if (openpgp_pk_algo == PUBKEY_ALGO_ECDH) + /* FIXME: We use the ECDH also for Kyber for now. */ + if (openpgp_pk_algo == PUBKEY_ALGO_ECDH + || openpgp_pk_algo == PUBKEY_ALGO_KYBER) { /* Pad to 8 byte granularity; the padding byte is the number of * padded bytes.