scd: Rework the handling of the displayed serial number.

* scd/app.c (app_new_register): Call app_munge_serialno for Yubikeys.
(app_munge_serialno): Handle Yubikey serial numbers.
(card_get_serialno): Remove special Yubikey treatment.  Drop arg
is_canonical.
(app_get_serialno): Clear ERRNO on error.
(card_get_dispserialno): New.  Also change formatting of Yubikey and
OpenPGP numbers to match those printed on the card.
(app_get_dispserialno): New.
* scd/app-openpgp.c (do_getattr): Use app_get_dispserialno.
(yubikey_get_serialno): Remove.
* scd/app-piv.c (get_dispserialno): Remove.
(do_getattr): Use app_get_dispserialno.
--

This patch gets us back to a unique serial number for cards and
provides a stable serial number as printed for Yubikeys.

Because we use a slightly different serial number now for Yubikeys and
cards only supporting OpenPGP card we need to come up with another
change so that the version number of OpenPGP serial numbers are
ignored when comparing card serial numbers.  This is so that existing
stub keys of gpg-agent will continue to work.

GnuPG-bug-id: 5100
Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2020-11-24 18:02:05 +01:00
parent c3a20c88fb
commit 3a8250c020
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
5 changed files with 180 additions and 121 deletions

View File

@ -235,9 +235,10 @@ const char *strapptype (apptype_t t);
void app_update_priority_list (const char *arg);
gpg_error_t app_send_card_list (ctrl_t ctrl);
gpg_error_t app_send_active_apps (card_t card, ctrl_t ctrl);
char *card_get_serialno (card_t card, int is_canonical);
char *card_get_serialno (card_t card);
char *app_get_serialno (app_t app);
char *yubikey_get_serialno (app_t app);
char *card_get_dispserialno (card_t card, int nofallback);
char *app_get_dispserialno (app_t app, int nofallback);
void app_dump_state (void);
void application_notify_card_reset (int slot);

View File

@ -1102,9 +1102,10 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
if (table[idx].special == -1)
{
/* The serial number is very special. We could have used the
AID DO to retrieve it. The AID DO is available anyway but
not hex formatted. */
/* The serial number is very special. We can't use the the AID
DO (0x4f) becuase this is the serialno per specs with the
correct appversion. We might however use a serialno with the
version set to 0.0 and that is what we need to return. */
char *serial = app_get_serialno (app);
if (serial)
@ -1147,17 +1148,14 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
}
if (table[idx].special == -4)
{
char *serial = app_get_serialno (app);
char *serial;
if (serial)
if ((serial = app_get_dispserialno (app, 0)))
{
if (strlen (serial) > 16+12)
{
send_status_info (ctrl, table[idx].name, serial+16, 12, NULL, 0);
xfree (serial);
return 0;
}
send_status_info (ctrl, table[idx].name,
serial, strlen (serial), NULL, 0);
xfree (serial);
return 0;
}
return gpg_error (GPG_ERR_INV_NAME);
}
@ -1383,39 +1381,6 @@ get_disp_name (app_t app)
}
/*
* Yubikey has its own serial number at app->serialno. When Yubikey
* is used for OpenPGP card app, we get the serial number for OpenPGP
* card from its AID data object.
*/
char *
yubikey_get_serialno (app_t app)
{
void *relptr;
unsigned char *buffer;
size_t buflen;
char *serial;
relptr = get_one_do (app, 0x004F, &buffer, &buflen, NULL);
if (!relptr)
return NULL;
if (buflen != 16)
{
xfree (relptr);
return NULL;
}
serial = xtrymalloc (32 + 1);
if (!serial)
return NULL;
serial[32] = 0;
bin2hex (buffer, buflen, serial);
xfree (relptr);
return serial;
}
/* Return the pretty formatted serialnumber. On error NULL is
* returned. */
static char *
@ -6113,7 +6078,15 @@ app_select_openpgp (app_t app)
app->appversion |= buffer[7];
manufacturer = (buffer[8]<<8 | buffer[9]);
/* For Yubikey, serialno is set in app.c, already. */
/* For Yubikey, serialno is set in app.c, already. The problem
* is that the OpenPGP appversion has been set to 0.0 because we
* are not able to deduce this if the OpenPGP app has not been
* enabled. Thus we here to to use the appversion from DO 0x4f
* but return a serialno with a version 0.0 as set by app.c.
* Users of scdaemon taking the version from the serialno won't
* work anymore and need to be modified. Recall that our
* architecture requires exactly one serilano per card.
*/
if (app->card->cardtype == CARDTYPE_YUBIKEY)
xfree (buffer);
else

View File

@ -746,38 +746,6 @@ parse_chv_keyref (const char *keyrefstr)
}
/* Return an allocated string with the serial number in a format to be
* show to the user. With FAILMODE is true return NULL if such an
* abbreviated S/N is not available, else return the full serial
* number as a hex string. May return NULL on malloc problem. */
static char *
get_dispserialno (app_t app, int failmode)
{
char *result;
if (app->card && app->card->serialno && app->card->serialnolen == 3+1+4
&& !memcmp (app->card->serialno, "\xff\x02\x00", 3))
{
/* This is a 4 byte S/N of a Yubikey which seems to be printed
* on the token in decimal. Maybe they will print larger S/N
* also in decimal but we can't be sure, thus do it only for
* these 32 bit numbers. */
unsigned long sn;
sn = app->card->serialno[4] * 16777216;
sn += app->card->serialno[5] * 65536;
sn += app->card->serialno[6] * 256;
sn += app->card->serialno[7];
result = xtryasprintf ("yk-%lu", sn);
}
else if (failmode)
result = NULL; /* No Abbreviated S/N. */
else
result = app_get_serialno (app);
return result;
}
/* The verify command can be used to retrieve the security status of
* the card. Given the PIN name (e.g. "PIV.80" for the application
* pin, a ISO7817_VERIFY_* code is returned or a non-negative number
@ -842,7 +810,7 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
}
else if (table[idx].special == -3)
{
char *tmp = get_dispserialno (app, 1);
char *tmp = app_get_dispserialno (app, 1);
if (tmp)
{
@ -1789,7 +1757,7 @@ make_prompt (app_t app, int remaining, const char *firstline)
{
char *serial, *tmpbuf, *result;
serial = get_dispserialno (app, 0);
serial = app_get_dispserialno (app, 0);
if (!serial)
return NULL;

193
scd/app.c
View File

@ -302,7 +302,7 @@ app_send_devinfo (ctrl_t ctrl)
char *serialno;
char card_info[80];
serialno = card_get_serialno (c, 0);
serialno = card_get_serialno (c);
snprintf (card_info, sizeof card_info, "DEVICE %s %s",
strcardtype (c->cardtype), serialno);
xfree (serialno);
@ -514,10 +514,7 @@ app_new_register (int slot, ctrl_t ctrl, const char *name,
card->serialno[2] = 0x0;
card->serialno[3] = formfactor;
memcpy (card->serialno + 4, s0, n);
/* Note that we do not clear the error
* so that no further serial number
* testing is done. After all we just
* set the serial number. */
err = app_munge_serialno (card);
}
}
@ -559,11 +556,11 @@ app_new_register (int slot, ctrl_t ctrl, const char *name,
}
}
if (!err)
if (!err && card->cardtype != CARDTYPE_YUBIKEY)
err = iso7816_select_file (slot, 0x2F02, 0);
if (!err)
if (!err && card->cardtype != CARDTYPE_YUBIKEY)
err = iso7816_read_binary (slot, 0, 0, &result, &resultlen);
if (!err)
if (!err && card->cardtype != CARDTYPE_YUBIKEY)
{
size_t n;
const unsigned char *p;
@ -1142,7 +1139,9 @@ card_unref_locked (card_t card)
FF 00 00 = For serial numbers starting with an FF
FF 01 00 = Some german p15 cards return an empty serial number so the
serial number from the EF(TokenInfo) is used instead.
FF 02 00 = Serial number from Yubikey config
FF 02 00 = Serial number from Yubikey config.
This is normally not seen because we modify this here
to an OpenPGP Card s/n.
FF 7F 00 = No serialno.
All other serial numbers not starting with FF are used as they are.
@ -1150,7 +1149,48 @@ card_unref_locked (card_t card)
gpg_error_t
app_munge_serialno (card_t card)
{
if (card->serialnolen && card->serialno[0] == 0xff)
if (card->cardtype == CARDTYPE_YUBIKEY
&& card->serialnolen == 3 + 1 + 4
&& !memcmp (card->serialno, "\xff\x02\x00", 3))
{
/* An example for a serial number is
* FF020001008A77C1
* ~~~~~~--~~~~~~~~
* ! ! !--------- 4 byte s/n
* ! !----------- Form factor
* !----------------- Our prefix
* Yubico seems to use the decimalized version of their S/N
* as the OpenPGP card S/N. Thus in theory we can contruct the
* number from this information so that we do not rely on having
* the OpenPGP app enabled.
*/
unsigned long sn;
sn = card->serialno[4] * 16777216;
sn += card->serialno[5] * 65536;
sn += card->serialno[6] * 256;
sn += card->serialno[7];
if (sn <= 99999999ul)
{
char *buf = xtrymalloc (16);
if (!buf)
return gpg_error_from_syserror ();
memcpy (buf, "\xD2\x76\x00\x01\x24\x01", 6);
buf[6] = 0; /* Application version which we don't know */
buf[7] = 0; /* thus we use 0.0 and don't use this directly. */
buf[8] = 0; /* Manufacturer: Yubico (0x0006). */
buf[9] = 6;
buf[10] = (sn >> 24);
buf[11] = (sn >> 16);
buf[12] = (sn >> 8);
buf[13] = sn;
buf[14] = 0; /* Last two bytes are RFU. */
buf[15] = 0;
xfree (card->serialno);
card->serialno = buf;
card->serialnolen = 16;
}
}
else if (card->serialnolen && card->serialno[0] == 0xff)
{
/* The serial number starts with our special prefix. This
requires that we put our default prefix "FF0000" in front. */
@ -1182,7 +1222,7 @@ app_munge_serialno (card_t card)
returned as a malloced string (hex encoded) in SERIAL. Caller must
free SERIAL unless the function returns an error. */
char *
card_get_serialno (card_t card, int is_canonical)
card_get_serialno (card_t card)
{
char *serial;
@ -1191,31 +1231,7 @@ card_get_serialno (card_t card, int is_canonical)
if (!card->serialnolen)
serial = xtrystrdup ("FF7F00");
else if (card->cardtype == CARDTYPE_YUBIKEY && !is_canonical
&& card->app && card->app->apptype == APPTYPE_OPENPGP)
{
app_t a;
for (a = card->app; a; a = a->next)
if (a->apptype == APPTYPE_OPENPGP)
break;
/* Found the OpenPGP app */
if (a->apptype == APPTYPE_OPENPGP)
{
if (card->app != a)
run_reselect (NULL, card, a, card->app);
serial = yubikey_get_serialno (a);
if (card->app != a)
run_reselect (NULL, card, card->app, a);
}
else
goto other;
}
else
other:
serial = bin2hex (card->serialno, card->serialnolen, NULL);
return serial;
@ -1226,8 +1242,109 @@ char *
app_get_serialno (app_t app)
{
if (!app || !app->card)
return NULL;
return card_get_serialno (app->card, 0);
{
gpg_err_set_errno (0);
return NULL;
}
return card_get_serialno (app->card);
}
/* Return an allocated string with the serial number in a format to be
* show to the user. With NOFALLBACK set to true return NULL if such an
* abbreviated S/N is not available, else return the full serial
* number as a hex string. May return NULL on malloc problem. */
char *
card_get_dispserialno (card_t card, int nofallback)
{
char *result, *p;
unsigned long sn;
if (card && card->serialno && card->serialnolen == 3+1+4
&& !memcmp (card->serialno, "\xff\x02\x00", 3))
{
/* This is a 4 byte S/N of a Yubikey which seems to be printed
* on the token in decimal. Maybe they will print larger S/N
* also in decimal but we can't be sure, thus do it only for
* these 32 bit numbers. */
sn = card->serialno[4] * 16777216;
sn += card->serialno[5] * 65536;
sn += card->serialno[6] * 256;
sn += card->serialno[7];
if ((card->cardversion >> 16) >= 5)
result = xtryasprintf ("%lu %03lu %03lu",
(sn/1000000ul),
(sn/1000ul % 1000ul),
(sn % 1000ul));
else
result = xtryasprintf ("%lu", sn);
}
else if (card && card->cardtype == CARDTYPE_YUBIKEY)
{
/* Get back the printed Yubikey number from the OpenPGP AID
* Example: D2760001240100000006008A77C10000
*/
result = card_get_serialno (card);
if (result && strlen (result) >= 28 && !strncmp (result+16, "0006", 4))
{
sn = xtoi_2 (result+20) * 16777216;
sn += xtoi_2 (result+22) * 65536;
sn += xtoi_2 (result+24) * 256;
sn += xtoi_2 (result+26);
if ((card->cardversion >> 16) >= 5)
p = xtryasprintf ("%lu %03lu %03lu",
(sn/1000000ul),
(sn/1000ul % 1000ul),
(sn % 1000ul));
else
p = xtryasprintf ("%lu", sn);
if (p)
{
xfree (result);
result = p;
}
}
else if (nofallback)
{
xfree (result);
result = NULL;
}
}
else if (card && card->app && card->app->apptype == APPTYPE_OPENPGP)
{
/* Extract number from standard OpenPGP AID. */
result = card_get_serialno (card);
if (result && strlen (result) > 16+12)
{
memcpy (result, result+16, 4);
result[4] = ' ';
memcpy (result+5, result+20, 8);
result[13] = 0;
}
else if (nofallback)
{
xfree (result);
result = NULL;
}
}
else if (nofallback)
result = NULL; /* No Abbreviated S/N. */
else
result = card_get_serialno (card);
return result;
}
/* Same as card_get_dispserialno but takes an APP object. */
char *
app_get_dispserialno (app_t app, int nofallback)
{
if (!app || !app->card)
{
gpg_err_set_errno (0);
return NULL;
}
return card_get_dispserialno (app->card, nofallback);
}
@ -2136,7 +2253,7 @@ send_serialno_and_app_status (card_t card, int with_apps, ctrl_t ctrl)
membuf_t mb;
int any = 0;
serial = card_get_serialno (card, 1);
serial = card_get_serialno (card);
if (!serial)
return 0; /* Oops. */

View File

@ -345,7 +345,7 @@ cmd_serialno (assuan_context_t ctx, char *line)
return rc;
}
serial = card_get_serialno (ctrl->card_ctx, 0);
serial = card_get_serialno (ctrl->card_ctx);
if (!serial)
return gpg_error (GPG_ERR_INV_VALUE);
@ -521,7 +521,7 @@ cmd_learn (assuan_context_t ctx, char *line)
send_status_direct (ctrl, "READER", reader);
/* No need to free the string of READER. */
serial = card_get_serialno (ctrl->card_ctx, 1);
serial = card_get_serialno (ctrl->card_ctx);
if (!serial)
return gpg_error (GPG_ERR_INV_VALUE);