diff options
| author | Werner Koch <[email protected]> | 2025-09-26 12:43:39 +0000 |
|---|---|---|
| committer | Werner Koch <[email protected]> | 2025-09-26 12:46:14 +0000 |
| commit | 600df5259db0b7be25a2e2b06c0c2d13265eceea (patch) | |
| tree | a019d82c85e29353a3988fb761ea1b8ce4bb2467 | |
| parent | w32: Fix gnupg_isatty. (diff) | |
| download | gnupg-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.c | 9 | ||||
| -rw-r--r-- | g10/encrypt.c | 80 | ||||
| -rw-r--r-- | g10/free-packet.c | 16 | ||||
| -rw-r--r-- | g10/mainproc.c | 35 | ||||
| -rw-r--r-- | g10/packet.h | 25 | ||||
| -rw-r--r-- | g10/pubkey-enc.c | 51 |
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) { |
