diff options
Diffstat (limited to 'agent')
-rw-r--r-- | agent/agent.h | 4 | ||||
-rw-r--r-- | agent/command.c | 29 | ||||
-rw-r--r-- | agent/pkdecrypt.c | 166 |
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; +} |