1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-06-07 23:27:48 +02:00

Experiment with hybrid KEM.

Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
This commit is contained in:
NIIBE Yutaka 2024-03-07 15:15:15 +09:00
parent 068bb5b6f9
commit c9f71ba8ee
No known key found for this signature in database
GPG Key ID: 640114AF89DE6054

View File

@ -171,6 +171,302 @@ reverse_buffer (unsigned char *buffer, unsigned int length)
}
}
static gpg_error_t
agent_hybrid_kem_decap (ctrl_t ctrl, const char *desc_text, int kemid,
gcry_sexp_t s_cipher, membuf_t *outbuf)
{
gcry_sexp_t s_skey0 = NULL;
gcry_sexp_t s_skey1 = NULL;
unsigned char *shadow_info = NULL;
gpg_error_t err = 0;
int no_shadow_info = 0;
unsigned int nbits;
const unsigned char *p;
size_t len;
gcry_mpi_t encrypted_sessionkey_mpi;
const unsigned char *encrypted_sessionkey;
size_t encrypted_sessionkey_len;
gcry_mpi_t ecc_sk_mpi;
unsigned char ecc_sk[32];
gcry_mpi_t ecc_pk_mpi;
unsigned char ecc_pk[32];
gcry_mpi_t ecc_ct_mpi;
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;
const unsigned char *mlkem_sk;
const unsigned char *mlkem_ct;
unsigned char mlkem_ss[GCRY_KEM_MLKEM768_SHARED_LEN];
gcry_buffer_t iov[13];
unsigned char head136[2];
unsigned char headK[2];
const unsigned char pad[95] = { 0 };
unsigned char right_encode_L[3];
unsigned char kekkey[16];
size_t kekkeylen = 16; /* AES, perhaps */
gcry_cipher_hd_t hd;
unsigned char sessionkey_encoded[256];
size_t sessionkey_encoded_len;
const unsigned char fixedinfo[1] = { 105 };
(void)kemid; /* For now, only PGP. */
/*
(enc-val(pqc(s%m)(e%m)(k%m))))
*/
err = agent_key_from_file (ctrl, NULL, desc_text,
NULL, &shadow_info,
CACHE_MODE_NORMAL, NULL, &s_skey0, NULL, NULL);
if (gpg_err_code (err) == GPG_ERR_NO_SECKEY)
no_shadow_info = 1;
else if (err)
{
log_error ("failed to read the secret key\n");
goto leave;
}
if (shadow_info || no_shadow_info)
{ /* divert operation to the smartcard */
err = gpg_error (GPG_ERR_NO_SECKEY); /* Not implemented yet. */
goto leave;
}
err = agent_key_from_file (ctrl, NULL, desc_text,
NULL, &shadow_info,
CACHE_MODE_NORMAL, NULL, &s_skey1, NULL, NULL);
if (gpg_err_code (err) == GPG_ERR_NO_SECKEY)
no_shadow_info = 1;
else if (err)
{
log_error ("failed to read the secret key\n");
goto leave;
}
if (shadow_info || no_shadow_info)
{ /* divert operation to the smartcard */
err = gpg_error (GPG_ERR_NO_SECKEY); /* Not implemented yet. */
goto leave;
}
/* No smartcard, but private keys */
gcry_sexp_extract_param (s_cipher, NULL, "/e/k/s",
&ecc_ct_mpi,
&mlkem_ct_mpi,
&encrypted_sessionkey_mpi, NULL);
encrypted_sessionkey = gcry_mpi_get_opaque (encrypted_sessionkey_mpi, &nbits);
encrypted_sessionkey_len = (nbits+7)/8;
encrypted_sessionkey_len--;
if (encrypted_sessionkey[0] != encrypted_sessionkey_len)
{
err = GPG_ERR_INV_DATA;
goto leave;
}
encrypted_sessionkey++;
/* Fistly, ECC. */
gcry_sexp_extract_param (s_skey0, NULL, "/q/d",
&ecc_pk_mpi, &ecc_sk_mpi, NULL);
p = gcry_mpi_get_opaque (ecc_pk_mpi, &nbits);
len = (nbits+7)/8;
memcpy (ecc_pk, p+1, 32);
p = gcry_mpi_get_opaque (ecc_sk_mpi, &nbits);
len = (nbits+7)/8;
memset (ecc_sk, 0, 32);
memcpy (ecc_sk + 32 - len, p, len);
reverse_buffer (ecc_sk, 32);
mpi_release (ecc_pk_mpi);
mpi_release (ecc_sk_mpi);
ecc_ct = gcry_mpi_get_opaque (ecc_ct_mpi, &nbits);
ecc_ct_len = (nbits+7)/8;
/* Remove the 0x40 prefix*/
ecc_ct++;
ecc_ct_len--;
/*FIXME make sure the lengths are all correct. */
/*FIXME: check the internal of optional to determine the KEK-algo and KEKKEYLEN. */
err = gcry_kem_decap (GCRY_KEM_RAW_X25519,
ecc_sk, 32,
ecc_ct, ecc_ct_len,
ecc_ecdh, 32,
NULL, 0);
mpi_release (ecc_ct_mpi);
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);
/* Secondly, ML-KEM */
gcry_sexp_extract_param (s_skey1, NULL, "/s", &mlkem_sk_mpi, NULL);
mlkem_sk = gcry_mpi_get_opaque (mlkem_sk_mpi, &nbits);
len = (nbits+7)/8;
mlkem_ct = gcry_mpi_get_opaque (mlkem_ct_mpi, &nbits);
len = (nbits+7)/8;
err = gcry_kem_decap (GCRY_KEM_MLKEM768,
mlkem_sk, GCRY_KEM_MLKEM768_SECKEY_LEN,
mlkem_ct, GCRY_KEM_MLKEM768_ENCAPS_LEN,
mlkem_ss, GCRY_KEM_MLKEM768_SHARED_LEN,
NULL, 0);
mpi_release (mlkem_sk_mpi);
mpi_release (mlkem_ct_mpi);
/* Then, combine two shared secrets into one */
// multiKeyCombine(eccKeyShare, eccCipherText,
// mlkemKeyShare, mlkemCipherText,
// fixedInfo, oBits)
//
// Input:
// eccKeyShare - the ECC key share encoded as an octet string
// eccCipherText - the ECC ciphertext encoded as an octet string
// mlkemKeyShare - the ML-KEM key share encoded as an octet string
// mlkemCipherText - the ML-KEM ciphertext encoded as an octet string
// fixedInfo - the fixed information octet string
// oBits - the size of the output keying material in bits
//
// Constants:
// domSeparation - the UTF-8 encoding of the string
// "OpenPGPCompositeKeyDerivationFunction"
// counter - the 4 byte value 00 00 00 01
// customizationString - the UTF-8 encoding of the string "KDF"
//
// eccData = eccKeyShare || eccCipherText
// mlkemData = mlkemKeyShare || mlkemCipherText
// encData = counter || eccData || mlkemData || fixedInfo
//
// KEK = KMAC256(domSeparation, encData, oBits, customizationString)
// return KEK
//
// fixedInfo = algID (105 for ML-KEM-768-x25519kem)
//
// KMAC256(K,X,L,S):
// newX = bytepad(encode_string(K), 136) || X || right_encode(L)
// cSHAKE256(newX,L,"KMAC",S)
len = 4 + 32 + 32 + GCRY_KEM_MLKEM768_SHARED_LEN + GCRY_KEM_MLKEM768_ENCAPS_LEN;
iov[0].data = "KMAC";
iov[0].off = 0;
iov[0].len = 4;
iov[1].data = "KDF";
iov[1].off = 0;
iov[1].len = 3;
head136[0] = 1;
head136[1] = 136;
iov[2].data = head136;
iov[2].off = 0;
iov[2].len = 2;
headK[0] = 1;
headK[1] = 37;
iov[3].data = headK;
iov[3].off = 0;
iov[3].len = 2;
iov[4].data = "OpenPGPCompositeKeyDerivationFunction";
iov[4].off = 0;
iov[4].len = 37;
iov[5].data = (unsigned char *)pad;
iov[5].off = 0;
iov[5].len = sizeof (pad);
iov[6].data = "\x00\x00\x00\x01"; /* Counter */
iov[6].off = 0;
iov[6].len = 4;
iov[7].data = ecc_ss;
iov[7].off = 0;
iov[7].len = 32;
iov[8].data = (unsigned char *)ecc_ct;
iov[8].off = 0;
iov[8].len = 32;
iov[9].data = mlkem_ss;
iov[9].off = 0;
iov[9].len = GCRY_KEM_MLKEM768_SHARED_LEN;
iov[10].data = (unsigned char *)mlkem_ct;
iov[10].off = 0;
iov[10].len = GCRY_KEM_MLKEM768_ENCAPS_LEN;
iov[11].data = (unsigned char *)fixedinfo;
iov[11].off = 0;
iov[11].len = 1;
right_encode_L[0] = (kekkeylen * 8);
right_encode_L[1] = 1;
iov[12].data = right_encode_L;
iov[12].off = 0;
iov[12].len = 2;
gcry_md_hash_buffers_extract (GCRY_MD_CSHAKE256, 0, kekkey, kekkeylen,
iov, DIM (iov));
if (DBG_CRYPTO)
{
log_printhex (kekkey, kekkeylen, "KEK key: ");
}
/*FIXME: KEK may be AES256, for example */
err = gcry_cipher_open (&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_AESWRAP, 0);
if (err)
{
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);
sessionkey_encoded_len = encrypted_sessionkey_len - 8;
gcry_cipher_decrypt (hd, sessionkey_encoded, sessionkey_encoded_len,
encrypted_sessionkey, encrypted_sessionkey_len);
gcry_cipher_close (hd);
mpi_release (encrypted_sessionkey_mpi);
if (err)
{
log_error ("KEM decap failed: %s\n", gpg_strerror (err));
goto leave;
}
put_membuf_printf (outbuf, "(5:value%u:", (unsigned int)sessionkey_encoded_len);
put_membuf (outbuf, sessionkey_encoded, sessionkey_encoded_len);
put_membuf (outbuf, ")", 2);
leave:
gcry_sexp_release (s_skey0);
gcry_sexp_release (s_skey1);
xfree (shadow_info);
return err;
}
gpg_error_t
agent_kem_decap (ctrl_t ctrl, const char *desc_text, int kemid,
const unsigned char *ciphertext, size_t ciphertextlen,
@ -182,8 +478,6 @@ agent_kem_decap (ctrl_t ctrl, const char *desc_text, int kemid,
gpg_error_t err = 0;
int no_shadow_info = 0;
/* IMPLEMENT: check ctrl->have_keygrip1, kem_decap, and call key combiner */
if (!ctrl->have_keygrip)
{
log_error ("speculative decryption not yet supported\n");
@ -204,6 +498,13 @@ agent_kem_decap (ctrl_t ctrl, const char *desc_text, int kemid,
log_printhex (ctrl->keygrip, 20, "keygrip:");
log_printhex (ciphertext, ciphertextlen, "cipher: ");
}
if (ctrl->have_keygrip1)
{
err = agent_hybrid_kem_decap (ctrl, desc_text, kemid, s_cipher, outbuf);
goto leave;
}
err = agent_key_from_file (ctrl, NULL, desc_text,
NULL, &shadow_info,
CACHE_MODE_NORMAL, NULL, &s_skey, NULL, NULL);
@ -217,7 +518,7 @@ agent_kem_decap (ctrl_t ctrl, const char *desc_text, int kemid,
if (shadow_info || no_shadow_info)
{ /* divert operation to the smartcard */
err = gpg_error (GPG_ERR_NO_SECKEY);
err = gpg_error (GPG_ERR_NO_SECKEY); /* Not implemented yet. */
goto leave;
}
else