aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README14
-rw-r--r--agent/agent.h13
-rw-r--r--agent/command.c2
-rw-r--r--agent/divert-tpm2.c31
-rw-r--r--agent/pkdecrypt.c294
-rw-r--r--common/kem.c60
-rw-r--r--common/util.h3
-rw-r--r--g10/call-agent.c10
-rw-r--r--g10/pkglue.c2
-rw-r--r--g10/pubkey-enc.c102
10 files changed, 428 insertions, 103 deletions
diff --git a/README b/README
index ba80dba0b..e9e317a0d 100644
--- a/README
+++ b/README
@@ -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)