Page MenuHome GnuPG

OpenPGP app overwrites Yubikey serial number
Closed, ResolvedPublic

Description

I'm using a Yubikey 5.

If the OpenPGP app is disabled, then gpg-card shows a "Displayed s/n" of the form "yk-1234567". If the OpenPGP app is enabled, then gpg-card shows a "Displayed s/n" of the form "MMMM01234567" (where MMMM is the manufacturer id).

Even worse: If the OpenPGP app is disabled, then scdaemon returns the nice Yubikey-specific display serial number:

$ gpg-connect-agent 'SCD SWITCHAPP piv' 'SCD GETATTR $DISPSERIALNO' /bye
S SERIALNO FF020001008A7796 piv
OK
S $DISPSERIALNO yk-9074582
OK

But, if the OpenPGP app is enabled, then the same command fails:

$ gpg-connect-agent 'SCD SWITCHAPP piv' 'SCD GETATTR $DISPSERIALNO' /bye
S SERIALNO D2760001240102010006090745820000 piv openpgp
OK
ERR 100663384 Invalid name <SCD>

I think the problem is that app_select_openpgp() overwrites card->serialno which was set by app_new_register(). This may make sense for non-Yubikey cards/tokens, but it doesn't seem to be sensible for Yubikey tokens.

Event Timeline

(I don't know well about Yubikey specific serial number.)

To avoid overwriting, we would need following change:

diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
index a6c39ed68..9979df322 100644
--- a/scd/app-openpgp.c
+++ b/scd/app-openpgp.c
@@ -6079,9 +6079,15 @@ app_select_openpgp (app_t app)
       app->appversion |= buffer[7];
       manufacturer = (buffer[8]<<8 | buffer[9]);
 
-      xfree (app->card->serialno);
-      app->card->serialno = buffer;
-      app->card->serialnolen = buflen;
+      if (app->card->cardtype != CARDTYPE_YUBIKEY)
+        {
+          xfree (app->card->serialno);
+          app->card->serialno = buffer;
+          app->card->serialnolen = buflen;
+        }
+      else
+        xfree (buffer);
+
       buffer = NULL;
       app->app_local = xtrycalloc (1, sizeof *app->app_local);
       if (!app->app_local)

No, the above patch makes OpenPGP app stop working.

Because OpenPGP app checks its serial number.

But changing just the displayed S/N should not disturb anything.

I created this patch D509: Yubikey supports two (or more) apps, serial number problem.

I found this breaks current gpg-card, which expects receiving OpenPGP card's serialno (instead of PIV's) for "SCD card_list" command.

SCD commands:

  • DEVINFO
    • returns app apecific serialno
  • SERIALNO
    • returns app specific serialno
  • LEARN
    • returns canonical serialno
gniibe changed the task status from Open to Testing.Oct 27 2020, 6:42 AM
gniibe edited projects, added gnupg, Restricted Project; removed gnupg (gpg22).
gniibe triaged this task as Normal priority.Oct 28 2020, 2:45 AM

I have tested this with Kleopatra. The good news is that SCD GETATTR $DISPSERIALNO now works for the piv app even if the openpgp app is enabled.

But I'm not sure that returning different serialno by different commands makes sense. I'll quote the relevant scdaemon log entries when starting Kleopatra with a Yubikey inserted.

Log entries for start of device info watcher thread

2020-10-28 09:32:13 scdaemon[5160] DBG: chan_9 -> OK GNU Privacy Guard's Smartcard server ready

2020-10-28 09:32:13 scdaemon[5160] DBG: chan_9 <- DEVINFO --watch
2020-10-28 09:32:13 scdaemon[5160] DBG: chan_9 -> S DEVINFO_START
2020-10-28 09:32:13 scdaemon[5160] DBG: chan_9 -> S DEVINFO_END

2020-10-28 09:32:14 scdaemon[5160] DBG: chan_9 -> S DEVINFO_STATUS new
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_9 -> S DEVINFO_START
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_9 -> S DEVICE yubikey D2760001240102010006090745820000 openpgp
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_9 -> S DEVINFO_END

Log entries for reading card information (I have left out some commands which do not involve the serialno like GETATTR MANUFACTURER, READKEY, and READCERT).

2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> OK GNU Privacy Guard's Smartcard server ready

2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 <- SERIALNO --all
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> S SERIALNO D2760001240102010006090745820000
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> OK

2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 <- GETINFO all_active_apps
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> S SERIALNO FF020001008A7796 openpgp piv
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> OK

2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 <- SWITCHCARD FF020001008A7796
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> S SERIALNO FF020001008A7796
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> OK

2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 <- SWITCHAPP openpgp
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> S SERIALNO FF020001008A7796 openpgp piv
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> OK

2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 <- LEARN --force
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> S READER 1050:0407:X:0
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> S SERIALNO FF020001008A7796
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> S CARDTYPE yubikey
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> S CARDVERSION 50102
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> S APPTYPE openpgp
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> S APPVERSION 201
[...]
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> OK

2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 <- GETATTR $DISPSERIALNO
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> S $DISPSERIALNO 000609074582
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> OK

2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 <- SWITCHCARD FF020001008A7796
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> S SERIALNO FF020001008A7796
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> OK

2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 <- SWITCHAPP piv
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> S SERIALNO FF020001008A7796 piv openpgp
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> OK

2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 <- LEARN --force
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> S READER 1050:0407:X:0
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> S SERIALNO FF020001008A7796
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> S CARDTYPE yubikey
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> S CARDVERSION 50102
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> S APPTYPE piv
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> S APPVERSION 100
[...]
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> OK

2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 <- GETATTR $DISPSERIALNO
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> S $DISPSERIALNO yk-9074582
2020-10-28 09:32:14 scdaemon[5160] DBG: chan_18 -> OK

By chance, Kleopatra ignores the serialno returned by DEVINFO --watch and by SERIALNO --all and therefore it does not get confused by the different serialno. For example, whenever DEVINFO reports a status change (i.e. new or removal), Kleopatra simply re-reads the card information instead of checking the device information between DEVINFO_START and DEVINFO_END. If Kleopatra would interpret the serialno returned by DEVINFO --watch, then it would ask for a card D2760001240102010006090745820000 (e.g. with LEARN) and get a reply with serialno FF020001008A7796. Kleopatra would likely raise an error because the serialno in the reply doesn't match the requested serialno. (Kleopatra does these checks when issuing a SWITCHCARD or SWITCHAPP command.) As I wrote, this doesn't happen because, by chance, Kleopatra mostly ignores the replies of DEVINFO and SERIALNO.

So, even if Kleopatra does not get confused about the different serialno, I question the usefulness of different commands returning different serialno. What's the use case for this?

We should find a way to figure out the OpenPGP S/N even if OpenPGP is disabled. I'll ask Yubico.

I just noticed that gpg --card-status now prints a bogus OpenPGP version number for my Yubikey. And it prints an empty serial number.

# gpg --card-status
Reader ...........: 1050:0407:X:0
Application ID ...: FF020001008A7796
Application type .: OpenPGP
Version ..........: 77.96
Manufacturer .....: Yubico
Serial number ....:

Version should be 2.1 and serial number should be 09074582. Both values are derived from the OpenPGP s/n, so I guess it's more or less expected that it does not work currently (even though OpenPGP is enabled).

Confirmed.

Let me describe current situation.

In scdaemon, "LEARN" command reports "SERIALNO" with canonical one.

In the --card-status implementation, it interprets the status report (canonical serial number) as "Application ID" of OpenPGP card and it derives information from that.

Possible fix will be (either, or both):
(1) Fix the latter, to get real "Application ID".
(2) Use OpenPGP S/N as canonical serial number, even if OpenPGP is disabled (when we will know how).

Fixing --card-status is definitely a good idea. gpg-card shows almost the same information as gpg --card-status except that it shows the correct "Version" and "Serial number". It would probably make sense to unify the code of --card-status and gpg-card's list command.

BTW, the idea is to fade out support for gpg --card-status and --card-edit. Thus no new features there. New features shall only go into gpg-card.

How about distinguishing CARDNO and application specific SERIALNO?

diff --git a/g10/call-agent.c b/g10/call-agent.c
index 1924f0f05..608dadfed 100644
--- a/g10/call-agent.c
+++ b/g10/call-agent.c
@@ -413,7 +413,7 @@ unhexify_fpr (const char *hexstr, unsigned char *fpr, unsigned int fprlen)
    allocated string.  We make sure that only hex characters are
    returned. */
 static char *
-store_serialno (const char *line)
+store_hexno (const char *line)
 {
   const char *s;
   char *p;
@@ -486,6 +486,7 @@ agent_release_card_info (struct agent_card_info_s *info)
 
   xfree (info->reader); info->reader = NULL;
   xfree (info->manufacturer_name); info->manufacturer_name = NULL;
+  xfree (info->cardno); info->cardno = NULL;
   xfree (info->serialno); info->serialno = NULL;
   xfree (info->apptype); info->apptype = NULL;
   xfree (info->disp_name); info->disp_name = NULL;
@@ -526,10 +527,15 @@ learn_status_cb (void *opaque, const char *line)
       xfree (parm->reader);
       parm->reader = unescape_status_string (line);
     }
+  else if (keywordlen == 6 && !memcmp (keyword, "CARDNO", keywordlen))
+    {
+      xfree (parm->cardno);
+      parm->cardno = store_hexno (line);
+    }
   else if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen))
     {
       xfree (parm->serialno);
-      parm->serialno = store_serialno (line);
+      parm->serialno = store_hexno (line);
       parm->is_v2 = (strlen (parm->serialno) >= 16
                      && xtoi_2 (parm->serialno+12) >= 2 );
     }
@@ -1609,7 +1615,7 @@ card_cardlist_cb (void *opaque, const char *line)
   while (spacep (line))
     line++;
 
-  if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen))
+  if (keywordlen == 6 && !memcmp (keyword, "CARDNO", keywordlen))
     {
       const char *s;
       int n;
diff --git a/g10/call-agent.h b/g10/call-agent.h
index 4a66af2aa..218e81882 100644
--- a/g10/call-agent.h
+++ b/g10/call-agent.h
@@ -34,6 +34,7 @@ struct agent_card_info_s
   char *apptype;     /* Malloced application type string.  */
   unsigned int manufacturer_id;
   char *manufacturer_name; /* malloced.  */
+  char *cardno;      /* malloced hex string. */
   char *serialno;    /* malloced hex string. */
   char *disp_name;   /* malloced. */
   char *disp_lang;   /* malloced. */
diff --git a/scd/app.c b/scd/app.c
index 872eb0ab2..5fdfbe932 100644
--- a/scd/app.c
+++ b/scd/app.c
@@ -36,7 +36,7 @@
 static gpg_error_t
 select_additional_application_internal (card_t card, apptype_t req_apptype);
 static gpg_error_t
-send_serialno_and_app_status (card_t card, int with_apps, ctrl_t ctrl);
+send_cardno_and_app_status (card_t card, int with_apps, ctrl_t ctrl);
 static gpg_error_t run_reselect (ctrl_t ctrl, card_t c, app_t a, app_t a_prev);
 
 /* Lock to protect the list of cards and its associated
@@ -828,7 +828,7 @@ app_switch_current_card (ctrl_t ctrl,
     }
 
   /* Print the status line.  */
-  err = send_serialno_and_app_status (ctrl->card_ctx, 0, ctrl);
+  err = send_cardno_and_app_status (ctrl->card_ctx, 0, ctrl);
 
  leave:
   npth_mutex_unlock (&card_list_lock);
@@ -2127,7 +2127,7 @@ compare_card_list_items (const void *arg_a, const void *arg_b)
 
 /* Helper for send_card_and_app_list and app_switch_active_app.  */
 static gpg_error_t
-send_serialno_and_app_status (card_t card, int with_apps, ctrl_t ctrl)
+send_cardno_and_app_status (card_t card, int with_apps, ctrl_t ctrl)
 {
   gpg_error_t err;
   app_t a;
@@ -2177,11 +2177,11 @@ send_serialno_and_app_status (card_t card, int with_apps, ctrl_t ctrl)
           xfree (serial);
           return err;
         }
-      send_status_direct (ctrl, "SERIALNO", p);
+      send_status_direct (ctrl, "CARDNO", p);
       xfree (p);
     }
   else
-    send_status_direct (ctrl, "SERIALNO", serial);
+    send_status_direct (ctrl, "CARDNO", serial);
 
   xfree (serial);
   return 0;
@@ -2219,7 +2219,7 @@ send_card_and_app_list (ctrl_t ctrl, card_t wantcard, int with_apps)
     {
       if (wantcard && wantcard != cardlist[n])
         continue;
-      err = send_serialno_and_app_status (cardlist[n], with_apps, ctrl);
+      err = send_cardno_and_app_status (cardlist[n], with_apps, ctrl);
       if (err)
         goto leave;
     }
@@ -2288,7 +2288,7 @@ app_switch_active_app (card_t card, ctrl_t ctrl, const char *appname)
     }
 
   /* Print the status line.  */
-  err = send_serialno_and_app_status (card, 1, ctrl);
+  err = send_cardno_and_app_status (card, 1, ctrl);
 
  leave:
   unlock_card (card);
diff --git a/scd/command.c b/scd/command.c
index 0f31218e8..044766ba5 100644
--- a/scd/command.c
+++ b/scd/command.c
@@ -525,6 +525,17 @@ cmd_learn (assuan_context_t ctx, char *line)
       if (!serial)
         return gpg_error (GPG_ERR_INV_VALUE);
 
+      rc = assuan_write_status (ctx, "CARDNO", serial);
+      if (rc < 0)
+        {
+          xfree (serial);
+          return out_of_core ();
+        }
+
+      serial = card_get_serialno (ctrl->card_ctx, 0);
+      if (!serial)
+        return gpg_error (GPG_ERR_INV_VALUE);
+
       rc = assuan_write_status (ctx, "SERIALNO", serial);
       if (rc < 0)
         {
diff --git a/tools/card-call-scd.c b/tools/card-call-scd.c
index 1f7008902..766759669 100644
--- a/tools/card-call-scd.c
+++ b/tools/card-call-scd.c
@@ -140,6 +140,7 @@ release_card_info (card_info_t info)
   xfree (info->reader); info->reader = NULL;
   xfree (info->cardtype); info->cardtype = NULL;
   xfree (info->manufacturer_name); info->manufacturer_name = NULL;
+  xfree (info->cardno); info->cardno = NULL;
   xfree (info->serialno); info->serialno = NULL;
   xfree (info->dispserialno); info->dispserialno = NULL;
   xfree (info->apptypestr); info->apptypestr = NULL;
@@ -356,8 +357,8 @@ start_agent (unsigned int flags)
             }
         }
 
-      if (!err && info.serialno)
-        gnupg_status_printf (STATUS_CARDCTRL, "3 %s", info.serialno);
+      if (!err && info.cardno)
+        gnupg_status_printf (STATUS_CARDCTRL, "3 %s", info.cardno);
 
       release_card_info (&info);
     }
@@ -401,7 +402,7 @@ unhexify_fpr (const char *hexstr, unsigned char *fpr, unsigned int fprlen)
  * allocated string.  We make sure that only hex characters are
  * returned.  Returns NULL on error. */
 static char *
-store_serialno (const char *line)
+store_hexno (const char *line)
 {
   const char *s;
   char *p;
@@ -532,7 +533,8 @@ dummy_data_cb (void *opaque, const void *buffer, size_t length)
   return 0;
 }
 
-/* A simple callback used to return the serialnumber of a card.  */
+/* A simple callback used to return the apprication specific
+   serialnumber of a card.  */
 static gpg_error_t
 get_serialno_cb (void *opaque, const char *line)
 {
@@ -566,9 +568,9 @@ get_serialno_cb (void *opaque, const char *line)
 }
 
 
-/* Make the card with SERIALNO the current one.  */
+/* Make the card with CARDNO the current one.  */
 gpg_error_t
-scd_switchcard (const char *serialno)
+scd_switchcard (const char *cardno)
 {
   int err;
   char line[ASSUAN_LINELENGTH];
@@ -577,7 +579,7 @@ scd_switchcard (const char *serialno)
   if (err)
     return err;
 
-  snprintf (line, DIM(line), "SCD SWITCHCARD -- %s", serialno);
+  snprintf (line, DIM(line), "SCD SWITCHCARD -- %s", cardno);
   return assuan_transact (agent_ctx, line,
                           NULL, NULL, NULL, NULL,
                           NULL, NULL);
@@ -778,6 +780,11 @@ learn_status_cb (void *opaque, const char *line)
             parm->cafpr3len = unhexify_fpr (line, parm->cafpr3,
                                             sizeof parm->cafpr3);
         }
+      else if (!memcmp (keyword, "CARDNO", keywordlen))
+        {
+          xfree (parm->cardno);
+          parm->cardno = store_hexno (line);
+        }
       break;
 
     case 7:
@@ -821,7 +828,7 @@ learn_status_cb (void *opaque, const char *line)
       if (!memcmp (keyword, "SERIALNO", keywordlen))
         {
           xfree (parm->serialno);
-          parm->serialno = store_serialno (line);
+          parm->serialno = store_hexno (line);
           parm->is_v2 = (strlen (parm->serialno) >= 16
                          && xtoi_2 (parm->serialno+12) >= 2 );
         }
@@ -1515,7 +1522,7 @@ card_cardlist_cb (void *opaque, const char *line)
   while (spacep (line))
     line++;
 
-  if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen))
+  if (keywordlen == 6 && !memcmp (keyword, "CARDNO", keywordlen))
     {
       const char *s;
       int n;
diff --git a/tools/gpg-card.c b/tools/gpg-card.c
index 9898c5e31..b89e15a35 100644
--- a/tools/gpg-card.c
+++ b/tools/gpg-card.c
@@ -1045,8 +1045,8 @@ list_card (card_info_t info, int no_key_lookup)
     tty_fprintf (fp, "Card type ........: %s\n", info->cardtype);
   if (info->cardversion)
     print_a_version (fp, "Card firmware ....:", info->cardversion);
-  tty_fprintf (fp, "Serial number ....: %s\n",
-               info->serialno? info->serialno : "[none]");
+  tty_fprintf (fp, "Card number ......: %s\n",
+               info->cardno? info->cardno : "[none]");
   tty_fprintf (fp, "Application type .: %s%s%s%s\n",
                app_type_string (info->apptype),
                info->apptype == APP_TYPE_UNKNOWN && info->apptypestr? "(":"",
@@ -1091,15 +1091,15 @@ print_card_list (estream_t fp, card_info_t info, strlist_t cards,
 
   for (count = 0, sl = cards; sl; sl = sl->next, count++)
     {
-      if (info && info->serialno)
+      if (info && info->cardno)
         {
           s = strchr (sl->d, ' ');
           if (s)
             snlen = s - sl->d;
           else
             snlen = strlen (sl->d);
-          star = (strlen (info->serialno) == snlen
-                  && !memcmp (info->serialno, sl->d, snlen));
+          star = (strlen (info->cardno) == snlen
+                  && !memcmp (info->cardno, sl->d, snlen));
         }
       else
         star = 0;
@@ -1294,15 +1294,15 @@ cmd_list (card_info_t info, char *argstr)
             goto leave;
           for (sl = cards; sl; sl = sl->next)
             {
-              if (info && info->serialno)
+              if (info && info->cardno)
                 {
                   s = strchr (sl->d, ' ');
                   if (s)
                     snlen = s - sl->d;
                   else
                     snlen = strlen (sl->d);
-                  if (strlen (info->serialno) == snlen
-                      && !memcmp (info->serialno, sl->d, snlen))
+                  if (strlen (info->cardno) == snlen
+                      && !memcmp (info->cardno, sl->d, snlen))
                     break;
                 }
             }
diff --git a/tools/gpg-card.h b/tools/gpg-card.h
index 1622f2d5f..0d8a7ae3f 100644
--- a/tools/gpg-card.h
+++ b/tools/gpg-card.h
@@ -152,6 +152,7 @@ struct card_info_s
   unsigned int appversion; /* Version of the application.  */
   unsigned int manufacturer_id;
   char *manufacturer_name; /* malloced. */
+  char *cardno;      /* malloced hex string. */
   char *serialno;    /* malloced hex string. */
   char *dispserialno;/* malloced string. */
   char *disp_name;   /* malloced. */

I found a good enough solution: I changed the code to compute the OpenPGP s/n from the Yubikey s/n right after a Yubikey has been detected. Later, and if OpenPGP enabled on the YK, the S/N is already there but we use the S/N from the 0x4f DO. That is needed because we can't compute the OpenPGP version number ahead and use 0.0 in the S/N.

The drawback is that existing stub files won't noticed this new S/N becuase of the changed different version number (0.0 instead of 3.4). We can fix this by ignoring the version number when looking for a specific OpenPGP card.

We anyway have our own idea of a card's s/n and thus it does not matter if we use a different one than the AID. Let me clean up things before I post a patch.

Okay, I now got such a patch:

I think the calculation of the OpenPGP s/n is not correct. As you write, "Yubico seems to use the decimalized version of their S/N as the OpenPGP card S/N." This matches my observation for my Yubikey:
s/n printed on Yubikey: 9074582
Yubikey s/n (with our prefix): FF020001008A7796
OpenPGP AID: D2760001240102010006090745820000

But with the above patch the following OpenPGP AID is calculated:
D2760001240100000006008A77960000

The attached patch fixes the calculation, e.g. it calculates the OpenPGP AID
D2760001240100000006090745820000
from the Yubikey s/n FF020001008A7796. The calculation could probably be written nicer.

Argh. The following patch replaces the previous patch. It fixes the calculation of the display serial number.

Sorry, I realized this myself this morning and did couple of fixes. rG7113263a00d8 does this all however I forgot to mention the bug number.

BTW, most of the time I had to fight with (e)logind which stole by runtime directory and caused strange effects.
A touch /var/lib/elogind/linger/wk solved this. I must have installed a newer elogind version recently which changed the directory name from systemd to elogin.

We need another patch, because there are two places for gpg --card-edit and gpg-card to check OpenPGPcard's version number if it's >= 2 or not.