Mailing List Archive

Suggestion needed: fix T3416 gpg should select available signing key on card (even with -u option)
Hi guys,

I encountered the issue of T3416 gpg should select available signing key
on card (even with -u option) <https://dev.gnupg.org/T3416> recently and
I've been working on a fix for it. But since I've got little knowledge
about GPG internals, it'd be nice if I can have some feedback and
suggestions from you and maybe get the changes merged if they do make
sense :)

Basically what I did was simply copying the code about smart card from
build_sk_list in skclist.c to finish_lookup in getkey.c so that cards
are always checked unless a user specifies a subkey suffixed with an
exclamation mark (patch based on v2.2.15):

diff --git a/g10/getkey.c b/g10/getkey.c
index c4afe4510..0f9839f73 100644
--- a/g10/getkey.c
+++ b/g10/getkey.c
@@ -3443,8 +3443,28 @@ finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact,
{
kbnode_t nextk;
int n_subkeys = 0;
int n_revoked_or_expired = 0;
+ struct agent_card_info_s card_info;
+ gpg_error_t card_err = 0;
+ int pk_card = 0;
+ int latest_key_card = 0;
+ char fpr[MAX_FINGERPRINT_LEN];
+ if (!foundk)
+ {
+ /* Check if a card is available. If any, use the key as a hint. */
+ char *serialno;
+ memset (&card_info, 0, sizeof(card_info));
+ card_err = agent_scd_serialno (&serialno, NULL);
+ if (!card_err)
+ {
+ xfree (serialno);
+ card_err = agent_scd_getattr ("KEY-FPR", &card_info);
+ if (card_err)
+ log_error ("error retrieving key fingerprint from card: %s\n",
+ gpg_strerror (card_err));
+ }
+ }

/* Either start a loop or check just this one subkey. */
for (k = foundk ? foundk : keyblock; k; k = nextk)
{
@@ -3513,12 +3533,19 @@ finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact,
/* In case a key has a timestamp of 0 set, we make sure
that it is used. A better change would be to compare
">=" but that might also change the selected keys and
is as such a more intrusive change. */
- if (pk->timestamp > latest_date || (!pk->timestamp && !latest_date))
+ fingerprint_from_pk(pk, fpr, NULL);
+ if (!(foundk || card_err))
+ pk_card = (card_info.fpr1valid && !strncmp(card_info.fpr1, fpr, MAX_FINGERPRINT_LEN))
+ || (card_info.fpr2valid && !strncmp(card_info.fpr2, fpr, MAX_FINGERPRINT_LEN))
+ || (card_info.fpr3valid && !strncmp(card_info.fpr3, fpr, MAX_FINGERPRINT_LEN));
+ if ((pk->timestamp > latest_date || (!pk->timestamp && !latest_date))
+ && (pk_card || !latest_key_card))
{
latest_date = pk->timestamp;
latest_key = k;
+ latest_key_card = pk_card;
}
}
if (n_subkeys == n_revoked_or_expired && r_flags)
*r_flags |= LOOKUP_ALL_SUBKEYS_EXPIRED;
diff --git a/g10/gpgv.c b/g10/gpgv.c
index c142cef86..c01d66425 100644
--- a/g10/gpgv.c
+++ b/g10/gpgv.c
@@ -620,8 +620,16 @@ agent_scd_getattr (const char *name, struct agent_card_info_s *info)
(void)name;
(void)info;
return 0;
}
+
+int
+agent_scd_serialno (char **r_serialno, const char *demand)
+{
+ (void)r_serialno;
+ (void)demand;
+ return 0;
+}
#endif /* ENABLE_CARD_SUPPORT */

/* We do not do any locking, so use these stubs here */
void
diff --git a/g10/test-stubs.c b/g10/test-stubs.c
index 1e1363266..e53e49a78 100644
--- a/g10/test-stubs.c
+++ b/g10/test-stubs.c
@@ -383,8 +383,16 @@ agent_scd_getattr (const char *name, struct agent_card_info_s *info)
(void)name;
(void)info;
return 0;
}
+
+int
+agent_scd_serialno (char **r_serialno, const char *demand)
+{
+ (void)r_serialno;
+ (void)demand;
+ return 0;
+}
#endif /* ENABLE_CARD_SUPPORT */

/* We do not do any locking, so use these stubs here */
void

Then it seems that there's actually no need to check smart cards in
build_sk_list as the program goes through finish_lookup anyway, so
to remove those codes:

diff --git a/g10/skclist.c b/g10/skclist.c
index 78890dc42..8bc1300b6 100644
--- a/g10/skclist.c
+++ b/g10/skclist.c
@@ -128,29 +128,14 @@ build_sk_list (ctrl_t ctrl,
are in batch mode, die. */

if (!locusr) /* No user ids given - use the card key or the default key. */
{
- struct agent_card_info_s info;
PKT_public_key *pk;
- char *serialno;

- memset (&info, 0, sizeof(info));
pk = xmalloc_clear (sizeof *pk);
pk->req_usage = use;

- /* Check if a card is available. If any, use the key as a hint. */
- err = agent_scd_serialno (&serialno, NULL);
- if (!err)
- {
- xfree (serialno);
- err = agent_scd_getattr ("KEY-FPR", &info);
- if (err)
- log_error ("error retrieving key fingerprint from card: %s\n",
- gpg_strerror (err));
- }
-
- err = get_seckey_default_or_card (ctrl, pk,
- info.fpr1valid? info.fpr1 : NULL, 20);
+ err = get_seckey_default (ctrl, pk);
if (err)
{
free_public_key (pk);
pk = NULL;

I tested these patches locally and everything seemed to work fine.
But please do let me know if there are any problems and it'd be
great if you can help me improve them. Thanks.

PS: Sorry I turned on HTML by mistake in my last email.

--
Best Regards,
Frederick Zhang

Email: frederick888@tsundere.moe
Suggestion needed: fix T3416 gpg should select available signing key on card (even with -u option) [ In reply to ]
Hi guys,

I encountered the issue of T3416 gpg should select available signing key
on card (even with -u option) <https://dev.gnupg.org/T3416> recently and
I've been working on a fix for it. But since I've got little knowledge
about GPG internals, it'd be nice if I can have some feedback and
suggestions from you and maybe get the changes merged if they do make
sense :)

Basically what I did was simply copying the code about smart card from
build_sk_list in skclist.c to finish_lookup in getkey.c so that cards
are always checked unless a user specifies a subkey suffixed with an
exclamation mark (patch based on v2.2.15):

diff --git a/g10/getkey.c b/g10/getkey.c
index c4afe4510..0f9839f73 100644
--- a/g10/getkey.c
+++ b/g10/getkey.c
@@ -3443,8 +3443,28 @@ finish_lookup (kbnode_t keyblock, unsigned int
req_usage, int want_exact,
     {
       kbnode_t nextk;
       int n_subkeys = 0;
       int n_revoked_or_expired = 0;
+      struct agent_card_info_s card_info;
+      gpg_error_t card_err = 0;
+      int pk_card = 0;
+      int latest_key_card = 0;
+      char fpr[MAX_FINGERPRINT_LEN];
+      if (!foundk)
+          {
+              /* Check if a card is available.  If any, use the key as
a hint.  */
+              char *serialno;
+              memset (&card_info, 0, sizeof(card_info));
+              card_err = agent_scd_serialno (&serialno, NULL);
+              if (!card_err)
+                {
+                  xfree (serialno);
+                  card_err = agent_scd_getattr ("KEY-FPR", &card_info);
+                  if (card_err)
+                    log_error ("error retrieving key fingerprint from
card: %s\n",
+                            gpg_strerror (card_err));
+                }
+          }
 
       /* Either start a loop or check just this one subkey.  */
       for (k = foundk ? foundk : keyblock; k; k = nextk)
     {
@@ -3513,12 +3533,19 @@ finish_lookup (kbnode_t keyblock, unsigned int
req_usage, int want_exact,
       /* In case a key has a timestamp of 0 set, we make sure
          that it is used.  A better change would be to compare
          ">=" but that might also change the selected keys and
          is as such a more intrusive change.  */
-      if (pk->timestamp > latest_date || (!pk->timestamp && !latest_date))
+          fingerprint_from_pk(pk, fpr, NULL);
+          if (!(foundk || card_err))
+              pk_card = (card_info.fpr1valid &&
!strncmp(card_info.fpr1, fpr, MAX_FINGERPRINT_LEN))
+                  || (card_info.fpr2valid && !strncmp(card_info.fpr2,
fpr, MAX_FINGERPRINT_LEN))
+                  || (card_info.fpr3valid && !strncmp(card_info.fpr3,
fpr, MAX_FINGERPRINT_LEN));
+          if ((pk->timestamp > latest_date || (!pk->timestamp &&
!latest_date))
+                  && (pk_card || !latest_key_card))
         {
           latest_date = pk->timestamp;
           latest_key = k;
+          latest_key_card = pk_card;
         }
     }
       if (n_subkeys == n_revoked_or_expired && r_flags)
         *r_flags |= LOOKUP_ALL_SUBKEYS_EXPIRED;
diff --git a/g10/gpgv.c b/g10/gpgv.c
index c142cef86..c01d66425 100644
--- a/g10/gpgv.c
+++ b/g10/gpgv.c
@@ -620,8 +620,16 @@ agent_scd_getattr (const char *name, struct
agent_card_info_s *info)
   (void)name;
   (void)info;
   return 0;
 }
+
+int
+agent_scd_serialno (char **r_serialno, const char *demand)
+{
+  (void)r_serialno;
+  (void)demand;
+  return 0;
+}
 #endif /* ENABLE_CARD_SUPPORT */
 
 /* We do not do any locking, so use these stubs here */
 void
diff --git a/g10/test-stubs.c b/g10/test-stubs.c
index 1e1363266..e53e49a78 100644
--- a/g10/test-stubs.c
+++ b/g10/test-stubs.c
@@ -383,8 +383,16 @@ agent_scd_getattr (const char *name, struct
agent_card_info_s *info)
   (void)name;
   (void)info;
   return 0;
 }
+
+int
+agent_scd_serialno (char **r_serialno, const char *demand)
+{
+  (void)r_serialno;
+  (void)demand;
+  return 0;
+}
 #endif /* ENABLE_CARD_SUPPORT */
 
 /* We do not do any locking, so use these stubs here */
 void

Then it seems that there's actually no need to check smart cards in
build_sk_list as the program goes through finish_lookup anyway, so to
remove those codes:

diff --git a/g10/skclist.c b/g10/skclist.c
index 78890dc42..8bc1300b6 100644
--- a/g10/skclist.c
+++ b/g10/skclist.c
@@ -128,29 +128,14 @@ build_sk_list (ctrl_t ctrl,
      are in batch mode, die.  */
 
   if (!locusr) /* No user ids given - use the card key or the default
key.  */
     {
-      struct agent_card_info_s info;
       PKT_public_key *pk;
-      char *serialno;
 
-      memset (&info, 0, sizeof(info));
       pk = xmalloc_clear (sizeof *pk);
       pk->req_usage = use;
 
-      /* Check if a card is available.  If any, use the key as a hint.  */
-      err = agent_scd_serialno (&serialno, NULL);
-      if (!err)
-        {
-          xfree (serialno);
-          err = agent_scd_getattr ("KEY-FPR", &info);
-          if (err)
-            log_error ("error retrieving key fingerprint from card: %s\n",
-                       gpg_strerror (err));
-        }
-
-      err = get_seckey_default_or_card (ctrl, pk,
-                                        info.fpr1valid? info.fpr1 :
NULL, 20);
+      err = get_seckey_default (ctrl, pk);
       if (err)
     {
       free_public_key (pk);
       pk = NULL;

I tested these patches locally and everything seemed to work fine. But
please do let me know if there are any problems and it'd be great if you
can help me improve them. Thanks.

--
Best Regards,
Frederick Zhang

Email: frederick888@tsundere.moe
Re: Suggestion needed: fix T3416 gpg should select available signing key on card (even with -u option) [ In reply to ]
Frederick Zhang via Gnupg-devel <gnupg-devel@gnupg.org> wrote:
> I encountered the issue of T3416 gpg should select available signing key
> on card (even with -u option) <https://dev.gnupg.org/T3416> recently and
> I've been working on a fix for it. But since I've got little knowledge
> about GPG internals, it'd be nice if I can have some feedback and
> suggestions from you and maybe get the changes merged if they do make
> sense :)

Please describe your problem, what you need/want, what to be achieved,
etc., at first. It is more helpful than a patch.

In my opinion, your approach of changing key search algorithm
(finish_lookup) looks not good, because the impact is larger.
--

_______________________________________________
Gnupg-devel mailing list
Gnupg-devel@gnupg.org
http://lists.gnupg.org/mailman/listinfo/gnupg-devel
Re: Suggestion needed: fix T3416 gpg should select available signing key on card (even with -u option) [ In reply to ]
The problem I had was rather similar as T3416. I've got 2 YubiKeys
loaded with two sets of signing/authentication subkeys. One of them is
supposed to be a backup key and they should be able to be used
interchangeably without any hiccup.

For example, here I've listed the subkeys I have, and the two YubiKeys,
i.e. #1 and #2, store different keys respectively:

sec# rsa4096/0x0D8BC11D 2019-01-23 [SC] [expires: 2020-01-23]
Card serial no. = 0006 00000001
Card serial no. = 0006 00000001
Card serial no. = 0006 00000001
Card serial no. = 0006 00000002
Card serial no. = 0006 00000002

Now the problem is, when I'm using #1 and trying to sign something, e.g.
git commit, because git specifies `-u` and the signing key 0x5FF72AA3 in
#2 is newer, GPG asks me to plug in #2 although it should be able to
sign the commit using 0x361BE1AE in #1 just fine.

So basically I'd expect the same behaviour as what GPG currently does
when no `-u` option is specified, where it prefers the key that's
currently available either locally on disk or from a smart card (oops,
just realised that I didn't take the on-disk keys into account in my
patch). And only when none of those private keys are available, it asks
the user to plug in the smart card which holds the latest subkey.

But even with this logic implemented, it still does not fully resolve my
problem, because I don't want to have different encryption subkeys on my
YubiKeys, which would confuse others and require me to always have both
cards to decrypt my files. Currently "keytocard" replaces the keygrip
with a shadow key (which I don't think works pretty intuitively in case
of multiple smart cards, as it requires users to manually back up the
subkey beforehand to transfer the same key to multiple cards) that
associates the subkey with a smart card, e.g. #1 above. So now every
time I plug in #2 I have to manually delete the shadow key and somehow
trigger "agent_card_learn" to use the same encryption subkey on #2,
otherwise GPG would ask me to plug in #1, and vice versa.

I can somewhat live with this problem as I've got only 2 cards. But
consider the scenario that vsrinu26f mentioned in T3416 where an admin
distributes a bunch of smart cards with the same subkey to users, things
could really get out of hand.

A quick solution I've got in mind would be allowing agent_card_learn to
overwrite existing shadow keys, but modifying files silently in the
background doesn't sound quite safe.

Another solution, which I don't know whether is actually feasible or
not, is:

1. storing different shadow keys for different smart cards, e.g.
using HEX_KEYGRIP-HEX_SMART_CART_SERIALNO.key as the file name
2. leaving the original keygrip untouched after "keytocard"

If this is possible, not only it would allow users to track which key is
stored in multiple smart cards and use whichever card they want, but
also make the process of duplicating smart cards much smoother.


On 10/4/19 9:58 am, NIIBE Yutaka wrote:
> Frederick Zhang via Gnupg-devel <gnupg-devel@gnupg.org> wrote:
>> I encountered the issue of T3416 gpg should select available signing key
>> on card (even with -u option) <https://dev.gnupg.org/T3416> recently and
>> I've been working on a fix for it. But since I've got little knowledge
>> about GPG internals, it'd be nice if I can have some feedback and
>> suggestions from you and maybe get the changes merged if they do make
>> sense :)
>
> Please describe your problem, what you need/want, what to be achieved,
> etc., at first. It is more helpful than a patch.
>
> In my opinion, your approach of changing key search algorithm
> (finish_lookup) looks not good, because the impact is larger.
>

--
Best Regards,
Frederick Zhang

Email: frederick888@tsundere.moe