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.
|
||||
* Copyright (C) 2005 Free Software Foundation, Inc.
|
||||
* Copyright (C) 2020 g10 Code GmbH
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
@ -15,6 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* 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:
|
||||
@ -48,6 +50,7 @@ typedef enum
|
||||
CARD_TYPE_UNKNOWN,
|
||||
CARD_TYPE_TCOS,
|
||||
CARD_TYPE_MICARDO,
|
||||
CARD_TYPE_CARDOS_50,
|
||||
CARD_TYPE_BELPIC /* Belgian eID card specs. */
|
||||
}
|
||||
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"
|
||||
"\x44\x20\x76\x65\x72\x20\x31\x2E\x30\x43"),
|
||||
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 }
|
||||
};
|
||||
#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
|
||||
A4 06 30 04 04 02 60 35 = Certificates
|
||||
A5 06 30 04 04 02 60 36 = TrustedCertificates
|
||||
A7 06 30 04 04 02 60 37 = DataObjects
|
||||
A8 06 30 04 04 02 60 38 = AuthObjects
|
||||
A5 06 30 04 04 02 60 36 = Trusted Certificates
|
||||
A7 06 30 04 04 02 60 37 = Data Objects
|
||||
A8 06 30 04 04 02 60 38 = Auth Objects
|
||||
|
||||
These are all PathOrObjects using the path CHOICE element. The
|
||||
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;
|
||||
unsigned char *buffer, *p;
|
||||
size_t buflen;
|
||||
size_t buflen, n;
|
||||
unsigned short value;
|
||||
size_t offset;
|
||||
unsigned short home_df = 0;
|
||||
|
||||
err = select_and_read_binary (app_get_slot (app), odf_fid, "ODF",
|
||||
&buffer, &buflen);
|
||||
@ -623,6 +628,8 @@ read_ef_odf (app_t app, unsigned short odf_fid)
|
||||
xfree (buffer);
|
||||
return gpg_error (GPG_ERR_INV_OBJ);
|
||||
}
|
||||
|
||||
home_df = app->app_local->home_df;
|
||||
p = buffer;
|
||||
while (buflen && *p && *p != 0xff)
|
||||
{
|
||||
@ -635,17 +642,33 @@ read_ef_odf (app_t app, unsigned short odf_fid)
|
||||
else if ( buflen >= 12
|
||||
&& (p[0] & 0xf0) == 0xA0
|
||||
&& !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
|
||||
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
|
||||
keeping a full path. */
|
||||
offset = 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
log_error ("ODF format is not supported by us\n");
|
||||
log_printhex (p, buflen, "ODF format is not supported by us:");
|
||||
xfree (buffer);
|
||||
return gpg_error (GPG_ERR_INV_OBJ);
|
||||
}
|
||||
@ -691,8 +714,16 @@ read_ef_odf (app_t app, unsigned short odf_fid)
|
||||
}
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
log_info ("TokenInfo:\n");
|
||||
/* serialNumber. */
|
||||
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
|
||||
&ndef, &objlen, &hdrlen);
|
||||
@ -2278,7 +2366,54 @@ read_ef_tokeninfo (app_t app)
|
||||
}
|
||||
memcpy (app->app_local->serialno, p, 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:
|
||||
xfree (buffer);
|
||||
@ -3307,11 +3442,11 @@ app_select_p15 (app_t app)
|
||||
does only allow for that. Many other cards supports this
|
||||
selection method too. Note, that we don't use
|
||||
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. */
|
||||
unsigned short path[1] = { 0x2f00 };
|
||||
|
||||
rc = iso7816_select_path (app_get_slot (app), path, 1);
|
||||
rc = iso7816_select_path (slot, path, 1);
|
||||
if (!rc)
|
||||
{
|
||||
direct = 1;
|
||||
@ -3319,7 +3454,7 @@ app_select_p15 (app_t app)
|
||||
if (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