diff options
-rw-r--r-- | agent/agent.h | 4 | ||||
-rw-r--r-- | agent/command.c | 29 | ||||
-rw-r--r-- | agent/pkdecrypt.c | 166 | ||||
-rw-r--r-- | g10/call-agent.c | 17 | ||||
-rw-r--r-- | g10/call-agent.h | 3 | ||||
-rw-r--r-- | g10/ecdh.c | 2 | ||||
-rw-r--r-- | g10/pkglue.c | 1 | ||||
-rw-r--r-- | g10/pkglue.h | 3 | ||||
-rw-r--r-- | g10/pubkey-enc.c | 41 |
9 files changed, 253 insertions, 13 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; +} diff --git a/g10/call-agent.c b/g10/call-agent.c index f545b6690..baa293b28 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -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); diff --git a/g10/call-agent.h b/g10/call-agent.h index 45af95422..398d68df4 100644 --- a/g10/call-agent.h +++ b/g10/call-agent.h @@ -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, diff --git a/g10/ecdh.c b/g10/ecdh.c index eb14154a1..7a0524ffa 100644 --- a/g10/ecdh.c +++ b/g10/ecdh.c @@ -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]) { diff --git a/g10/pkglue.c b/g10/pkglue.c index 360edc8cb..c9169f85b 100644 --- a/g10/pkglue.c +++ b/g10/pkglue.c @@ -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; diff --git a/g10/pkglue.h b/g10/pkglue.h index abeddb811..f0d7a0414 100644 --- a/g10/pkglue.h +++ b/g10/pkglue.h @@ -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 diff --git a/g10/pubkey-enc.c b/g10/pubkey-enc.c index 6e1b0898e..605f5c00e 100644 --- a/g10/pubkey-enc.c +++ b/g10/pubkey-enc.c @@ -244,13 +244,48 @@ get_it (ctrl_t ctrl, goto leave; 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. */ 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) |