aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2025-09-26 12:43:39 +0000
committerWerner Koch <[email protected]>2025-09-26 12:46:14 +0000
commit600df5259db0b7be25a2e2b06c0c2d13265eceea (patch)
treea019d82c85e29353a3988fb761ea1b8ce4bb2467
parentw32: Fix gnupg_isatty. (diff)
downloadgnupg-600df5259db0b7be25a2e2b06c0c2d13265eceea.tar.gz
gnupg-600df5259db0b7be25a2e2b06c0c2d13265eceea.zip
gpg: Detect duplicate keys with --add-recipients.
* g10/packet.h (struct pubkey_enc_list): Change to use a union to also store symkey_enc data. Adjust all users accordingly. (struct pubkey_enc_info_item): New. * g10/free-packet.c (free_pubkey_enc_list): New. * g10/mainproc.c (release_list): Use it here. * g10/decrypt.c (decrypt_message): and here. * g10/encrypt.c (reencrypt_to_new_recipients): Record the used pubkey_enc packets and pass them to write_pubkey_enc_from_list. (write_pubkey_enc_from_list): Add arg restrict_pk_list and use it to skip recipients already used. -- GnuPG-bug-id: 1825 Note that we here already prepare to better handle symkey_enc packets.
-rw-r--r--g10/decrypt.c9
-rw-r--r--g10/encrypt.c80
-rw-r--r--g10/free-packet.c16
-rw-r--r--g10/mainproc.c35
-rw-r--r--g10/packet.h25
-rw-r--r--g10/pubkey-enc.c51
6 files changed, 152 insertions, 64 deletions
diff --git a/g10/decrypt.c b/g10/decrypt.c
index 644bf2eba..539637086 100644
--- a/g10/decrypt.c
+++ b/g10/decrypt.c
@@ -120,14 +120,7 @@ decrypt_message (ctrl_t ctrl, const char *filename, strlist_t remusr)
}
xfree (dek);
- while (pkenc_list)
- {
- struct pubkey_enc_list *tmp = pkenc_list->next;
-
- release_pubkey_enc_parts (&pkenc_list->d);
- xfree (pkenc_list);
- pkenc_list = tmp;
- }
+ free_pubkey_enc_list (pkenc_list);
iobuf_close (fp);
release_armor_context (afx);
release_progress_context (pfx);
diff --git a/g10/encrypt.c b/g10/encrypt.c
index ea0ea1b2b..eaca0a51a 100644
--- a/g10/encrypt.c
+++ b/g10/encrypt.c
@@ -43,8 +43,10 @@
static int encrypt_simple( const char *filename, int mode, int use_seskey );
-static int write_pubkey_enc_from_list (ctrl_t ctrl,
- PK_LIST pk_list, DEK *dek, iobuf_t out);
+static int write_pubkey_enc_from_list (ctrl_t ctrl, pk_list_t pk_list,
+ DEK *dek, iobuf_t out,
+ struct pubkey_enc_info_item *restrct);
+
/* Helper for show the "encrypted for USER" during encryption.
@@ -933,7 +935,7 @@ encrypt_crypt (ctrl_t ctrl, gnupg_fd_t filefd, const char *filename,
if (DBG_CRYPTO)
log_printhex (cfx.dek->key, cfx.dek->keylen, "DEK is: ");
- rc = write_pubkey_enc_from_list (ctrl, pk_list, cfx.dek, out);
+ rc = write_pubkey_enc_from_list (ctrl, pk_list, cfx.dek, out, NULL);
if (rc)
goto leave;
@@ -1090,12 +1092,35 @@ reencrypt_to_new_recipients (ctrl_t ctrl, int armor, const char *filename,
gpg_error_t err;
int save_no_encrypt_to;
pk_list_t newpk_list = NULL;
+ struct pubkey_enc_info_item *restrict_pk_list = NULL;
+ struct pubkey_enc_info_item *pkei; /* Iterator */
iobuf_t outfp = NULL;
armor_filter_context_t *outafx = NULL;
PACKET pkt;
struct pubkey_enc_list *el;
unsigned int count;
+ /* Unless we want to clear the recipients, record the pubkey encrypt
+ * infos so hat we can avoid to double encrypt to the same
+ * recipient. We can't do that for wildcards, though. */
+ if (!ctrl->clear_recipients)
+ {
+ for (el = pkenc_list; el; el = el->next, count++)
+ {
+ if (el->u_sym)
+ continue;
+ if (!el->u.pub.keyid[0] && !el->u.pub.keyid[1])
+ continue; /* Wildcard encrypt - no useful info. */
+ pkei = xcalloc (1, sizeof *pkei);
+ pkei->keyid[0] = el->u.pub.keyid[0];
+ pkei->keyid[1] = el->u.pub.keyid[1];
+ pkei->version = el->u.pub.version;
+ pkei->pubkey_algo = el->u.pub.pubkey_algo;
+ pkei->next = restrict_pk_list;
+ restrict_pk_list = pkei;
+ }
+ }
+
/* Get the keys for all additional recipients but do not encrypt to
* the encrypt-to keys. */
save_no_encrypt_to = opt.no_encrypt_to;
@@ -1117,19 +1142,20 @@ reencrypt_to_new_recipients (ctrl_t ctrl, int armor, const char *filename,
}
/* Write the new recipients first. */
- err = write_pubkey_enc_from_list (ctrl, newpk_list, dek, outfp);
+ err = write_pubkey_enc_from_list (ctrl, newpk_list, dek, outfp,
+ restrict_pk_list);
if (err)
goto leave;
/* Write the old recipients in --add-recipients mode. */
for (count=0, el = pkenc_list; el; el = el->next, count++)
- if (!ctrl->clear_recipients)
+ if (!ctrl->clear_recipients && !el->u_sym)
{
if (opt.verbose)
- show_encrypted_for_user_info (ctrl, 0, &el->d, dek);
+ show_encrypted_for_user_info (ctrl, 0, &el->u.pub, dek);
init_packet (&pkt);
pkt.pkttype = PKT_PUBKEY_ENC;
- pkt.pkt.pubkey_enc = &el->d;
+ pkt.pkt.pubkey_enc = &el->u.pub;
err = build_packet (outfp, &pkt);
if (err)
log_error ("build_packet(pubkey_enc) failed: %s\n",
@@ -1157,6 +1183,12 @@ reencrypt_to_new_recipients (ctrl_t ctrl, int armor, const char *filename,
iobuf_close (outfp);
release_armor_context (outafx);
release_pk_list (newpk_list);
+ while (restrict_pk_list)
+ {
+ pkei = restrict_pk_list->next;
+ xfree (restrict_pk_list);
+ restrict_pk_list = pkei;
+ }
return err;
}
@@ -1198,7 +1230,7 @@ encrypt_filter (void *opaque, int control,
log_printhex (efx->cfx.dek->key, efx->cfx.dek->keylen, "DEK is: ");
rc = write_pubkey_enc_from_list (efx->ctrl,
- efx->pk_list, efx->cfx.dek, a);
+ efx->pk_list, efx->cfx.dek, a, NULL);
if (rc)
return rc;
@@ -1288,11 +1320,19 @@ write_pubkey_enc (ctrl_t ctrl,
/*
- * Write pubkey-enc packets from the list of PKs to OUT.
+ * Write pubkey-enc packets from the list of PKs PKLIST to OUT. DEK
+ * has the session key. If a packet with the same key is also found
+ * in RESTRICT_PK_LIST, it is not written.
*/
static int
-write_pubkey_enc_from_list (ctrl_t ctrl, PK_LIST pk_list, DEK *dek, iobuf_t out)
+write_pubkey_enc_from_list (ctrl_t ctrl, pk_list_t pk_list, DEK *dek,
+ iobuf_t out,
+ struct pubkey_enc_info_item *restrict_pk_list)
{
+ PKT_public_key *pk;
+ struct pubkey_enc_info_item *pkei;
+ int throw_keyid, rc;
+
if (opt.throw_keyids && (PGP7 || PGP8))
{
log_info(_("option '%s' may not be used in %s mode\n"),
@@ -1303,9 +1343,23 @@ write_pubkey_enc_from_list (ctrl_t ctrl, PK_LIST pk_list, DEK *dek, iobuf_t out)
for ( ; pk_list; pk_list = pk_list->next )
{
- PKT_public_key *pk = pk_list->pk;
- int throw_keyid = (opt.throw_keyids || (pk_list->flags&1));
- int rc = write_pubkey_enc (ctrl, pk, throw_keyid, dek, out);
+ pk = pk_list->pk;
+ for (pkei = restrict_pk_list; pkei; pkei = pkei->next)
+ if (pk->keyid[0] == pkei->keyid[0]
+ && pk->keyid[1] == pkei->keyid[1]
+ && pk->version == pkei->version
+ && pk->pubkey_algo == pkei->pubkey_algo)
+ break;
+ if (pkei)
+ {
+ if (opt.verbose)
+ log_info (_("already encrypted to %08lX\n"),
+ (ulong) keyid_from_pk (pk, NULL));
+ continue;
+ }
+
+ throw_keyid = (opt.throw_keyids || (pk_list->flags&1));
+ rc = write_pubkey_enc (ctrl, pk, throw_keyid, dek, out);
if (rc)
return rc;
}
diff --git a/g10/free-packet.c b/g10/free-packet.c
index 3a9e17665..47615596a 100644
--- a/g10/free-packet.c
+++ b/g10/free-packet.c
@@ -538,6 +538,22 @@ free_packet (PACKET *pkt, parse_packet_ctx_t parsectx)
}
+/* Free a entire list of public key encrypted data. */
+void
+free_pubkey_enc_list (struct pubkey_enc_list *pkenc_list)
+{
+ while (pkenc_list)
+ {
+ struct pubkey_enc_list *tmp = pkenc_list->next;
+
+ if (!pkenc_list->u_sym)
+ release_pubkey_enc_parts (&pkenc_list->u.pub);
+ xfree (pkenc_list);
+ pkenc_list = tmp;
+ }
+}
+
+
/****************
* returns 0 if they match.
*/
diff --git a/g10/mainproc.c b/g10/mainproc.c
index 3b921b15d..fc7bee32b 100644
--- a/g10/mainproc.c
+++ b/g10/mainproc.c
@@ -137,14 +137,7 @@ release_list( CTX c )
{
proc_tree (c, c->list);
release_kbnode (c->list);
- while (c->pkenc_list)
- {
- struct pubkey_enc_list *tmp = c->pkenc_list->next;
-
- release_pubkey_enc_parts (&c->pkenc_list->d);
- xfree (c->pkenc_list);
- c->pkenc_list = tmp;
- }
+ free_pubkey_enc_list (c->pkenc_list);
c->pkenc_list = NULL;
while (c->symenc_list)
{
@@ -526,7 +519,7 @@ proc_pubkey_enc (CTX c, PACKET *pkt)
{
struct pubkey_enc_list *x = xcalloc (1, sizeof *x);
- copy_pubkey_enc_parts (&x->d, enc);
+ copy_pubkey_enc_parts (&x->u.pub, enc);
x->result = -1;
x->next = c->pkenc_list;
c->pkenc_list = x;
@@ -549,24 +542,27 @@ print_pkenc_list (ctrl_t ctrl, struct pubkey_enc_list *list)
char pkstrbuf[PUBKEY_STRING_SIZE];
char *p;
+ if (list->u_sym)
+ continue;
+
pk = xmalloc_clear (sizeof *pk);
- pk->pubkey_algo = list->d.pubkey_algo;
- if (!get_pubkey (ctrl, pk, list->d.keyid))
+ pk->pubkey_algo = list->u.pub.pubkey_algo;
+ if (!get_pubkey (ctrl, pk, list->u.pub.keyid))
{
pubkey_string (pk, pkstrbuf, sizeof pkstrbuf);
log_info (_("encrypted with %s key, ID %s, created %s\n"),
pkstrbuf, keystr_from_pk (pk),
strtimestamp (pk->timestamp));
- p = get_user_id_native (ctrl, list->d.keyid);
+ p = get_user_id_native (ctrl, list->u.pub.keyid);
log_printf (_(" \"%s\"\n"), p);
xfree (p);
}
else
log_info (_("encrypted with %s key, ID %s\n"),
- openpgp_pk_algo_name (list->d.pubkey_algo),
- keystr (list->d.keyid));
+ openpgp_pk_algo_name (list->u.pub.pubkey_algo),
+ keystr (list->u.pub.keyid));
if (opt.flags.require_pqc_encryption
&& pk->pubkey_algo != PUBKEY_ALGO_KYBER)
@@ -637,11 +633,12 @@ proc_encrypted (CTX c, PACKET *pkt)
struct pubkey_enc_list *list;
for (list = c->pkenc_list; list; list = list->next)
- if (list->result)
+ if (list->result && !list->u_sym)
{ /* Key was not tried or it caused an error. */
char buf[20];
snprintf (buf, sizeof buf, "%08lX%08lX",
- (ulong)list->d.keyid[0], (ulong)list->d.keyid[1]);
+ (ulong)list->u.pub.keyid[0],
+ (ulong)list->u.pub.keyid[1]);
write_status_text (STATUS_NO_SECKEY, buf);
}
}
@@ -792,9 +789,11 @@ proc_encrypted (CTX c, PACKET *pkt)
* is compliant. */
for (i = c->pkenc_list; i && compliant; i = i->next)
{
+ if (i->u_sym)
+ continue;
memset (pk, 0, sizeof *pk);
- pk->pubkey_algo = i->d.pubkey_algo;
- if (!get_pubkey (c->ctrl, pk, i->d.keyid)
+ pk->pubkey_algo = i->u.pub.pubkey_algo;
+ if (!get_pubkey (c->ctrl, pk, i->u.pub.keyid)
&& !gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, 0,
pk->pkey, nbits_from_pk (pk), NULL))
compliant = 0;
diff --git a/g10/packet.h b/g10/packet.h
index 6b1be6fc8..52f9aef8f 100644
--- a/g10/packet.h
+++ b/g10/packet.h
@@ -166,17 +166,34 @@ typedef struct {
/* Whether to hide the key id. This value is not directly
serialized. */
byte throw_keyid;
- /* The session key. */
+ /* The encrypted session key. */
gcry_mpi_t data[PUBKEY_MAX_NENC];
} PKT_pubkey_enc;
-/* An object to build a list of public-key encrypted session key. */
+/* An object to build a list of public-key and symkey encrypted
+ * session key. Note that we use a dedicated uinion here instead of
+ * the usual PACKET type; this the need for extra allocations. */
struct pubkey_enc_list
{
struct pubkey_enc_list *next;
int result;
- PKT_pubkey_enc d;
+ int u_sym; /* Use the sym member. */
+ union {
+ PKT_pubkey_enc pub;
+ PKT_symkey_enc sym;
+ } u;
+};
+
+
+/* An object to record some properties of a PKT_pubkey_enc packet. */
+struct pubkey_enc_info_item
+{
+ struct pubkey_enc_info_item *next;
+ /* 3 fields copied from a PKT_pubkey_enc: */
+ u32 keyid[2];
+ byte version;
+ byte pubkey_algo;
};
@@ -945,6 +962,8 @@ PKT_public_key *copy_public_key( PKT_public_key *d, PKT_public_key *s );
PKT_signature *copy_signature( PKT_signature *d, PKT_signature *s );
PKT_user_id *scopy_user_id (PKT_user_id *sd );
+void free_pubkey_enc_list (struct pubkey_enc_list *pkenc_list);
+
int cmp_public_keys( PKT_public_key *a, PKT_public_key *b );
int cmp_signatures( PKT_signature *a, PKT_signature *b );
int cmp_user_ids( PKT_user_id *a, PKT_user_id *b );
diff --git a/g10/pubkey-enc.c b/g10/pubkey-enc.c
index 418e4ab72..9d6193e4d 100644
--- a/g10/pubkey-enc.c
+++ b/g10/pubkey-enc.c
@@ -115,23 +115,25 @@ get_session_key (ctrl_t ctrl, struct pubkey_enc_list *list, DEK *dek)
*/
for (k = list; k; k = k->next)
{
- if (!(k->d.pubkey_algo == PUBKEY_ALGO_ELGAMAL_E
- || k->d.pubkey_algo == PUBKEY_ALGO_ECDH
- || k->d.pubkey_algo == PUBKEY_ALGO_KYBER
- || k->d.pubkey_algo == PUBKEY_ALGO_RSA
- || k->d.pubkey_algo == PUBKEY_ALGO_RSA_E
- || k->d.pubkey_algo == PUBKEY_ALGO_ELGAMAL))
+ if (k->u_sym)
+ continue;
+ if (!(k->u.pub.pubkey_algo == PUBKEY_ALGO_ELGAMAL_E
+ || k->u.pub.pubkey_algo == PUBKEY_ALGO_ECDH
+ || k->u.pub.pubkey_algo == PUBKEY_ALGO_KYBER
+ || k->u.pub.pubkey_algo == PUBKEY_ALGO_RSA
+ || k->u.pub.pubkey_algo == PUBKEY_ALGO_RSA_E
+ || k->u.pub.pubkey_algo == PUBKEY_ALGO_ELGAMAL))
continue;
- if (openpgp_pk_test_algo2 (k->d.pubkey_algo, PUBKEY_USAGE_ENC))
+ if (openpgp_pk_test_algo2 (k->u.pub.pubkey_algo, PUBKEY_USAGE_ENC))
continue;
- if (sk->pubkey_algo != k->d.pubkey_algo)
+ if (sk->pubkey_algo != k->u.pub.pubkey_algo)
continue;
keyid_from_pk (sk, keyid);
- if (!k->d.keyid[0] && !k->d.keyid[1])
+ if (!k->u.pub.keyid[0] && !k->u.pub.keyid[1])
{
if (opt.skip_hidden_recipients)
continue;
@@ -141,7 +143,8 @@ get_session_key (ctrl_t ctrl, struct pubkey_enc_list *list, DEK *dek)
keystr (keyid));
}
else if (opt.try_all_secrets
- || (k->d.keyid[0] == keyid[0] && k->d.keyid[1] == keyid[1]))
+ || (k->u.pub.keyid[0] == keyid[0]
+ && k->u.pub.keyid[1] == keyid[1]))
{
if (!opt.quiet && !(sk->pubkey_usage & PUBKEY_USAGE_XENC_MASK))
log_info (_("used key is not marked for encryption use.\n"));
@@ -153,7 +156,7 @@ get_session_key (ctrl_t ctrl, struct pubkey_enc_list *list, DEK *dek)
k->result = err;
if (!err)
{
- if (!opt.quiet && !k->d.keyid[0] && !k->d.keyid[1])
+ if (!opt.quiet && !k->u.pub.keyid[0] && !k->u.pub.keyid[1])
{
log_info (_("okay, we are the anonymous recipient.\n"));
if (!(sk->pubkey_usage & PUBKEY_USAGE_XENC_MASK))
@@ -211,7 +214,7 @@ ecdh_sexp_build (gcry_sexp_t *r_s_data, struct pubkey_enc_list *enc,
keywrap_cipher_algo = kdf_params_spec[3];
kdf_hash_algo = kdf_params_spec[2];
- if (!enc->d.data[0] || !enc->d.data[1])
+ if (!enc->u.pub.data[0] || !enc->u.pub.data[1])
{
xfree (kdf_params);
return gpg_error (GPG_ERR_BAD_MPI);
@@ -220,7 +223,7 @@ ecdh_sexp_build (gcry_sexp_t *r_s_data, struct pubkey_enc_list *enc,
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],
+ enc->u.pub.data[0], enc->u.pub.data[1],
(int)kdf_params_len, kdf_params);
xfree (kdf_params);
return err;
@@ -244,6 +247,8 @@ get_it (ctrl_t ctrl,
if (DBG_CLOCK)
log_clock ("decryption start");
+ log_assert (!enc->u_sym);
+
/* Get the keygrip. */
err = hexkeygrip_from_pk (sk, &keygrip);
if (err)
@@ -253,20 +258,20 @@ get_it (ctrl_t ctrl,
if (sk->pubkey_algo == PUBKEY_ALGO_ELGAMAL
|| sk->pubkey_algo == PUBKEY_ALGO_ELGAMAL_E)
{
- if (!enc->d.data[0] || !enc->d.data[1])
+ if (!enc->u.pub.data[0] || !enc->u.pub.data[1])
err = gpg_error (GPG_ERR_BAD_MPI);
else
err = gcry_sexp_build (&s_data, NULL, "(enc-val(elg(a%m)(b%m)))",
- enc->d.data[0], enc->d.data[1]);
+ enc->u.pub.data[0], enc->u.pub.data[1]);
}
else if (sk->pubkey_algo == PUBKEY_ALGO_RSA
|| sk->pubkey_algo == PUBKEY_ALGO_RSA_E)
{
- if (!enc->d.data[0])
+ if (!enc->u.pub.data[0])
err = gpg_error (GPG_ERR_BAD_MPI);
else
err = gcry_sexp_build (&s_data, NULL, "(enc-val(rsa(a%m)))",
- enc->d.data[0]);
+ enc->u.pub.data[0]);
}
else if (sk->pubkey_algo == PUBKEY_ALGO_ECDH)
err = ecdh_sexp_build (&s_data, enc, sk);
@@ -283,18 +288,20 @@ get_it (ctrl_t ctrl,
}
else
{
- fixedinfo[0] = enc->d.seskey_algo;
+ fixedinfo[0] = enc->u.pub.seskey_algo;
v5_fingerprint_from_pk (sk, fixedinfo+1, NULL);
fixedlen = 33;
}
- if (!enc->d.data[0] || !enc->d.data[1] || !enc->d.data[2])
+ if (!enc->u.pub.data[0] || !enc->u.pub.data[1] || !enc->u.pub.data[2])
err = gpg_error (GPG_ERR_BAD_MPI);
else
err = gcry_sexp_build (&s_data, NULL,
"(enc-val(pqc(e%m)(k%m)(s%m)(c%d)(fixed-info%b)))",
- enc->d.data[0], enc->d.data[1], enc->d.data[2],
- enc->d.seskey_algo, fixedlen, fixedinfo);
+ enc->u.pub.data[0],
+ enc->u.pub.data[1],
+ enc->u.pub.data[2],
+ enc->u.pub.seskey_algo, fixedlen, fixedinfo);
}
else
err = gpg_error (GPG_ERR_BUG);
@@ -341,7 +348,7 @@ get_it (ctrl_t ctrl,
log_info (_("WARNING: session key is not quantum-resistant\n"));
}
dek->keylen = nframe;
- dek->algo = enc->d.seskey_algo;
+ dek->algo = enc->u.pub.seskey_algo;
}
else if (sk->pubkey_algo == PUBKEY_ALGO_ECDH)
{