From bd32d0ce4ef455f4304fd29884bd49ada7d793dc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20M=C3=BCller?= <deso@posteo.net>
Date: Thu, 8 Oct 2026 19:23:25 +0200
Subject: [PATCH] g10: Prefer local secret keys over unavailable smartcard keys

When decrypting a message encrypted to multiple public-key recipients,
an unavailable smartcard key can currently be tried before a locally
available secret key. This causes gpg to prompt for insertion of the
smartcard even though the message can be decrypted without it.

Defer smartcard keys whose card is unavailable until after locally
available secret keys have been tried. If no local key can decrypt the
message, smartcard keys are still considered as before.

This change effectively restores the behavior (broken by commit
ce2f71760155 ("g10: Change decryption key selection for public key
encryption.")) for messages encrypted to both a local key and a
smartcard key, without changing the general secret-key lookup ordering
or behavior when the smartcard is available.
---
 g10/pubkey-enc.c | 33 ++++++++++++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/g10/pubkey-enc.c b/g10/pubkey-enc.c
index 7e65b8..dc32fb 100644
--- a/g10/pubkey-enc.c
+++ b/g10/pubkey-enc.c
@@ -91,6 +91,7 @@ get_session_key (ctrl_t ctrl, struct seskey_enc_list *list, DEK *dek,
   PKT_public_key *sk = NULL;
   gpg_error_t err;
   void *enum_context = NULL;
+  int pass = 0;
   u32 keyid[2];
   int search_for_secret_keys = 1;
   struct seskey_enc_list *k;
@@ -103,7 +104,37 @@ get_session_key (ctrl_t ctrl, struct seskey_enc_list *list, DEK *dek,
       sk = xmalloc_clear (sizeof *sk);
       err = enum_secret_keys (ctrl, &enum_context, sk);
       if (err)
-        break;
+        {
+          if (pass == 0)
+            {
+              enum_secret_keys (ctrl, &enum_context, NULL);
+              enum_context = NULL;
+              pass = 1;
+              continue;
+            }
+          break;
+        }
+
+      /*
+       * Do not prompt for an unavailable smartcard while another
+       * locally available secret key may decrypt the message.
+       */
+      {
+        /*
+         * 0 := No key or error
+         * 1 := Key available
+         * 2 := Key available on a smartcard
+         * 3 := Key available and passphrase cached
+         * 4 := Key available on current smartcard
+         */
+        int avail = agent_probe_secret_key (ctrl, sk);
+
+        if (avail == 0)
+          continue;
+        if (pass == 0 && (avail == 2 || avail == 4))
+          /* Any smartcard access is deferred. */
+          continue;
+      }
 
       /* Check compliance.  */
       if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_DECRYPTION,
-- 
2.56.0

