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:
parent
eaa3be7ff2
commit
3114d16bf2
@ -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,
|
||||
const unsigned char *ciphertext, size_t ciphertextlen,
|
||||
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 --*/
|
||||
#define CHECK_CONSTRAINTS_NOT_EMPTY 1
|
||||
|
@ -1010,10 +1010,12 @@ cmd_pksign (assuan_context_t ctx, char *line)
|
||||
|
||||
|
||||
static const char hlp_pkdecrypt[] =
|
||||
"PKDECRYPT [<options>]\n"
|
||||
"PKDECRYPT [--kem]\n"
|
||||
"\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
|
||||
cmd_pkdecrypt (assuan_context_t ctx, char *line)
|
||||
{
|
||||
@ -1022,20 +1024,33 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line)
|
||||
unsigned char *value;
|
||||
size_t valuelen;
|
||||
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 */
|
||||
rc = print_assuan_status (ctx, "INQUIRE_MAXLEN", "%u", MAXLEN_CIPHERTEXT);
|
||||
if (!rc)
|
||||
rc = assuan_inquire (ctx, "CIPHERTEXT",
|
||||
&value, &valuelen, MAXLEN_CIPHERTEXT);
|
||||
if (!rc && is_kem)
|
||||
rc = assuan_inquire (ctx, "OPTION",
|
||||
&option, &optionlen, MAXLEN_CIPHERTEXT); /* FIXME for maxlen? */
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
init_membuf (&outbuf, 512);
|
||||
|
||||
if (is_kem)
|
||||
{
|
||||
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);
|
||||
|
@ -157,3 +157,169 @@ agent_pkdecrypt (ctrl_t ctrl, const char *desc_text,
|
||||
xfree (shadow_info);
|
||||
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;
|
||||
}
|
||||
|
@ -75,6 +75,8 @@ struct cipher_parm_s
|
||||
assuan_context_t ctx;
|
||||
unsigned char *ciphertext;
|
||||
size_t ciphertextlen;
|
||||
const unsigned char *option;
|
||||
size_t optionlen;
|
||||
};
|
||||
|
||||
struct writecert_parm_s
|
||||
@ -2748,6 +2750,13 @@ inq_ciphertext_cb (void *opaque, const char *line)
|
||||
parm->ciphertext, parm->ciphertextlen);
|
||||
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
|
||||
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,
|
||||
u32 *keyid, u32 *mainkeyid, int pubkey_algo,
|
||||
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;
|
||||
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);
|
||||
if (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,
|
||||
inq_ciphertext_cb, &parm,
|
||||
padding_info_cb, r_padding);
|
||||
|
@ -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,
|
||||
gcry_sexp_t s_ciphertext,
|
||||
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. */
|
||||
gpg_error_t agent_keywrap_key (ctrl_t ctrl, int forexport,
|
||||
|
@ -142,7 +142,7 @@ extract_secret_x (byte **r_secret_x,
|
||||
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
|
||||
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,
|
||||
gcry_mpi_t *pkey, const byte pk_fp[MAX_FINGERPRINT_LEN])
|
||||
{
|
||||
|
@ -471,6 +471,7 @@ pk_encrypt (pubkey_algo_t algo, gcry_mpi_t *resarr, gcry_mpi_t data,
|
||||
int kdf_encr_algo;
|
||||
gcry_cipher_hd_t hd;
|
||||
|
||||
/*FIXME use build_kdf_params! */
|
||||
oid = gcry_mpi_get_opaque (pkey[0], &nbits);
|
||||
oidlen = (nbits + 7) / 8;
|
||||
|
||||
|
@ -37,6 +37,9 @@ int pk_check_secret_key (pubkey_algo_t algo, gcry_mpi_t *skey);
|
||||
|
||||
|
||||
/*-- 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);
|
||||
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
|
||||
|
@ -244,13 +244,48 @@ get_it (ctrl_t ctrl,
|
||||
goto leave;
|
||||
|
||||
if (sk->pubkey_algo == PUBKEY_ALGO_ECDH)
|
||||
{
|
||||
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. */
|
||||
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);
|
||||
s_data, &frame, &nframe, &padding,
|
||||
0, NULL, 0);
|
||||
xfree (desc);
|
||||
gcry_sexp_release (s_data);
|
||||
if (err)
|
||||
@ -293,6 +328,8 @@ get_it (ctrl_t ctrl,
|
||||
if (err)
|
||||
goto leave;
|
||||
|
||||
decryption_done:
|
||||
|
||||
/* Now the frame are the bytes decrypted but padded session key. */
|
||||
if (!nframe || nframe <= 8
|
||||
|| frame[nframe-1] > nframe)
|
||||
|
Loading…
x
Reference in New Issue
Block a user