aboutsummaryrefslogtreecommitdiffstats
path: root/agent
diff options
context:
space:
mode:
Diffstat (limited to 'agent')
-rw-r--r--agent/agent.h4
-rw-r--r--agent/command.c29
-rw-r--r--agent/pkdecrypt.c166
3 files changed, 192 insertions, 7 deletions
diff --git a/agent/agent.h b/agent/agent.h
index 3bedab121..e283398fe 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -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
diff --git a/agent/command.c b/agent/command.c
index b7a71cbe5..7bf161031 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -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,22 +1024,35 @@ 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);
+ &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);
- rc = agent_pkdecrypt (ctrl, ctrl->server_local->keydesc,
- value, valuelen, &outbuf, &padding);
+ 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);
if (rc)
clear_outbuf (&outbuf);
diff --git a/agent/pkdecrypt.c b/agent/pkdecrypt.c
index c26f21d35..a66dd20f7 100644
--- a/agent/pkdecrypt.c
+++ b/agent/pkdecrypt.c
@@ -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;
+}