diff --git a/agent/pkdecrypt.c b/agent/pkdecrypt.c index e93ac0a26..3848bcefc 100644 --- a/agent/pkdecrypt.c +++ b/agent/pkdecrypt.c @@ -198,30 +198,28 @@ agent_hybrid_pgp_kem_decrypt (ctrl_t ctrl, const char *desc_text, const unsigned char *p; size_t len; - gcry_mpi_t encrypted_sessionkey_mpi; + gcry_mpi_t encrypted_sessionkey_mpi = NULL; const unsigned char *encrypted_sessionkey; size_t encrypted_sessionkey_len; - gcry_mpi_t ecc_sk_mpi; + gcry_mpi_t ecc_sk_mpi = NULL; unsigned char ecc_sk[32]; - gcry_mpi_t ecc_pk_mpi; + gcry_mpi_t ecc_pk_mpi = NULL; unsigned char ecc_pk[32]; - gcry_mpi_t ecc_ct_mpi; + gcry_mpi_t ecc_ct_mpi = NULL; const unsigned char *ecc_ct; size_t ecc_ct_len; unsigned char ecc_ecdh[32]; unsigned char ecc_ss[32]; - gcry_mpi_t mlkem_sk_mpi; - gcry_mpi_t mlkem_ct_mpi; + gcry_mpi_t mlkem_sk_mpi = NULL; + gcry_mpi_t mlkem_ct_mpi = NULL; const unsigned char *mlkem_sk; const unsigned char *mlkem_ct; unsigned char mlkem_ss[GCRY_KEM_MLKEM768_SHARED_LEN]; - gcry_buffer_t iov[6]; - - unsigned char kekkey[32]; - size_t kekkeylen = 32; /* AES-256 is mandatory */ + unsigned char kek[32]; + size_t kek_len = 32; /* AES-256 is mandatory */ gcry_cipher_hd_t hd; unsigned char sessionkey[256]; @@ -290,6 +288,8 @@ agent_hybrid_pgp_kem_decrypt (ctrl_t ctrl, const char *desc_text, reverse_buffer (ecc_sk, 32); mpi_release (ecc_pk_mpi); mpi_release (ecc_sk_mpi); + ecc_pk_mpi = NULL; + ecc_sk_mpi = NULL; ecc_ct = gcry_mpi_get_opaque (ecc_ct_mpi, &nbits); ecc_ct_len = (nbits+7)/8; @@ -302,16 +302,14 @@ agent_hybrid_pgp_kem_decrypt (ctrl_t ctrl, const char *desc_text, err = gcry_kem_decap (GCRY_KEM_RAW_X25519, ecc_sk, 32, ecc_ct, ecc_ct_len, ecc_ecdh, 32, NULL, 0); - iov[0].data = ecc_ecdh; - iov[0].off = 0; - iov[0].len = 32; - iov[1].data = (unsigned char *)ecc_ct; - iov[1].off = 0; - iov[1].len = 32; - iov[2].data = ecc_pk; - iov[2].off = 0; - iov[2].len = 32; - gcry_md_hash_buffers (GCRY_MD_SHA3_256, 0, ecc_ss, iov, 3); + if (err) + goto leave; + + err = gnupg_ecc_kem_kdf (ecc_ss, 32, GCRY_MD_SHA3_256, + ecc_ecdh, 32, ecc_ct, 32, ecc_pk, 32); + + if (err) + goto leave; /* Secondly, PQC part. For now, we assume ML-KEM. */ gcry_sexp_extract_param (s_skey1, NULL, "/s", &mlkem_sk_mpi, NULL); @@ -334,45 +332,32 @@ agent_hybrid_pgp_kem_decrypt (ctrl_t ctrl, const char *desc_text, mlkem_ct, GCRY_KEM_MLKEM768_CIPHER_LEN, mlkem_ss, GCRY_KEM_MLKEM768_SHARED_LEN, NULL, 0); + if (err) + goto leave; mpi_release (mlkem_sk_mpi); + mlkem_sk_mpi = NULL; - /* Then, combine two shared secrets into one */ - - iov[0].data = "\x00\x00\x00\x01"; /* Counter */ - iov[0].off = 0; - iov[0].len = 4; - - iov[1].data = ecc_ss; - iov[1].off = 0; - iov[1].len = 32; - - iov[2].data = (unsigned char *)ecc_ct; - iov[2].off = 0; - iov[2].len = 32; - - iov[3].data = mlkem_ss; - iov[3].off = 0; - iov[3].len = GCRY_KEM_MLKEM768_SHARED_LEN; - - iov[4].data = (unsigned char *)mlkem_ct; - iov[4].off = 0; - iov[4].len = GCRY_KEM_MLKEM768_ENCAPS_LEN; - - iov[5].data = (unsigned char *)fixedinfo; - iov[5].off = 0; - iov[5].len = 1; - - err = compute_kmac256 (kekkey, kekkeylen, - "OpenPGPCompositeKeyDerivationFunction", 37, - "KDF", 3, iov, 6); + /* Then, combine two shared secrets and ciphertexts into one KEK */ + err = gnupg_kem_combiner (kek, kek_len, + ecc_ss, 32, ecc_ct, 32, + mlkem_ss, GCRY_KEM_MLKEM768_SHARED_LEN, + mlkem_ct, GCRY_KEM_MLKEM768_CIPHER_LEN, + fixedinfo, 1); + if (err) + { + log_error ("KEM combiner failed: %s\n", gpg_strerror (err)); + goto leave; + } mpi_release (ecc_ct_mpi); mpi_release (mlkem_ct_mpi); + ecc_ct_mpi = NULL; + mlkem_ct_mpi = NULL; if (DBG_CRYPTO) { - log_printhex (kekkey, kekkeylen, "KEK key: "); + log_printhex (kek, kek_len, "KEK key: "); } err = gcry_cipher_open (&hd, GCRY_CIPHER_AES256, @@ -381,11 +366,10 @@ agent_hybrid_pgp_kem_decrypt (ctrl_t ctrl, const char *desc_text, { log_error ("ecdh failed to initialize AESWRAP: %s\n", gpg_strerror (err)); - mpi_release (encrypted_sessionkey_mpi); goto leave; } - err = gcry_cipher_setkey (hd, kekkey, kekkeylen); + err = gcry_cipher_setkey (hd, kek, kek_len); sessionkey_len = encrypted_sessionkey_len - 8; err = gcry_cipher_decrypt (hd, sessionkey, sessionkey_len, @@ -393,6 +377,7 @@ agent_hybrid_pgp_kem_decrypt (ctrl_t ctrl, const char *desc_text, gcry_cipher_close (hd); mpi_release (encrypted_sessionkey_mpi); + encrypted_sessionkey_mpi = NULL; if (err) { @@ -406,6 +391,12 @@ agent_hybrid_pgp_kem_decrypt (ctrl_t ctrl, const char *desc_text, put_membuf (outbuf, ")", 2); leave: + mpi_release (mlkem_sk_mpi); + mpi_release (ecc_pk_mpi); + mpi_release (ecc_sk_mpi); + mpi_release (ecc_ct_mpi); + mpi_release (mlkem_ct_mpi); + mpi_release (encrypted_sessionkey_mpi); gcry_sexp_release (s_skey0); gcry_sexp_release (s_skey1); return err; diff --git a/common/kmac.c b/common/kmac.c index 69e18a2c3..c5de8b102 100644 --- a/common/kmac.c +++ b/common/kmac.c @@ -1,4 +1,4 @@ -/* kmac.c - Keccak based MAC +/* kem.c - KEM helper functions * Copyright (C) 2024 g10 Code GmbH. * * This file is part of GnuPG. @@ -36,7 +36,7 @@ #include "mischelp.h" #define KECCAK512_BLOCKSIZE 136 -gpg_error_t +static gpg_error_t compute_kmac256 (void *digest, size_t digestlen, const void *key, size_t keylen, const void *custom, size_t customlen, @@ -134,3 +134,76 @@ compute_kmac256 (void *digest, size_t digestlen, return gpg_error (GPG_ERR_NOT_IMPLEMENTED); #endif } + +/* Compute KEK (shared secret) for ECC with HASHALGO, ECDH result, + ciphertext in ECC_CT, public key in ECC_PK. */ +gpg_error_t +gnupg_ecc_kem_kdf (void *kek, size_t kek_len, + int hashalgo, const void *ecdh, size_t ecdh_len, + const void *ecc_ct, size_t ecc_ct_len, + const void *ecc_pk, size_t ecc_pk_len) +{ + gcry_buffer_t iov[3]; + unsigned int dlen; + + dlen = gcry_md_get_algo_dlen (hashalgo); + if (kek_len != dlen) + return gpg_error (GPG_ERR_INV_LENGTH); + + memset (iov, 0, sizeof (iov)); + + iov[0].data = (unsigned char *)ecdh; + iov[0].len = ecdh_len; + iov[1].data = (unsigned char *)ecc_ct; + iov[1].len = ecc_ct_len; + iov[2].data = (unsigned char *)ecc_pk; + iov[2].len = ecc_pk_len; + gcry_md_hash_buffers (hashalgo, 0, kek, iov, 3); + + return 0; +} + + +/* domSeperation */ +#define KMAC_KEY "OpenPGPCompositeKeyDerivationFunction" + +/* customizationString */ +#define KMAC_CUSTOM "KDF" + +/* Compute KEK by combining two KEMs. */ +gpg_error_t +gnupg_kem_combiner (void *kek, size_t kek_len, + const void *ecc_ss, size_t ecc_ss_len, + const void *ecc_ct, size_t ecc_ct_len, + const void *mlkem_ss, size_t mlkem_ss_len, + const void *mlkem_ct, size_t mlkem_ct_len, + const void *fixedinfo, size_t fixedinfo_len) +{ + gpg_error_t err; + gcry_buffer_t iov[6]; + + memset (iov, 0, sizeof (iov)); + + iov[0].data = "\x00\x00\x00\x01"; /* Counter */ + iov[0].len = 4; + + iov[1].data = (unsigned char *)ecc_ss; + iov[1].len = ecc_ss_len; + + iov[2].data = (unsigned char *)ecc_ct; + iov[2].len = ecc_ct_len; + + iov[3].data = (unsigned char *)mlkem_ss; + iov[3].len = mlkem_ss_len; + + iov[4].data = (unsigned char *)mlkem_ct; + iov[4].len = mlkem_ct_len; + + iov[5].data = (unsigned char *)fixedinfo; + iov[5].len = fixedinfo_len; + + err = compute_kmac256 (kek, kek_len, + KMAC_KEY, strlen (KMAC_KEY), + KMAC_CUSTOM, strlen (KMAC_CUSTOM), iov, 6); + return err; +} diff --git a/common/util.h b/common/util.h index 7b2601db1..5c953a8a1 100644 --- a/common/util.h +++ b/common/util.h @@ -299,11 +299,18 @@ char *gnupg_get_help_string (const char *key, int only_current_locale); /*-- localename.c --*/ const char *gnupg_messages_locale_name (void); -/*-- kmac.c --*/ -gpg_error_t compute_kmac256 (void *digest, size_t digestlen, - const void *key, size_t keylen, - const void *custom, size_t customlen, - gcry_buffer_t *data_iov, int data_iovlen); +/*-- kem.c --*/ +gpg_error_t gnupg_ecc_kem_kdf (void *kek, size_t kek_len, + int hashalgo, const void *ecdh, size_t ecdh_len, + const void *ecc_ct, size_t ecc_ct_len, + const void *ecc_pk, size_t ecc_pk_len); + +gpg_error_t gnupg_kem_combiner (void *kek, size_t kek_len, + const void *ecc_ss, size_t ecc_ss_len, + const void *ecc_ct, size_t ecc_ct_len, + const void *mlkem_ss, size_t mlkem_ss_len, + const void *mlkem_ct, size_t mlkem_ct_len, + const void *fixedinfo, size_t fixedinfo_len); /*-- miscellaneous.c --*/