diff options
-rw-r--r-- | README | 14 | ||||
-rw-r--r-- | agent/agent.h | 13 | ||||
-rw-r--r-- | agent/command.c | 2 | ||||
-rw-r--r-- | agent/divert-tpm2.c | 31 | ||||
-rw-r--r-- | agent/pkdecrypt.c | 294 | ||||
-rw-r--r-- | common/kem.c | 60 | ||||
-rw-r--r-- | common/util.h | 3 | ||||
-rw-r--r-- | g10/call-agent.c | 10 | ||||
-rw-r--r-- | g10/pkglue.c | 2 | ||||
-rw-r--r-- | g10/pubkey-enc.c | 102 |
10 files changed, 428 insertions, 103 deletions
@@ -222,8 +222,18 @@ remote use gpg-agent because the no-autostart feature on the remote site will not work as expected. - Thus the recommendation is not to use the --supervised option. All - GnuPG components handle the startup of their daemons on their own. + If your systems already comes with a systemd enabled GnuPG, you + should thus tell it not to start its own GnuPG daemons by running + the following three commands once: + + systemctl --user mask --now gpg-agent.service \ + gpg-agent.socket gpg-agent-ssh.socket \ + gpg-agent-extra.socket gpg-agent-browser.socket + systemctl --user mask --now dirmngr.socket + systemctl --user mask --now keyboxd.socket + + This way all GnuPG components can handle the startup of their + daemons on their own and start the correct version. The only problem is that for using GnuPG's ssh-agent protocol support, the gpg-agent must have been started before ssh. This can diff --git a/agent/agent.h b/agent/agent.h index 5d426f6b8..e891981b2 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -655,6 +655,9 @@ int divert_tpm2_pkdecrypt (ctrl_t ctrl, char **r_buf, size_t *r_len, int *r_padding); int divert_tpm2_writekey (ctrl_t ctrl, const unsigned char *grip, gcry_sexp_t s_skey); +int agent_tpm2d_ecc_kem (ctrl_t ctrl, const unsigned char *shadow_info, + const unsigned char *ecc_ct, + size_t ecc_point_len, unsigned char *ecc_ecdh); #else /*!HAVE_LIBTSS*/ static inline int divert_tpm2_pksign (ctrl_t ctrl, @@ -686,6 +689,16 @@ divert_tpm2_writekey (ctrl_t ctrl, const unsigned char *grip, (void)ctrl; (void)grip; (void)s_skey; return gpg_error (GPG_ERR_NOT_SUPPORTED); } + +static inline int +agent_tpm2d_ecc_kem (ctrl_t ctrl, const unsigned char *shadow_info, + const unsigned char *ecc_ct, + size_t ecc_point_len, unsigned char *ecc_ecdh) +{ + (void)ctrl; (void)ecc_ct; + (void)ecc_point_len; (void)ecc_ecdh; + return gpg_error (GPG_ERR_NOT_SUPPORTED); +} #endif /*!HAVE_LIBTSS*/ diff --git a/agent/command.c b/agent/command.c index 88c5576fb..a9eb0104e 100644 --- a/agent/command.c +++ b/agent/command.c @@ -1096,7 +1096,7 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line) if (!rc) rc = assuan_inquire (ctx, "CIPHERTEXT", &value, &valuelen, MAXLEN_CIPHERTEXT); - if (!rc && kemid > KEM_PQC_PGP) + if (!rc && kemid > KEM_PGP) rc = assuan_inquire (ctx, "OPTION", &option, &optionlen, MAXLEN_CIPHERTEXT); if (rc) diff --git a/agent/divert-tpm2.c b/agent/divert-tpm2.c index 5779ee974..b9e8784bd 100644 --- a/agent/divert-tpm2.c +++ b/agent/divert-tpm2.c @@ -168,3 +168,34 @@ divert_tpm2_pkdecrypt (ctrl_t ctrl, return agent_tpm2d_pkdecrypt (ctrl, s, n, shadow_info, r_buf, r_len); } + +int +agent_tpm2d_ecc_kem (ctrl_t ctrl, + const unsigned char *shadow_info, + const unsigned char *ecc_ct, + size_t ecc_point_len, unsigned char *ecc_ecdh) +{ + char *ecdh = NULL; + size_t len; + int rc; + + rc = agent_tpm2d_pkdecrypt (ctrl, ecc_ct, ecc_point_len, shadow_info, + &ecdh, &len); + if (rc) + return rc; + + if (len == ecc_point_len) + memcpy (ecc_ecdh, ecdh, len); + else if (len == ecc_point_len + 1 && ecdh[0] == 0x40) /* The prefix */ + memcpy (ecc_ecdh, ecdh + 1, len - 1); + else + { + if (opt.verbose) + log_info ("%s: ECC result length invalid (%zu != %zu)\n", + __func__, len, ecc_point_len); + return gpg_error (GPG_ERR_INV_DATA); + } + + xfree (ecdh); + return rc; +} diff --git a/agent/pkdecrypt.c b/agent/pkdecrypt.c index af4a63815..90d84ee3f 100644 --- a/agent/pkdecrypt.c +++ b/agent/pkdecrypt.c @@ -34,11 +34,11 @@ * find an entry. */ struct ecc_params { - const char *curve; /* Canonical name of the curve. */ - size_t pubkey_len; /* Pubkey in the SEXP representation. */ + const char *curve; /* Canonical name of the curve. */ + size_t pubkey_len; /* Pubkey length in the SEXP representation. */ size_t scalar_len; size_t point_len; - int hash_algo; + int hash_algo; /* Hash algo when it's used for composite KEM. */ int kem_algo; int scalar_reverse; }; @@ -58,6 +58,24 @@ static const struct ecc_params ecc_table[] = 0 }, { + "NIST P-256", + 65, 32, 65, + GCRY_MD_SHA3_256, GCRY_KEM_RAW_P256R1, + 0 + }, + { + "NIST P-384", + 97, 48, 97, + GCRY_MD_SHA3_512, GCRY_KEM_RAW_P384R1, + 0 + }, + { + "NIST P-521", + 133, 66, 133, + GCRY_MD_SHA3_512, GCRY_KEM_RAW_P521R1, + 0 + }, + { "brainpoolP256r1", 65, 32, 65, GCRY_MD_SHA3_256, GCRY_KEM_RAW_BP256, @@ -81,8 +99,8 @@ static const struct ecc_params ecc_table[] = /* Maximum buffer sizes required for ECC KEM. Keep this aligned to * the ecc_table above. */ -#define ECC_SCALAR_LEN_MAX 64 -#define ECC_POINT_LEN_MAX (1+2*64) +#define ECC_SCALAR_LEN_MAX 66 +#define ECC_POINT_LEN_MAX (1+2*ECC_SCALAR_LEN_MAX) #define ECC_HASH_LEN_MAX 64 @@ -425,18 +443,17 @@ ecc_get_curve (ctrl_t ctrl, gcry_sexp_t s_skey, const char **r_curve) /* Given a private key in SEXP by S_SKEY0 and a cipher text by ECC_CT * with length ECC_POINT_LEN, do ECC KEM decap (== raw ECDH) * operation. Result is returned in the memory referred by ECC_ECDH. - * Public key is extracted and put into ECC_PK. The hash algorithm - * which is used for following KDF operation is stored into - * R_HASH_ALGO. SHADOW_INFO0 is used to determine if the private key - * is actually on smartcard. CTRL is used to access smartcard, - * internally. */ + * Public key is extracted and put into ECC_PK. The pointer to ECC + * parameters is stored into R_ECC. SHADOW_INFO0 is used to determine + * if the private key is actually on smartcard. CTRL is used to + * access smartcard, internally. */ static gpg_error_t ecc_pgp_kem_decap (ctrl_t ctrl, gcry_sexp_t s_skey0, const unsigned char *shadow_info0, const unsigned char *ecc_ct, size_t ecc_point_len, unsigned char ecc_ecdh[ECC_POINT_LEN_MAX], unsigned char ecc_pk[ECC_POINT_LEN_MAX], - int *r_hash_algo) + const struct ecc_params **r_ecc) { gpg_error_t err; const char *curve; @@ -465,8 +482,7 @@ ecc_pgp_kem_decap (ctrl_t ctrl, gcry_sexp_t s_skey0, log_info ("%s: curve '%s' not supported\n", __func__, curve); return gpg_error (GPG_ERR_BAD_SECKEY); } - - *r_hash_algo = ecc->hash_algo; + *r_ecc = ecc; if (ecc->point_len != ecc_point_len) { @@ -487,8 +503,13 @@ ecc_pgp_kem_decap (ctrl_t ctrl, gcry_sexp_t s_skey0, { if (s_skey0 && agent_is_tpm2_key (s_skey0)) { - log_error ("TPM decryption failed: %s\n", gpg_strerror (err)); - return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + err = agent_tpm2d_ecc_kem (ctrl, shadow_info0, + ecc_ct, ecc->point_len, ecc_ecdh); + if (err) + { + log_error ("TPM decryption failed: %s\n", gpg_strerror (err)); + return err; + } } else { @@ -518,7 +539,7 @@ ecc_pgp_kem_decap (ctrl_t ctrl, gcry_sexp_t s_skey0, should follow the format of: (enc-val(pqc(c%d)(e%m)(k%m)(s%m)(fixed-info&))) - c: cipher identifier (symmetric) + c: cipher identifier (of session key (wrapped key)) e: ECDH ciphertext k: ML-KEM ciphertext s: encrypted session key @@ -552,6 +573,7 @@ composite_pgp_kem_decrypt (ctrl_t ctrl, const char *desc_text, unsigned char ecc_ss[ECC_HASH_LEN_MAX]; int ecc_hashalgo; size_t ecc_shared_len, ecc_point_len; + const struct ecc_params *ecc; enum gcry_kem_algos mlkem_kem_algo; gcry_mpi_t mlkem_sk_mpi = NULL; @@ -619,18 +641,19 @@ composite_pgp_kem_decrypt (ctrl_t ctrl, const char *desc_text, /* Firstly, ECC part. */ ecc_point_len = ecc_ct_len; err = ecc_pgp_kem_decap (ctrl, s_skey0, shadow_info0, ecc_ct, ecc_point_len, - ecc_ecdh, ecc_pk, &ecc_hashalgo); + ecc_ecdh, ecc_pk, &ecc); if (err) goto leave; + ecc_hashalgo = ecc->hash_algo; ecc_shared_len = gcry_md_get_algo_dlen (ecc_hashalgo); err = gnupg_ecc_kem_kdf (ecc_ss, ecc_shared_len, ecc_hashalgo, ecc_ecdh, ecc_point_len, ecc_ct, ecc_point_len, - ecc_pk, ecc_point_len); + ecc_pk, ecc_point_len, NULL); if (err) { if (opt.verbose) log_info ("%s: kdf for ECC failed\n", __func__); - return err; + goto leave; } wipememory (ecc_ecdh, sizeof ecc_ecdh); if (DBG_CRYPTO) @@ -728,10 +751,10 @@ composite_pgp_kem_decrypt (ctrl_t ctrl, const char *desc_text, } err = gcry_cipher_setkey (hd, kek, kek_len); - sessionkey_len = encrypted_sessionkey_len - 8; - err = gcry_cipher_decrypt (hd, sessionkey, sessionkey_len, - encrypted_sessionkey, encrypted_sessionkey_len); + if (!err) + err = gcry_cipher_decrypt (hd, sessionkey, sessionkey_len, + encrypted_sessionkey, encrypted_sessionkey_len); gcry_cipher_close (hd); mpi_release (encrypted_sessionkey_mpi); @@ -766,43 +789,185 @@ composite_pgp_kem_decrypt (ctrl_t ctrl, const char *desc_text, return err; } -/* DECRYPT the encrypted stuff (like encrypted session key) in - CIPHERTEXT using KEM API, with KEMID. Keys (or a key) are - specified in CTRL. DESC_TEXT is used to retrieve private key. - OPTION can be specified for upper layer option for KEM. Decrypted - stuff (like session key) is written to OUTBUF. - */ -gpg_error_t -agent_kem_decrypt (ctrl_t ctrl, const char *desc_text, int kemid, - const unsigned char *ciphertext, size_t ciphertextlen, - const unsigned char *option, size_t optionlen, - membuf_t *outbuf) +/* For ECC PGP KEM, decrypt CIPHERTEXT using KEM API. CIPHERTEXT + should follow the format of: + + (enc-val(ecc(c%d)(h%d)(e%m)(s%m)(kdf-params&))) + c: cipher identifier (of wrapping key) + h: hash identifier + e: ECDH ciphertext + s: encrypted session key + fixed-info: A buffer with the fixed info (the KDF parameters). + + */ +static gpg_error_t +ecc_kem_decrypt (ctrl_t ctrl, const char *desc_text, + gcry_sexp_t s_cipher, membuf_t *outbuf) { - gcry_sexp_t s_cipher = NULL; + gcry_sexp_t s_skey = NULL; + unsigned char *shadow_info = NULL; gpg_error_t err = 0; - /* For now, only PQC-PGP is supported. */ - if (kemid != KEM_PQC_PGP) - return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); + unsigned int nbits; - (void)optionlen; - if (kemid == KEM_PQC_PGP && option) + int algo; + int hashalgo; + gcry_mpi_t encrypted_sessionkey_mpi = NULL; + const unsigned char *encrypted_sessionkey; + size_t encrypted_sessionkey_len; + + gcry_mpi_t ecc_ct_mpi = NULL; + const unsigned char *ecc_ct; + size_t ecc_ct_len; + unsigned char ecc_ecdh[ECC_POINT_LEN_MAX]; + unsigned char ecc_pk[ECC_POINT_LEN_MAX]; + size_t ecc_point_len; + const struct ecc_params *ecc; + + unsigned char *kek = NULL; + size_t kek_len; + + gcry_cipher_hd_t hd; + unsigned char sessionkey[256]; + size_t sessionkey_len; + gcry_buffer_t kdf_params = { 0, 0, 0, NULL }; + + err = agent_key_from_file (ctrl, NULL, desc_text, + NULL, &shadow_info, + CACHE_MODE_NORMAL, NULL, &s_skey, NULL, NULL); + if (err && gpg_err_code (err) != GPG_ERR_NO_SECKEY) { - log_error ("PQC-PGP requires no option\n"); - return gpg_error (GPG_ERR_INV_ARG); + log_error ("failed to read the secret key\n"); + goto leave; } - if (!ctrl->have_keygrip) + err = gcry_sexp_extract_param (s_cipher, NULL, "%dc%dh/es&'kdf-params'", + &algo, &hashalgo, &ecc_ct_mpi, + &encrypted_sessionkey_mpi, &kdf_params, NULL); + if (err) { - log_error ("speculative decryption not yet supported\n"); - return gpg_error (GPG_ERR_NO_SECKEY); + if (opt.verbose) + log_info ("%s: extracting parameters failed\n", __func__); + goto leave; + } + + if (!kdf_params.data) + { + if (opt.verbose) + log_info ("%s: the KDF parameters is required\n", __func__); + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + + ecc_ct = gcry_mpi_get_opaque (ecc_ct_mpi, &nbits); + ecc_ct_len = (nbits+7)/8; + + encrypted_sessionkey = gcry_mpi_get_opaque (encrypted_sessionkey_mpi, &nbits); + encrypted_sessionkey_len = (nbits+7)/8; + + kek_len = gcry_cipher_get_algo_keylen (algo); + if (kek_len == 0 || kek_len > gcry_md_get_algo_dlen (hashalgo)) + { + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; } - if (!ctrl->have_keygrip1) + kek = xtrymalloc (kek_len); + if (!kek) { - log_error ("Composite KEM requires two KEYGRIPs\n"); - return gpg_error (GPG_ERR_NO_SECKEY); + err = gpg_error_from_syserror (); + goto leave; + } + + ecc_point_len = ecc_ct_len; + err = ecc_pgp_kem_decap (ctrl, s_skey, shadow_info, + ecc_ct, ecc_point_len, + ecc_ecdh, ecc_pk, &ecc); + if (err) + goto leave; + err = gnupg_ecc_kem_kdf (kek, kek_len, hashalgo, + ecc->point_len > ecc->scalar_len ? + /* For Weierstrass curve, extract + x-component from the point. */ + ecc_ecdh + 1 : ecc_ecdh, + ecc->scalar_len, ecc_ct, ecc_point_len, + ecc_pk, ecc_point_len, &kdf_params); + if (err) + { + if (opt.verbose) + log_info ("%s: kdf for ECC failed\n", __func__); + goto leave; } + wipememory (ecc_ecdh, sizeof ecc_ecdh); + if (DBG_CRYPTO) + { + log_printhex (kek, kek_len, "KEK key: "); + } + + err = gcry_cipher_open (&hd, algo, GCRY_CIPHER_MODE_AESWRAP, 0); + if (err) + { + if (opt.verbose) + log_error ("ecdh failed to initialize AESWRAP: %s\n", + gpg_strerror (err)); + goto leave; + } + + if (encrypted_sessionkey[0] != encrypted_sessionkey_len - 1) + { + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + + err = gcry_cipher_setkey (hd, kek, kek_len); + sessionkey_len = encrypted_sessionkey_len - 8 - 1; + if (!err) + err = gcry_cipher_decrypt (hd, sessionkey, sessionkey_len, + encrypted_sessionkey + 1, + encrypted_sessionkey_len - 1); + gcry_cipher_close (hd); + + if (err) + { + log_error ("KEM decrypt failed: %s\n", gpg_strerror (err)); + goto leave; + } + + put_membuf_printf (outbuf, + "(5:value%u:", (unsigned int)sessionkey_len); + put_membuf (outbuf, sessionkey, sessionkey_len); + put_membuf (outbuf, ")", 2); + + leave: + wipememory (sessionkey, sizeof sessionkey); + wipememory (kek, sizeof kek); + xfree (kek); + mpi_release (ecc_ct_mpi); + mpi_release (encrypted_sessionkey_mpi); + gcry_free (kdf_params.data); + gcry_sexp_release (s_skey); + xfree (shadow_info); + return err; +} + + +/* DECRYPT the encrypted stuff (like encrypted session key) in + * CIPHERTEXT using KEM API, with KEMID. Keys (or a key) are + * specified in CTRL. DESC_TEXT is used to retrieve private key. + * OPTION can be specified for upper layer option for KEM. Decrypted + * stuff (like session key) is written to OUTBUF. For now, + * KEMID==KEM_CMS is _not_ yet supported. + */ +gpg_error_t +agent_kem_decrypt (ctrl_t ctrl, const char *desc_text, int kemid, + const unsigned char *ciphertext, size_t ciphertextlen, + const unsigned char *option, size_t optionlen, + membuf_t *outbuf) +{ + gcry_sexp_t s_cipher = NULL; + gpg_error_t err = 0; + + (void)optionlen; err = gcry_sexp_sscan (&s_cipher, NULL, (char*)ciphertext, ciphertextlen); if (err) @@ -811,15 +976,40 @@ agent_kem_decrypt (ctrl_t ctrl, const char *desc_text, int kemid, return gpg_error (GPG_ERR_INV_DATA); } - if (DBG_CRYPTO) + if (option) { - log_printhex (ctrl->keygrip, 20, "keygrip0:"); - log_printhex (ctrl->keygrip1, 20, "keygrip1:"); - gcry_log_debugsxp ("cipher", s_cipher); + log_error ("KEM (%d) requires no option\n", kemid); + err = gpg_error (GPG_ERR_INV_ARG); + goto leave; } - err = composite_pgp_kem_decrypt (ctrl, desc_text, s_cipher, outbuf); + if (kemid == KEM_PGP) + err = ecc_kem_decrypt (ctrl, desc_text, s_cipher, outbuf); + else if (kemid == KEM_PQC_PGP) + { + if (!ctrl->have_keygrip) + { + log_error ("speculative decryption not yet supported\n"); + return gpg_error (GPG_ERR_NO_SECKEY); + } + + if (!ctrl->have_keygrip1) + { + log_error ("Composite KEM requires two KEYGRIPs\n"); + return gpg_error (GPG_ERR_NO_SECKEY); + } + + if (DBG_CRYPTO) + { + log_printhex (ctrl->keygrip, 20, "keygrip0:"); + log_printhex (ctrl->keygrip1, 20, "keygrip1:"); + gcry_log_debugsxp ("cipher", s_cipher); + } + + err = composite_pgp_kem_decrypt (ctrl, desc_text, s_cipher, outbuf); + } + leave: gcry_sexp_release (s_cipher); return err; } diff --git a/common/kem.c b/common/kem.c index bbb450e1b..fc5575f4f 100644 --- a/common/kem.c +++ b/common/kem.c @@ -150,24 +150,50 @@ gpg_error_t gnupg_ecc_kem_kdf (void *kek, size_t kek_len, int hashalgo, const void *ecdh, size_t ecdh_len, const void *ecc_ct, size_t ecc_ct_len, - const void *ecc_pk, size_t ecc_pk_len) + const void *ecc_pk, size_t ecc_pk_len, + gcry_buffer_t *fixed_info) { - gcry_buffer_t iov[3]; - unsigned int dlen; - - dlen = gcry_md_get_algo_dlen (hashalgo); - if (kek_len != dlen) - return gpg_error (GPG_ERR_INV_LENGTH); - - memset (iov, 0, sizeof (iov)); - - iov[0].data = (unsigned char *)ecdh; - iov[0].len = ecdh_len; - iov[1].data = (unsigned char *)ecc_ct; - iov[1].len = ecc_ct_len; - iov[2].data = (unsigned char *)ecc_pk; - iov[2].len = ecc_pk_len; - gcry_md_hash_buffers (hashalgo, 0, kek, iov, 3); + if (fixed_info) + { + /* Traditional ECC */ + gpg_error_t err; + gcry_kdf_hd_t hd; + unsigned long param[1]; + + param[0] = kek_len; + err = gcry_kdf_open (&hd, GCRY_KDF_ONESTEP_KDF, hashalgo, param, 1, + ecdh, ecdh_len, NULL, 0, NULL, 0, + (char *)fixed_info->data+fixed_info->off, + fixed_info->len); + if (!err) + { + gcry_kdf_compute (hd, NULL); + gcry_kdf_final (hd, kek_len, kek); + gcry_kdf_close (hd); + } + + return err; + } + else + { + /* ECC in composite KEM */ + gcry_buffer_t iov[3]; + unsigned int dlen; + + dlen = gcry_md_get_algo_dlen (hashalgo); + if (kek_len != dlen) + return gpg_error (GPG_ERR_INV_LENGTH); + + memset (iov, 0, sizeof (iov)); + + iov[0].data = (unsigned char *)ecdh; + iov[0].len = ecdh_len; + iov[1].data = (unsigned char *)ecc_ct; + iov[1].len = ecc_ct_len; + iov[2].data = (unsigned char *)ecc_pk; + iov[2].len = ecc_pk_len; + gcry_md_hash_buffers (hashalgo, 0, kek, iov, 3); + } return 0; } diff --git a/common/util.h b/common/util.h index 4564009ce..cd5483a1a 100644 --- a/common/util.h +++ b/common/util.h @@ -305,7 +305,8 @@ const char *gnupg_messages_locale_name (void); gpg_error_t gnupg_ecc_kem_kdf (void *kek, size_t kek_len, int hashalgo, const void *ecdh, size_t ecdh_len, const void *ecc_ct, size_t ecc_ct_len, - const void *ecc_pk, size_t ecc_pk_len); + const void *ecc_pk, size_t ecc_pk_len, + gcry_buffer_t *fixed_info); gpg_error_t gnupg_kem_combiner (void *kek, size_t kek_len, const void *ecc_ss, size_t ecc_ss_len, diff --git a/g10/call-agent.c b/g10/call-agent.c index bdde9b053..bba6fa833 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -2909,6 +2909,7 @@ agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, char *p, *buf, *endp; const char *keygrip2 = NULL; struct default_inq_parm_s dfltparm; + const char *cmdline; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; @@ -2935,6 +2936,12 @@ agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, return gpg_error (GPG_ERR_INV_VALUE); } + if (*keygrip2) + cmdline = "PKDECRYPT --kem=PQC-PGP"; + else if (pubkey_algo == PUBKEY_ALGO_ECDH) + cmdline = "PKDECRYPT --kem=PGP"; + else + cmdline = "PKDECRYPT"; err = start_agent (ctrl, 0); if (err) @@ -2977,8 +2984,7 @@ 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, - *keygrip2? "PKDECRYPT --kem=PQC-PGP":"PKDECRYPT", + err = assuan_transact (agent_ctx, cmdline, put_membuf_cb, &data, inq_ciphertext_cb, &parm, padding_info_cb, r_padding); diff --git a/g10/pkglue.c b/g10/pkglue.c index 307e39e0c..240f50846 100644 --- a/g10/pkglue.c +++ b/g10/pkglue.c @@ -598,7 +598,7 @@ do_encrypt_kem (PKT_public_key *pk, gcry_mpi_t data, int seskey_algo, ecc_hash_algo, ecc_ecdh, ecc_ecdh_len, ecc_ct, ecc_ct_len, - ecc_pubkey, ecc_pubkey_len); + ecc_pubkey, ecc_pubkey_len, NULL); if (err) { if (opt.verbose) diff --git a/g10/pubkey-enc.c b/g10/pubkey-enc.c index 3cbd5624a..d9c4cdb60 100644 --- a/g10/pubkey-enc.c +++ b/g10/pubkey-enc.c @@ -188,6 +188,80 @@ get_session_key (ctrl_t ctrl, struct pubkey_enc_list *list, DEK *dek) } +/* Build an SEXP to gpg-agent, for PKDECRYPT command. */ +static gpg_error_t +ecdh_sexp_build (gcry_sexp_t *r_s_data, struct pubkey_enc_list *enc, + PKT_public_key *sk) +{ + gpg_error_t err; + const unsigned char *oid; + const unsigned char *kdf_params_spec; + unsigned int nbits; + size_t len; + size_t oid_len; + byte fp[MAX_FINGERPRINT_LEN]; + int keywrap_cipher_algo; + int kdf_hash_algo; + unsigned char *kdf_params = NULL; + int kdf_params_len = 0; + + if (!gcry_mpi_get_flag (sk->pkey[0], GCRYMPI_FLAG_OPAQUE)) + return gpg_error (GPG_ERR_BAD_PUBKEY); + + oid = gcry_mpi_get_opaque (sk->pkey[0], &nbits); + oid_len = (nbits+7)/8; + + /* In the public key part of SK, there is a specifier of KDF + parameters (namely, hash algo for KDF and symmetric algo for + wrapping key). Using this specifier (together with curve OID + of the public key and the fingerprint), we build _the_ KDF + parameters. */ + if (!gcry_mpi_get_flag (sk->pkey[2], GCRYMPI_FLAG_OPAQUE)) + return gpg_error (GPG_ERR_BAD_PUBKEY); + + kdf_params_spec = gcry_mpi_get_opaque (sk->pkey[2], &nbits); + len = (nbits+7)/8; + + fingerprint_from_pk (sk, fp, NULL); + + /* Expect 4 bytes 03 01 hash_alg symm_alg. */ + if (len != 4 || kdf_params_spec[0] != 3 || kdf_params_spec[1] != 1) + return gpg_error (GPG_ERR_BAD_PUBKEY); + + kdf_params_len = oid_len + 1 + 4 + 20 + 20; + kdf_params = xtrymalloc (kdf_params_len); + if (!kdf_params) + return gpg_error_from_syserror (); + + memcpy (kdf_params, oid, oid_len); + kdf_params[oid_len] = PUBKEY_ALGO_ECDH; + memcpy (kdf_params + oid_len + 1, kdf_params_spec, 4); + memcpy (kdf_params + oid_len + 1 + 4, "Anonymous Sender ", 20); + memcpy (kdf_params + oid_len + 1 + 4 + 20, fp, 20); + + if (DBG_CRYPTO) + log_printhex (kdf_params, kdf_params_len, + "ecdh KDF message params are:"); + + keywrap_cipher_algo = kdf_params_spec[3]; + kdf_hash_algo = kdf_params_spec[2]; + + if (!enc->d.data[0] || !enc->d.data[1]) + { + xfree (kdf_params); + return gpg_error (GPG_ERR_BAD_MPI); + } + + err = gcry_sexp_build (r_s_data, NULL, + "(enc-val(ecc(c%d)(h%d)(e%m)(s%m)(kdf-params%b)))", + keywrap_cipher_algo, kdf_hash_algo, + enc->d.data[0], enc->d.data[1], + kdf_params_len, kdf_params); + xfree (kdf_params); + return err; +} + + static gpg_error_t get_it (ctrl_t ctrl, struct pubkey_enc_list *enc, DEK *dek, PKT_public_key *sk, u32 *keyid) @@ -201,7 +275,6 @@ get_it (ctrl_t ctrl, gcry_sexp_t s_data; char *desc; char *keygrip; - byte fp[MAX_FINGERPRINT_LEN]; if (DBG_CLOCK) log_clock ("decryption start"); @@ -231,13 +304,7 @@ get_it (ctrl_t ctrl, enc->d.data[0]); } else if (sk->pubkey_algo == PUBKEY_ALGO_ECDH) - { - if (!enc->d.data[0] || !enc->d.data[1]) - err = gpg_error (GPG_ERR_BAD_MPI); - else - err = gcry_sexp_build (&s_data, NULL, "(enc-val(ecdh(s%m)(e%m)))", - enc->d.data[1], enc->d.data[0]); - } + err = ecdh_sexp_build (&s_data, enc, sk); else if (sk->pubkey_algo == PUBKEY_ALGO_KYBER) { char fixedinfo[1+MAX_FINGERPRINT_LEN]; @@ -270,9 +337,6 @@ get_it (ctrl_t ctrl, if (err) goto leave; - if (sk->pubkey_algo == PUBKEY_ALGO_ECDH) - fingerprint_from_pk (sk, fp, NULL); - /* Decrypt. */ desc = gpg_format_keydesc (ctrl, sk, FORMAT_KEYDESC_NORMAL, 1); @@ -316,22 +380,6 @@ get_it (ctrl_t ctrl, } else if (sk->pubkey_algo == PUBKEY_ALGO_ECDH) { - gcry_mpi_t decoded; - - /* At the beginning the frame are the bytes of shared point MPI. */ - err = pk_ecdh_decrypt (&decoded, fp, - enc->d.data[1], /*encr data as an MPI*/ - frame, nframe, - sk->pkey); - if(err) - goto leave; - - xfree (frame); - err = gcry_mpi_aprint (GCRYMPI_FMT_USG, &frame, &nframe, decoded); - mpi_release (decoded); - if (err) - goto leave; - /* Now the frame are the bytes decrypted but padded session key. */ if (!nframe || nframe <= 8 || frame[nframe-1] > nframe) |