Page MenuHome GnuPG

gpg should select available signing key on card (even with -u option)
Closed, ResolvedPublic


While gpg 2.1.22 or later supports automatic selection of signing key on card, but it only works when no default key and no -u option.
When multiple subkeys are attached on a user's primary key, and only one is available on card, why not select it?

Event Timeline

Hi, currently to be able to use 2 different cards with 2 different sets of subkeys from the same primary key (home and work) I need to manually delete ~/.gnupg/private-keys-v1.d/* everytime I want to switch from the first card to the second.

gpg --sign works as intended, but all clients I use, like Evolution for emails and git for code signing, appear to be using gpg --sign -u so if the first set of subkeys is known gpg requires it even if the second set is present and the card is inserted and known to gpg. Unfortunately it's not very usable at the moment.


Is it you are moving to new sub keys? if yes do we still need outdated old
subkeys? Is it safe to cleanup old subkeys?

or is that the 3 slots on token are not enough.

What is the usecase for keeping same public key but different private keys
on two tokens?

After reading PIV and using PIV token I understood how much simple and easy
GnuPG is by design. You guys rock.

Regarding hardware tokens PIV has many slots around 33 or so to hold
expired but needed for decryption keys.

This is where GnuPG tokens lack. Can there be tokens which have many slots
and move keys between slots or better auto manage slots keeping only new x
number of sub keys.

Again without compromising security for convenience like FST-01.

The use case is having 2 different hardware tokens - I have an opengpg card which supports 4096 rsa subkeys, and a yubikey which supports 2048 rsa subkeys. At work I need one, at home the other.

GnuPG by design uses latest sub keys so in your setup office and home one
of them is latest.

I believe it is working correct if you use office token on office laptop
and home token on home laptop and not using tokens the other way.

Sorry previosly I asked for more slots for keys on token. But its not
needed one. I dont even know it is a valid request but

The needed space is to be able to hold WOT on the token to some extent to
be able to use offline only GnuPG tokens.

It is on the same machine, as I mentioned manually deleting ~/.gnupg/private-keys-v1.d/* is a workaround I have to use, but it is not very user friendly.

As far as I can understand there is no reason why an arbitrary subkey should be preferred when another is available. This now works correctly without -u in recent versions, so can it be fixed also when using -u like git and Evolution do?

What is the benefit of two subkeys?

Why dont you use two public keys to keep the separation office and home?

Because of policy requirements I have.

The point is, GPG allows to have multiple subkeys, it allows to split them between multiple cards, recently thanks to @gniibe's work it also automatically prefers a key that is present when no -u option is passed. So why not making it work also when -u is passed?

Sorry for jumping in out of the blue but the idea of automatically selecting the available signing key sounds also very appealing to me.

I've got two YubiKeys and one of them is used as a backup. Occasionally when I'm working at home and being too lazy to get my wallet for the primary one, I use the backup YubiKey instead but now suddenly Git starts asking me for the primary key, although the backup one should just work normally.

Although it's certainly not difficult for me to change Git config to specify the correct subkey, for the sake of simplicity and consistency, imho GPG should also select an available signing key in this scenario.


This is already implemented by yutaka.


Sorry, ignore my comment if there is something with subkeys and you are
already using latest gnupg.

@vsrinu26f Yes I'm using subkeys with YubiKey.

Just to clarify, I'm using 2.2.14 currently and when signing with -u option, unless "subkey!" is used GPG still sticks to the latest signing key instead of latest "available" signing key based on which YubiKey I have at the moment.

I think if we have to update one token then we have to update backup token as well if moved to new subkey.

Both tokens should have same material.


On the other hand if we want to track which token is used by having multiple unexpired signing subkeys and each token have its own subkey is a possible usecase where multiple admins have the tokens.

Both tokens should have same material.

But the stub keygrip of GnuPG can only associate a subkey with a single token. In order to make "single subkey multi token" to work, every time I switch to another token I have to manually delete the stub keygrips so that GPG can generate new ones after I plug the other token in. This is somewhat troublesome (perhaps I can tweak udev to do the job for me under Linux, but I have completely no idea how this can be achieved under macOS) so I only do this when absolutely necessary, i.e. for encryption subkey. In case of signing and authentication keys, I have two sets of different subkeys on each YubiKey respectively.

And tbh currently keytocard command updates the keygrip in-place so if a user forgets to back it up before moving the subkey to a token, it'd be impossible to transfer the same subkey to another, which doesn't seem to be a smooth workflow to me.

On the other hand if we want to track which token is used by having multiple unexpired signing subkeys and each token have its own subkey is a possible usecase where multiple admins have the tokens.

Exactly. I've got no knowledge about GPG internals but I wonder whether it's possible to have different keygrips for different tokens and optionally clear those stub keygrips up after a token is removed?

For exactly same key material on tokens. Just before writing first token
backup .gnupg folder or export all key info. Do key to card. Delete .gnupg
folder and restore from backup and keytocard second token.

I wish gnupg natively supports creating backup cards. To be able to import
private key material to do another keyto card. And every time it moves that
to card and removes from gnupg.

Sorry i think i blabbered without understanding context.

Current different subkeys on tokens is good enough

@vsrinu26f No worries, looks like we are on the same page :)

Here's an ugly hack to make this work (patch based on v2.2.15).

I basically copied the code from skclist.c:141 and put it in finish_lookup so that tokens are checked when getkey_byname is called from build_sk_list. No idea whether this is the right thing to do but at least it now signing works no matter which token I'm using. Actually I'm not sure whether the original code from skclist.c:141 can be removed or not with my fix as it seems no matter how gpg looks up the key, it always goes through lookup -> finish_lookup.

Again I don't have much knowledge about GPG internals and I haven't touched C programming for a loooong time (excuse my messy code lol). So I'd greatly appreciate it if anyone can give me some feedback or do the heavy lifting to tidy it up.

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)
   return 0;
+agent_scd_serialno (char **r_serialno, const char *demand)
+  (void)r_serialno;
+  (void)demand;
+  return 0;
 /* We do not do any locking, so use these stubs here */
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)
   return 0;
+agent_scd_serialno (char **r_serialno, const char *demand)
+  (void)r_serialno;
+  (void)demand;
+  return 0;
 /* We do not do any locking, so use these stubs here */

With new "KEYINFO" command of scdaemon, finally, we can move on to support better selection of signing key.
(Note: having a private key on multiple cards had already been solved in T4301: Handling multiple subkeys on two SmartCards.)

gniibe raised the priority of this task from Normal to High.Jan 16 2020, 5:43 AM
gniibe changed the task status from Open to Testing.Jan 17 2020, 8:12 AM
gniibe added a project: Testing.

Implemented in master.