diff options
author | Werner Koch <[email protected]> | 2024-04-15 10:16:40 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2024-04-15 10:18:09 +0000 |
commit | c736052e9ccaca8fc4662af43820090910e88122 (patch) | |
tree | 836e12da9c2dca21b96608e062ac578aff5d479a /g10/pkglue.c | |
parent | gpg: Add arg session_algo to pk_decrypt. (diff) | |
download | gnupg-c736052e9ccaca8fc4662af43820090910e88122.tar.gz gnupg-c736052e9ccaca8fc4662af43820090910e88122.zip |
gpg: Implement Kyber encryption.
* g10/build-packet.c (do_pubkey_enc): Support Kyber.
* g10/pkglue.c (do_encrypt_kem): Implement.
--
Note that the code does only work for ky768_cv25519 for now.
GnuPG-bug-id: 6815
Diffstat (limited to 'g10/pkglue.c')
-rw-r--r-- | g10/pkglue.c | 209 |
1 files changed, 207 insertions, 2 deletions
diff --git a/g10/pkglue.c b/g10/pkglue.c index 52d8c1012..0167e80e3 100644 --- a/g10/pkglue.c +++ b/g10/pkglue.c @@ -423,9 +423,214 @@ static gpg_error_t do_encrypt_kem (PKT_public_key *pk, gcry_mpi_t data, int seskey_algo, gcry_mpi_t *resarr) { + gpg_error_t err; + int i; + unsigned int nbits, n; + gcry_sexp_t s_data = NULL; + gcry_cipher_hd_t hd = NULL; + + const unsigned char *ecc_pubkey; + size_t ecc_pubkey_len; + const unsigned char *kyber_pubkey; + size_t kyber_pubkey_len; + const unsigned char *seskey; + size_t seskey_len; + unsigned char *enc_seskey = NULL; + size_t enc_seskey_len; + + unsigned char ecc_ct[GCRY_KEM_ECC_X25519_ENCAPS_LEN]; + size_t ecc_ct_len = GCRY_KEM_ECC_X25519_ENCAPS_LEN; + unsigned char ecc_ecdh[GCRY_KEM_RAW_X25519_SHARED_LEN]; + size_t ecc_ecdh_len = GCRY_KEM_RAW_X25519_SHARED_LEN; + unsigned char ecc_ss[GCRY_KEM_RAW_X25519_SHARED_LEN]; + size_t ecc_ss_len = GCRY_KEM_RAW_X25519_SHARED_LEN; + + unsigned char kyber_ct[GCRY_KEM_MLKEM768_ENCAPS_LEN]; + size_t kyber_ct_len = GCRY_KEM_MLKEM768_ENCAPS_LEN; + unsigned char kyber_ss[GCRY_KEM_MLKEM768_SHARED_LEN]; + size_t kyber_ss_len = GCRY_KEM_MLKEM768_SHARED_LEN; + + char fixedinfo[1+MAX_FINGERPRINT_LEN]; + int fixedlen; + + unsigned char kek[32]; /* AES-256 is mandatory. */ + size_t kek_len = 32; + + /* For later error checking we make sure the array is cleared. */ + resarr[0] = resarr[1] = resarr[2] = NULL; + + /* As of now we use KEM only for the combined Kyber and thus a + * second public key is expected. Right now we take the keys + * directly from the PK->data elements. */ + + /* FIXME: This is only for cv25519 */ + ecc_pubkey = gcry_mpi_get_opaque (pk->pkey[1], &nbits); + ecc_pubkey_len = (nbits+7)/8; + if (ecc_pubkey_len != 33) + { + if (opt.verbose) + log_info ("%s: ECC public key length invalid (%zu)\n", + __func__, ecc_pubkey_len); + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + ecc_pubkey++; /* Remove the 0x40 prefix. */ + ecc_pubkey_len--; + if (DBG_CRYPTO) + log_printhex (ecc_pubkey, ecc_pubkey_len, "ECC pubkey:"); + + err = gcry_kem_encap (GCRY_KEM_RAW_X25519, + ecc_pubkey, ecc_pubkey_len, + ecc_ct, ecc_ct_len, + ecc_ecdh, ecc_ecdh_len, + NULL, 0); + if (err) + { + if (opt.verbose) + log_info ("%s: gcry_kem_encap for ECC failed\n", __func__); + goto leave; + } + if (DBG_CRYPTO) + { + log_printhex (ecc_ct, ecc_ct_len, "ECC ephem:"); + log_printhex (ecc_ecdh, ecc_ecdh_len, "ECC ecdh:"); + } + err = gnupg_ecc_kem_kdf (ecc_ss, ecc_ss_len, + GCRY_MD_SHA3_256, + ecc_ecdh, ecc_ecdh_len, + ecc_ct, ecc_ct_len, + ecc_pubkey, ecc_pubkey_len); + if (err) + { + if (opt.verbose) + log_info ("%s: kdf for ECC failed\n", __func__); + goto leave; + } + if (DBG_CRYPTO) + log_printhex (ecc_ss, ecc_ss_len, "ECC shared:"); + + /* FIXME: This is only for kyber768 */ + kyber_pubkey = gcry_mpi_get_opaque (pk->pkey[2], &nbits); + kyber_pubkey_len = (nbits+7)/8; + if (kyber_pubkey_len != GCRY_KEM_MLKEM768_PUBKEY_LEN) + { + if (opt.verbose) + log_info ("%s: Kyber public key length invalid (%zu)\n", + __func__, kyber_pubkey_len); + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + if (DBG_CRYPTO) + log_printhex (kyber_pubkey, kyber_pubkey_len, "|!trunc|Kyber pubkey:"); + + err = gcry_kem_encap (GCRY_KEM_MLKEM768, + kyber_pubkey, kyber_pubkey_len, + kyber_ct, kyber_ct_len, + kyber_ss, kyber_ss_len, + NULL, 0); + if (err) + { + if (opt.verbose) + log_info ("%s: gcry_kem_encap for ECC failed\n", __func__); + goto leave; + } - log_debug ("Implement Kyber encryption\n"); - return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + if (DBG_CRYPTO) + { + log_printhex (kyber_ct, kyber_ct_len, "|!trunc|Kyber ephem:"); + log_printhex (kyber_ss, kyber_ss_len, "Kyber shared:"); + } + + + fixedinfo[0] = seskey_algo; + v5_fingerprint_from_pk (pk, fixedinfo+1, NULL); + fixedlen = 33; + + err = gnupg_kem_combiner (kek, kek_len, + ecc_ss, ecc_ss_len, ecc_ct, ecc_ct_len, + kyber_ss, kyber_ss_len, kyber_ct, kyber_ct_len, + fixedinfo, fixedlen); + if (err) + { + if (opt.verbose) + log_info ("%s: KEM combiner failed\n", __func__); + goto leave; + } + if (DBG_CRYPTO) + log_printhex (kek, kek_len, "KEK:"); + + err = gcry_cipher_open (&hd, GCRY_CIPHER_AES256, + GCRY_CIPHER_MODE_AESWRAP, 0); + if (!err) + err = gcry_cipher_setkey (hd, kek, kek_len); + if (err) + { + if (opt.verbose) + log_error ("%s: failed to initialize AESWRAP: %s\n", __func__, + gpg_strerror (err)); + goto leave; + } + + err = gcry_sexp_build (&s_data, NULL, "%m", data); + if (err) + goto leave; + + n = gcry_cipher_get_algo_keylen (seskey_algo); + seskey = gcry_mpi_get_opaque (data, &nbits); + seskey_len = (nbits+7)/8; + if (seskey_len != n) + { + if (opt.verbose) + log_info ("%s: session key length %zu" + " does not match the length for algo %d\n", + __func__, seskey_len, seskey_algo); + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + if (DBG_CRYPTO) + log_printhex (seskey, seskey_len, "seskey:"); + + enc_seskey_len = 1 + seskey_len + 8; + enc_seskey = xtrymalloc (enc_seskey_len); + if (!enc_seskey || enc_seskey_len > 254) + { + err = gpg_error_from_syserror (); + goto leave; + } + + enc_seskey[0] = enc_seskey_len - 1; + err = gcry_cipher_encrypt (hd, enc_seskey+1, enc_seskey_len-1, + seskey, seskey_len); + if (err) + { + log_error ("%s: wrapping session key failed\n", __func__); + goto leave; + } + if (DBG_CRYPTO) + log_printhex (enc_seskey, enc_seskey_len, "enc_seskey:"); + + resarr[0] = gcry_mpi_set_opaque_copy (NULL, ecc_ct, 8 * ecc_ct_len); + if (resarr[0]) + resarr[1] = gcry_mpi_set_opaque_copy (NULL, kyber_ct, 8 * kyber_ct_len); + if (resarr[1]) + resarr[2] = gcry_mpi_set_opaque_copy (NULL, enc_seskey, 8 * enc_seskey_len); + if (!resarr[0] || !resarr[1] || !resarr[2]) + { + err = gpg_error_from_syserror (); + for (i=0; i < 3; i++) + gcry_mpi_release (resarr[i]), resarr[i] = NULL; + } + + leave: + wipememory (ecc_ct, ecc_ct_len); + wipememory (ecc_ecdh, ecc_ecdh_len); + wipememory (ecc_ss, ecc_ss_len); + wipememory (kyber_ct, kyber_ct_len); + wipememory (kyber_ss, kyber_ss_len); + wipememory (kek, kek_len); + xfree (enc_seskey); + gcry_cipher_close (hd); + return err; } |