1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-12-22 10:19:57 +01:00

Experiment to use KEM interface for Curve25519 ECDH.

Works now with hard-coded, but not checked things.

Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
This commit is contained in:
NIIBE Yutaka 2024-01-26 15:34:03 +09:00
parent eaa3be7ff2
commit 3114d16bf2
No known key found for this signature in database
GPG Key ID: 640114AF89DE6054
9 changed files with 253 additions and 13 deletions

View File

@ -537,6 +537,10 @@ gpg_error_t agent_pksign (ctrl_t ctrl, const char *cache_nonce,
gpg_error_t agent_pkdecrypt (ctrl_t ctrl, const char *desc_text, gpg_error_t agent_pkdecrypt (ctrl_t ctrl, const char *desc_text,
const unsigned char *ciphertext, size_t ciphertextlen, const unsigned char *ciphertext, size_t ciphertextlen,
membuf_t *outbuf, int *r_padding); membuf_t *outbuf, int *r_padding);
gpg_error_t agent_kem_decap (ctrl_t ctrl, const char *desc_text,
const unsigned char *ciphertext, size_t ciphertextlen,
membuf_t *outbuf,
const unsigned char *option, size_t optionlen);
/*-- genkey.c --*/ /*-- genkey.c --*/
#define CHECK_CONSTRAINTS_NOT_EMPTY 1 #define CHECK_CONSTRAINTS_NOT_EMPTY 1

View File

@ -1010,10 +1010,12 @@ cmd_pksign (assuan_context_t ctx, char *line)
static const char hlp_pkdecrypt[] = static const char hlp_pkdecrypt[] =
"PKDECRYPT [<options>]\n" "PKDECRYPT [--kem]\n"
"\n" "\n"
"Perform the actual decrypt operation. Input is not\n" "Perform the actual decrypt operation. Input is not\n"
"sensitive to eavesdropping."; "sensitive to eavesdropping.\n"
"If the --kem option is used, decryption is done with the KEM,\n"
"inquiring upper-layer option, when needed.";
static gpg_error_t static gpg_error_t
cmd_pkdecrypt (assuan_context_t ctx, char *line) cmd_pkdecrypt (assuan_context_t ctx, char *line)
{ {
@ -1022,22 +1024,35 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line)
unsigned char *value; unsigned char *value;
size_t valuelen; size_t valuelen;
membuf_t outbuf; membuf_t outbuf;
int padding; int padding = -1;
int is_kem;
unsigned char *option = NULL;
size_t optionlen = 0;
(void)line; is_kem = has_option (line, "--kem");
/* First inquire the data to decrypt */ /* First inquire the data to decrypt */
rc = print_assuan_status (ctx, "INQUIRE_MAXLEN", "%u", MAXLEN_CIPHERTEXT); rc = print_assuan_status (ctx, "INQUIRE_MAXLEN", "%u", MAXLEN_CIPHERTEXT);
if (!rc) if (!rc)
rc = assuan_inquire (ctx, "CIPHERTEXT", rc = assuan_inquire (ctx, "CIPHERTEXT",
&value, &valuelen, MAXLEN_CIPHERTEXT); &value, &valuelen, MAXLEN_CIPHERTEXT);
if (!rc && is_kem)
rc = assuan_inquire (ctx, "OPTION",
&option, &optionlen, MAXLEN_CIPHERTEXT); /* FIXME for maxlen? */
if (rc) if (rc)
return rc; return rc;
init_membuf (&outbuf, 512); init_membuf (&outbuf, 512);
rc = agent_pkdecrypt (ctrl, ctrl->server_local->keydesc, if (is_kem)
value, valuelen, &outbuf, &padding); {
rc = agent_kem_decap (ctrl, ctrl->server_local->keydesc,
value, valuelen, &outbuf, option, optionlen);
xfree (option);
}
else
rc = agent_pkdecrypt (ctrl, ctrl->server_local->keydesc,
value, valuelen, &outbuf, &padding);
xfree (value); xfree (value);
if (rc) if (rc)
clear_outbuf (&outbuf); clear_outbuf (&outbuf);

View File

@ -157,3 +157,169 @@ agent_pkdecrypt (ctrl_t ctrl, const char *desc_text,
xfree (shadow_info); xfree (shadow_info);
return err; return err;
} }
static void
reverse_buffer (unsigned char *buffer, unsigned int length)
{
unsigned int tmp, i;
for (i=0; i < length/2; i++)
{
tmp = buffer[i];
buffer[i] = buffer[length-1-i];
buffer[length-1-i] = tmp;
}
}
gpg_error_t
agent_kem_decap (ctrl_t ctrl, const char *desc_text,
const unsigned char *ciphertext, size_t ciphertextlen,
membuf_t *outbuf,
const unsigned char *option, size_t optionlen)
{
gcry_sexp_t s_skey = NULL, s_cipher = NULL;
unsigned char *shadow_info = NULL;
gpg_error_t err = 0;
int no_shadow_info = 0;
if (!ctrl->have_keygrip)
{
log_error ("speculative decryption not yet supported\n");
err = gpg_error (GPG_ERR_NO_SECKEY);
goto leave;
}
err = gcry_sexp_sscan (&s_cipher, NULL, (char*)ciphertext, ciphertextlen);
if (err)
{
log_error ("failed to convert ciphertext: %s\n", gpg_strerror (err));
err = gpg_error (GPG_ERR_INV_DATA);
goto leave;
}
if (DBG_CRYPTO)
{
log_printhex (ctrl->keygrip, 20, "keygrip:");
log_printhex (ciphertext, ciphertextlen, "cipher: ");
}
err = agent_key_from_file (ctrl, NULL, desc_text,
NULL, &shadow_info,
CACHE_MODE_NORMAL, NULL, &s_skey, 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);
goto leave;
}
else
{ /* No smartcard, but a private key */
unsigned int nbits;
gcry_mpi_t seckey_mpi;
gcry_mpi_t ephemkey_mpi;
gcry_mpi_t encrypted_sessionkey_mpi;
const unsigned char *p;
size_t len;
unsigned char seckey[32];
size_t seckeylen;
const unsigned char *ephemkey;
size_t ephemkeylen;
const unsigned char *encrypted_sessionkey;
size_t encrypted_sessionkey_len;
unsigned char kekkey[32]; /* FIXME */
size_t kekkeylen;
gcry_cipher_hd_t hd;
unsigned char sessionkey_encoded[256];
size_t sessionkey_encoded_len;
gcry_sexp_extract_param (s_skey, NULL, "/d", &seckey_mpi, NULL);
gcry_sexp_extract_param (s_cipher, NULL, "/e/s",
&ephemkey_mpi,
&encrypted_sessionkey_mpi, NULL);
p = gcry_mpi_get_opaque (seckey_mpi, &nbits);
len = (nbits+7)/8;
memset (seckey, 0, 32);
memcpy (seckey + 32 - len, p, len);
seckeylen = 32;
reverse_buffer (seckey, seckeylen);
ephemkey = gcry_mpi_get_opaque (ephemkey_mpi, &nbits);
ephemkeylen = (nbits+7)/8;
/* Remove the 0x40 prefix*/
ephemkey++;
ephemkeylen--;
encrypted_sessionkey = gcry_mpi_get_opaque (encrypted_sessionkey_mpi, &nbits);
encrypted_sessionkey_len = (nbits+7)/8;
/*FIXME make sure the lengths are all correct. */
encrypted_sessionkey_len--;
if (encrypted_sessionkey[0] != encrypted_sessionkey_len)
{
err = GPG_ERR_INV_DATA;
goto leave;
}
encrypted_sessionkey++;
/*FIXME: check the internal of optional to determine the KEK-algo and KEKKEYLEN. */
kekkeylen = 16;
err = gcry_kem_decap (GCRY_KEM_PGP_X25519,
seckey, seckeylen,
ephemkey, ephemkeylen,
kekkey, kekkeylen,
option, optionlen);
mpi_release (seckey_mpi);
mpi_release (ephemkey_mpi);
if (DBG_CRYPTO)
{
log_printhex (kekkey, kekkeylen, "KEK key: ");
}
/*FIXME*/
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_skey);
gcry_sexp_release (s_cipher);
xfree (shadow_info);
return err;
}

View File

@ -75,6 +75,8 @@ struct cipher_parm_s
assuan_context_t ctx; assuan_context_t ctx;
unsigned char *ciphertext; unsigned char *ciphertext;
size_t ciphertextlen; size_t ciphertextlen;
const unsigned char *option;
size_t optionlen;
}; };
struct writecert_parm_s struct writecert_parm_s
@ -2748,6 +2750,13 @@ inq_ciphertext_cb (void *opaque, const char *line)
parm->ciphertext, parm->ciphertextlen); parm->ciphertext, parm->ciphertextlen);
assuan_end_confidential (parm->ctx); assuan_end_confidential (parm->ctx);
} }
else if (has_leading_keyword (line, "OPTION"))
{
assuan_begin_confidential (parm->ctx);
rc = assuan_send_data (parm->dflt->ctx,
parm->option, parm->optionlen);
assuan_end_confidential (parm->ctx);
}
else else
rc = default_inq_cb (parm->dflt, line); rc = default_inq_cb (parm->dflt, line);
@ -2782,7 +2791,8 @@ gpg_error_t
agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc,
u32 *keyid, u32 *mainkeyid, int pubkey_algo, u32 *keyid, u32 *mainkeyid, int pubkey_algo,
gcry_sexp_t s_ciphertext, gcry_sexp_t s_ciphertext,
unsigned char **r_buf, size_t *r_buflen, int *r_padding) unsigned char **r_buf, size_t *r_buflen, int *r_padding,
int use_kem, const unsigned char *option, size_t optionlen)
{ {
gpg_error_t err; gpg_error_t err;
char line[ASSUAN_LINELENGTH]; char line[ASSUAN_LINELENGTH];
@ -2837,7 +2847,10 @@ agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc,
err = make_canon_sexp (s_ciphertext, &parm.ciphertext, &parm.ciphertextlen); err = make_canon_sexp (s_ciphertext, &parm.ciphertext, &parm.ciphertextlen);
if (err) if (err)
return err; return err;
err = assuan_transact (agent_ctx, "PKDECRYPT", parm.option = option;
parm.optionlen = optionlen;
snprintf (line, sizeof line, "PKDECRYPT%s", use_kem? " --kem" : "");
err = assuan_transact (agent_ctx, line,
put_membuf_cb, &data, put_membuf_cb, &data,
inq_ciphertext_cb, &parm, inq_ciphertext_cb, &parm,
padding_info_cb, r_padding); padding_info_cb, r_padding);

View File

@ -219,7 +219,8 @@ gpg_error_t agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc,
u32 *keyid, u32 *mainkeyid, int pubkey_algo, u32 *keyid, u32 *mainkeyid, int pubkey_algo,
gcry_sexp_t s_ciphertext, gcry_sexp_t s_ciphertext,
unsigned char **r_buf, size_t *r_buflen, unsigned char **r_buf, size_t *r_buflen,
int *r_padding); int *r_padding, int use_kem,
const unsigned char *option, size_t optionlen);
/* Retrieve a key encryption key. */ /* Retrieve a key encryption key. */
gpg_error_t agent_keywrap_key (ctrl_t ctrl, int forexport, gpg_error_t agent_keywrap_key (ctrl_t ctrl, int forexport,

View File

@ -142,7 +142,7 @@ extract_secret_x (byte **r_secret_x,
master key fingerprint". For v5 key, it is considered "adequate" master key fingerprint". For v5 key, it is considered "adequate"
(in terms of NIST SP 800 56A, see 5.8.2 FixedInfo) to use the first (in terms of NIST SP 800 56A, see 5.8.2 FixedInfo) to use the first
20 octets of its 32 octets fingerprint. */ 20 octets of its 32 octets fingerprint. */
static gpg_error_t gpg_error_t
build_kdf_params (unsigned char kdf_params[256], size_t *r_size, build_kdf_params (unsigned char kdf_params[256], size_t *r_size,
gcry_mpi_t *pkey, const byte pk_fp[MAX_FINGERPRINT_LEN]) gcry_mpi_t *pkey, const byte pk_fp[MAX_FINGERPRINT_LEN])
{ {

View File

@ -471,6 +471,7 @@ pk_encrypt (pubkey_algo_t algo, gcry_mpi_t *resarr, gcry_mpi_t data,
int kdf_encr_algo; int kdf_encr_algo;
gcry_cipher_hd_t hd; gcry_cipher_hd_t hd;
/*FIXME use build_kdf_params! */
oid = gcry_mpi_get_opaque (pkey[0], &nbits); oid = gcry_mpi_get_opaque (pkey[0], &nbits);
oidlen = (nbits + 7) / 8; oidlen = (nbits + 7) / 8;

View File

@ -37,6 +37,9 @@ int pk_check_secret_key (pubkey_algo_t algo, gcry_mpi_t *skey);
/*-- ecdh.c --*/ /*-- ecdh.c --*/
gpg_error_t
build_kdf_params (unsigned char kdf_params[256], size_t *r_size,
gcry_mpi_t *pkey, const byte pk_fp[MAX_FINGERPRINT_LEN]);
gcry_mpi_t pk_ecdh_default_params (unsigned int qbits); gcry_mpi_t pk_ecdh_default_params (unsigned int qbits);
gpg_error_t pk_ecdh_generate_ephemeral_key (gcry_mpi_t *pkey, gcry_mpi_t *r_k); gpg_error_t pk_ecdh_generate_ephemeral_key (gcry_mpi_t *pkey, gcry_mpi_t *r_k);
gpg_error_t pk_ecdh_encrypt_with_shared_point gpg_error_t pk_ecdh_encrypt_with_shared_point

View File

@ -244,13 +244,48 @@ get_it (ctrl_t ctrl,
goto leave; goto leave;
if (sk->pubkey_algo == PUBKEY_ALGO_ECDH) if (sk->pubkey_algo == PUBKEY_ALGO_ECDH)
fingerprint_from_pk (sk, fp, NULL); {
int with_ecdh_cv25519;
fingerprint_from_pk (sk, fp, NULL);
with_ecdh_cv25519 = openpgp_oid_is_cv25519 (sk->pkey[0]);
if (with_ecdh_cv25519)
{
unsigned char kdf_params[256];
size_t kdf_params_size;
log_info ("ECDH KEM\n");
build_kdf_params (kdf_params, &kdf_params_size,
sk->pkey, fp);
log_printhex (kdf_params, kdf_params_size, "KDF (option):");
log_printsexp ("sexp data:", s_data);
/* Do PKDECRYPT with --kem. */
desc = gpg_format_keydesc (ctrl, sk, FORMAT_KEYDESC_NORMAL, 1);
err = agent_pkdecrypt (NULL, keygrip,
desc, sk->keyid, sk->main_keyid, sk->pubkey_algo,
s_data, &frame, &nframe, &padding,
1, kdf_params, kdf_params_size);
xfree (desc);
gcry_sexp_release (s_data);
log_printhex (frame, nframe, "DEK frame:");
if (err)
goto leave;
goto decryption_done;
}
}
/* Decrypt. */ /* Decrypt. */
desc = gpg_format_keydesc (ctrl, sk, FORMAT_KEYDESC_NORMAL, 1); desc = gpg_format_keydesc (ctrl, sk, FORMAT_KEYDESC_NORMAL, 1);
err = agent_pkdecrypt (NULL, keygrip, err = agent_pkdecrypt (NULL, keygrip,
desc, sk->keyid, sk->main_keyid, sk->pubkey_algo, desc, sk->keyid, sk->main_keyid, sk->pubkey_algo,
s_data, &frame, &nframe, &padding); s_data, &frame, &nframe, &padding,
0, NULL, 0);
xfree (desc); xfree (desc);
gcry_sexp_release (s_data); gcry_sexp_release (s_data);
if (err) if (err)
@ -293,6 +328,8 @@ get_it (ctrl_t ctrl,
if (err) if (err)
goto leave; goto leave;
decryption_done:
/* Now the frame are the bytes decrypted but padded session key. */ /* Now the frame are the bytes decrypted but padded session key. */
if (!nframe || nframe <= 8 if (!nframe || nframe <= 8
|| frame[nframe-1] > nframe) || frame[nframe-1] > nframe)