mirror of
git://git.gnupg.org/gnupg.git
synced 2025-01-03 12:11:33 +01:00
scd:p15: Detect CardOS 5 cards and print some basic infos.
* scd/app-p15.c (read_ef_odf): Detect the home_DF on the fly. Silence the garbage warning for null bytes. (print_tokeninfo_tokenflags): New. (read_ef_tokeninfo): Print manufacturer, label, and flags. (app_select_p15): No need to use the app_get_slot macro. (CARD_TYPE_CARDOS_50): New const. (card_atr_list): Detect CardOS 5.0 -- The card under test is a "Test-Signaturkarte D-TRUST Card 3.1" for a mere 49 Euro and no specs available. D-Trust is a branch of the German Bundesdruckerei. Compare that to Telesec and Yubikey who have always been nice enough to send bunches of sample cards without a need to wade through lots of forms and not even asking for money. Guess which cards I prefer. Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
parent
c5c21a0646
commit
8a68d497f1
163
scd/app-p15.c
163
scd/app-p15.c
@ -1,5 +1,6 @@
|
|||||||
/* app-p15.c - The pkcs#15 card application.
|
/* app-p15.c - The pkcs#15 card application.
|
||||||
* Copyright (C) 2005 Free Software Foundation, Inc.
|
* Copyright (C) 2005 Free Software Foundation, Inc.
|
||||||
|
* Copyright (C) 2020 g10 Code GmbH
|
||||||
*
|
*
|
||||||
* This file is part of GnuPG.
|
* This file is part of GnuPG.
|
||||||
*
|
*
|
||||||
@ -15,6 +16,7 @@
|
|||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Information pertaining to the BELPIC developer card samples:
|
/* Information pertaining to the BELPIC developer card samples:
|
||||||
@ -48,6 +50,7 @@ typedef enum
|
|||||||
CARD_TYPE_UNKNOWN,
|
CARD_TYPE_UNKNOWN,
|
||||||
CARD_TYPE_TCOS,
|
CARD_TYPE_TCOS,
|
||||||
CARD_TYPE_MICARDO,
|
CARD_TYPE_MICARDO,
|
||||||
|
CARD_TYPE_CARDOS_50,
|
||||||
CARD_TYPE_BELPIC /* Belgian eID card specs. */
|
CARD_TYPE_BELPIC /* Belgian eID card specs. */
|
||||||
}
|
}
|
||||||
card_type_t;
|
card_type_t;
|
||||||
@ -78,7 +81,8 @@ static struct
|
|||||||
{ 26, X("\x3B\xFE\x94\x00\xFF\x80\xB1\xFA\x45\x1F\x03\x45\x73\x74\x45\x49"
|
{ 26, X("\x3B\xFE\x94\x00\xFF\x80\xB1\xFA\x45\x1F\x03\x45\x73\x74\x45\x49"
|
||||||
"\x44\x20\x76\x65\x72\x20\x31\x2E\x30\x43"),
|
"\x44\x20\x76\x65\x72\x20\x31\x2E\x30\x43"),
|
||||||
CARD_TYPE_MICARDO }, /* EstEID (Estonian Big Brother card) */
|
CARD_TYPE_MICARDO }, /* EstEID (Estonian Big Brother card) */
|
||||||
|
{ 11, X("\x3b\xd2\x18\x00\x81\x31\xfe\x58\xc9\x01\x14"),
|
||||||
|
CARD_TYPE_CARDOS_50 }, /* CardOS 5.0 */
|
||||||
{ 0 }
|
{ 0 }
|
||||||
};
|
};
|
||||||
#undef X
|
#undef X
|
||||||
@ -595,9 +599,9 @@ prkdf_object_from_keyidstr (app_t app, const char *keyidstr,
|
|||||||
|
|
||||||
A0 06 30 04 04 02 60 34 = Private Keys
|
A0 06 30 04 04 02 60 34 = Private Keys
|
||||||
A4 06 30 04 04 02 60 35 = Certificates
|
A4 06 30 04 04 02 60 35 = Certificates
|
||||||
A5 06 30 04 04 02 60 36 = TrustedCertificates
|
A5 06 30 04 04 02 60 36 = Trusted Certificates
|
||||||
A7 06 30 04 04 02 60 37 = DataObjects
|
A7 06 30 04 04 02 60 37 = Data Objects
|
||||||
A8 06 30 04 04 02 60 38 = AuthObjects
|
A8 06 30 04 04 02 60 38 = Auth Objects
|
||||||
|
|
||||||
These are all PathOrObjects using the path CHOICE element. The
|
These are all PathOrObjects using the path CHOICE element. The
|
||||||
paths are octet strings of length 2. Using this Path CHOICE
|
paths are octet strings of length 2. Using this Path CHOICE
|
||||||
@ -608,9 +612,10 @@ read_ef_odf (app_t app, unsigned short odf_fid)
|
|||||||
{
|
{
|
||||||
gpg_error_t err;
|
gpg_error_t err;
|
||||||
unsigned char *buffer, *p;
|
unsigned char *buffer, *p;
|
||||||
size_t buflen;
|
size_t buflen, n;
|
||||||
unsigned short value;
|
unsigned short value;
|
||||||
size_t offset;
|
size_t offset;
|
||||||
|
unsigned short home_df = 0;
|
||||||
|
|
||||||
err = select_and_read_binary (app_get_slot (app), odf_fid, "ODF",
|
err = select_and_read_binary (app_get_slot (app), odf_fid, "ODF",
|
||||||
&buffer, &buflen);
|
&buffer, &buflen);
|
||||||
@ -623,6 +628,8 @@ read_ef_odf (app_t app, unsigned short odf_fid)
|
|||||||
xfree (buffer);
|
xfree (buffer);
|
||||||
return gpg_error (GPG_ERR_INV_OBJ);
|
return gpg_error (GPG_ERR_INV_OBJ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
home_df = app->app_local->home_df;
|
||||||
p = buffer;
|
p = buffer;
|
||||||
while (buflen && *p && *p != 0xff)
|
while (buflen && *p && *p != 0xff)
|
||||||
{
|
{
|
||||||
@ -635,17 +642,33 @@ read_ef_odf (app_t app, unsigned short odf_fid)
|
|||||||
else if ( buflen >= 12
|
else if ( buflen >= 12
|
||||||
&& (p[0] & 0xf0) == 0xA0
|
&& (p[0] & 0xf0) == 0xA0
|
||||||
&& !memcmp (p+1, "\x0a\x30\x08\x04\x06\x3F\x00", 7)
|
&& !memcmp (p+1, "\x0a\x30\x08\x04\x06\x3F\x00", 7)
|
||||||
&& app->app_local->home_df == ((p[8]<<8)|p[9]) )
|
&& (!home_df || home_df == ((p[8]<<8)|p[9])) )
|
||||||
{
|
{
|
||||||
|
/* If we do not know the home DF, we take it from the first
|
||||||
|
* ODF object. Here are sample values:
|
||||||
|
* a0 0a 30 08 0406 3f00 5015 4401
|
||||||
|
* a1 0a 30 08 0406 3f00 5015 4411
|
||||||
|
* a4 0a 30 08 0406 3f00 5015 4441
|
||||||
|
* a5 0a 30 08 0406 3f00 5015 4451
|
||||||
|
* a8 0a 30 08 0406 3f00 5015 4481
|
||||||
|
* 00000000 */
|
||||||
|
if (!home_df)
|
||||||
|
{
|
||||||
|
home_df = ((p[8]<<8)|p[9]);
|
||||||
|
app->app_local->home_df = home_df;
|
||||||
|
log_info ("pkcs#15 application directory detected as 0x%04hX\n",
|
||||||
|
home_df);
|
||||||
|
}
|
||||||
|
|
||||||
/* We only allow a full path if all files are at the same
|
/* We only allow a full path if all files are at the same
|
||||||
level and below the home directory. The extend this we
|
level and below the home directory. To extend this we
|
||||||
would need to make use of new data type capable of
|
would need to make use of new data type capable of
|
||||||
keeping a full path. */
|
keeping a full path. */
|
||||||
offset = 10;
|
offset = 10;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
log_error ("ODF format is not supported by us\n");
|
log_printhex (p, buflen, "ODF format is not supported by us:");
|
||||||
xfree (buffer);
|
xfree (buffer);
|
||||||
return gpg_error (GPG_ERR_INV_OBJ);
|
return gpg_error (GPG_ERR_INV_OBJ);
|
||||||
}
|
}
|
||||||
@ -691,8 +714,16 @@ read_ef_odf (app_t app, unsigned short odf_fid)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (buflen)
|
if (buflen)
|
||||||
log_info ("warning: %u bytes of garbage detected at end of ODF\n",
|
{
|
||||||
(unsigned int)buflen);
|
/* Print a warning if non-null garbage is left over. */
|
||||||
|
for (n=0; n < buflen && !p[n]; n++)
|
||||||
|
;
|
||||||
|
if (n < buflen)
|
||||||
|
{
|
||||||
|
log_info ("warning: garbage detected at end of ODF: ");
|
||||||
|
log_printhex (p, buflen, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
xfree (buffer);
|
xfree (buffer);
|
||||||
return 0;
|
return 0;
|
||||||
@ -2157,6 +2188,62 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Print the BIT STRING with the tokenflags from the TokenInfo. */
|
||||||
|
static void
|
||||||
|
print_tokeninfo_tokenflags (const unsigned char *der, size_t derlen)
|
||||||
|
{
|
||||||
|
unsigned int bits, mask;
|
||||||
|
int i, unused, full;
|
||||||
|
int other = 0;
|
||||||
|
|
||||||
|
if (!derlen)
|
||||||
|
{
|
||||||
|
log_printf (" [invalid object]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unused = *der++; derlen--;
|
||||||
|
if ((!derlen && unused) || unused/8 > derlen)
|
||||||
|
{
|
||||||
|
log_printf (" [wrong encoding]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
full = derlen - (unused+7)/8;
|
||||||
|
unused %= 8;
|
||||||
|
mask = 0;
|
||||||
|
for (i=1; unused; i <<= 1, unused--)
|
||||||
|
mask |= i;
|
||||||
|
|
||||||
|
/* First octet */
|
||||||
|
if (derlen)
|
||||||
|
{
|
||||||
|
bits = *der++; derlen--;
|
||||||
|
if (full)
|
||||||
|
full--;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bits &= ~mask;
|
||||||
|
mask = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
bits = 0;
|
||||||
|
if ((bits & 0x80)) log_printf (" readonly");
|
||||||
|
if ((bits & 0x40)) log_printf (" loginRequired");
|
||||||
|
if ((bits & 0x20)) log_printf (" prnGeneration");
|
||||||
|
if ((bits & 0x10)) log_printf (" eidCompliant");
|
||||||
|
if ((bits & 0x08)) other = 1;
|
||||||
|
if ((bits & 0x04)) other = 1;
|
||||||
|
if ((bits & 0x02)) other = 1;
|
||||||
|
if ((bits & 0x01)) other = 1;
|
||||||
|
|
||||||
|
/* Next octet. */
|
||||||
|
if (derlen)
|
||||||
|
other = 1;
|
||||||
|
|
||||||
|
if (other)
|
||||||
|
log_printf (" [unknown]");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -2261,6 +2348,7 @@ read_ef_tokeninfo (app_t app)
|
|||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log_info ("TokenInfo:\n");
|
||||||
/* serialNumber. */
|
/* serialNumber. */
|
||||||
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
|
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
|
||||||
&ndef, &objlen, &hdrlen);
|
&ndef, &objlen, &hdrlen);
|
||||||
@ -2278,7 +2366,54 @@ read_ef_tokeninfo (app_t app)
|
|||||||
}
|
}
|
||||||
memcpy (app->app_local->serialno, p, objlen);
|
memcpy (app->app_local->serialno, p, objlen);
|
||||||
app->app_local->serialnolen = objlen;
|
app->app_local->serialnolen = objlen;
|
||||||
log_printhex (p, objlen, "Serialnumber from EF(TokenInfo) is:");
|
/* *We use a separate log_info to avoid the "DBG:" prefix.) */
|
||||||
|
log_info (" serialNumber .: ");
|
||||||
|
log_printhex (p, objlen, "");
|
||||||
|
p += objlen;
|
||||||
|
n -= objlen;
|
||||||
|
|
||||||
|
/* Is there an optional manufacturerID? */
|
||||||
|
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
|
||||||
|
&ndef, &objlen, &hdrlen);
|
||||||
|
if (!err && (objlen > n || !objlen))
|
||||||
|
err = gpg_error (GPG_ERR_INV_OBJ);
|
||||||
|
if (err)
|
||||||
|
goto leave;
|
||||||
|
if (class == CLASS_UNIVERSAL && tag == TAG_UTF8_STRING)
|
||||||
|
{
|
||||||
|
log_info (" manufacturerID: %.*s\n", (int)objlen, p);
|
||||||
|
p += objlen;
|
||||||
|
n -= objlen;
|
||||||
|
/* Get next TLV. */
|
||||||
|
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
|
||||||
|
&ndef, &objlen, &hdrlen);
|
||||||
|
if (!err && (objlen > n || !objlen))
|
||||||
|
err = gpg_error (GPG_ERR_INV_OBJ);
|
||||||
|
if (err)
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
if (class == CLASS_CONTEXT && tag == 0)
|
||||||
|
{
|
||||||
|
log_info (" label ........: %.*s\n", (int)objlen, p);
|
||||||
|
p += objlen;
|
||||||
|
n -= objlen;
|
||||||
|
/* Get next TLV. */
|
||||||
|
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
|
||||||
|
&ndef, &objlen, &hdrlen);
|
||||||
|
if (!err && (objlen > n || !objlen))
|
||||||
|
err = gpg_error (GPG_ERR_INV_OBJ);
|
||||||
|
if (err)
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
/* The next is the mandatory tokenflags object. */
|
||||||
|
if (class == CLASS_UNIVERSAL && tag == TAG_BIT_STRING)
|
||||||
|
{
|
||||||
|
log_info (" tokenflags ...:");
|
||||||
|
print_tokeninfo_tokenflags (p, objlen);
|
||||||
|
log_printf ("\n");
|
||||||
|
p += objlen;
|
||||||
|
n -= objlen;
|
||||||
|
}
|
||||||
|
|
||||||
leave:
|
leave:
|
||||||
xfree (buffer);
|
xfree (buffer);
|
||||||
@ -3307,11 +3442,11 @@ app_select_p15 (app_t app)
|
|||||||
does only allow for that. Many other cards supports this
|
does only allow for that. Many other cards supports this
|
||||||
selection method too. Note, that we don't use
|
selection method too. Note, that we don't use
|
||||||
select_application above for the Belgian card - the call
|
select_application above for the Belgian card - the call
|
||||||
works but it seems that it did not switch to the correct DF.
|
works but it seems that it does not switch to the correct DF.
|
||||||
Using the 2f02 just works. */
|
Using the 2f02 just works. */
|
||||||
unsigned short path[1] = { 0x2f00 };
|
unsigned short path[1] = { 0x2f00 };
|
||||||
|
|
||||||
rc = iso7816_select_path (app_get_slot (app), path, 1);
|
rc = iso7816_select_path (slot, path, 1);
|
||||||
if (!rc)
|
if (!rc)
|
||||||
{
|
{
|
||||||
direct = 1;
|
direct = 1;
|
||||||
@ -3319,7 +3454,7 @@ app_select_p15 (app_t app)
|
|||||||
if (def_home_df)
|
if (def_home_df)
|
||||||
{
|
{
|
||||||
path[0] = def_home_df;
|
path[0] = def_home_df;
|
||||||
rc = iso7816_select_path (app_get_slot (app), path, 1);
|
rc = iso7816_select_path (slot, path, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user