aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2021-01-26 17:42:55 +0100
committerWerner Koch <wk@gnupg.org>2021-01-26 17:42:55 +0100
commitfc287c0552b0fe489c66bb493879f4330c34f287 (patch)
tree70353302f713b1e1b9d3924e79c9b543865c6c04
parent224e26cf7b67f22bb0140133eac6b4ad24f3b1b7 (diff)
downloadgnupg-fc287c0552b0fe489c66bb493879f4330c34f287.tar.gz
scd:p15: First step towards real CardOS 5 support.
* scd/iso7816.c (iso7816_select_path): Add arg from_cdf. * scd/app-nks.c (do_readkey): Adjust for this change. * scd/app-p15.c (CARD_TYPE_CARDOS_53): New. (IS_CARDOS_5): New. (card_atr_list): Add standard ATR for CardOS 5.3. (select_and_read_binary): Remove the fallback to record read hack. (select_and_read_record): New. (select_ef_by_path): Rework and support CardOS feature. (read_ef_prkdf): Use read record for CardOS. (read_ef_cdf): Ditto. (read_ef_aodf): Ditto. Also fix bug in the detection of other unsupported attribute types. (verify_pin): Use IS_CARDOS_5 macro. (app_select_p15): Force direct method for CardOS. Signed-off-by: Werner Koch <wk@gnupg.org>
-rw-r--r--scd/app-nks.c2
-rw-r--r--scd/app-p15.c280
-rw-r--r--scd/iso7816.c9
-rw-r--r--scd/iso7816.h3
4 files changed, 218 insertions, 76 deletions
diff --git a/scd/app-nks.c b/scd/app-nks.c
index bf73fa7c7..006e6a92c 100644
--- a/scd/app-nks.c
+++ b/scd/app-nks.c
@@ -1231,7 +1231,7 @@ do_readkey (app_t app, ctrl_t ctrl, const char *keyid, unsigned int flags,
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
/* Access the KEYD file which is always in the master directory. */
- err = iso7816_select_path (app_get_slot (app), path, DIM (path));
+ err = iso7816_select_path (app_get_slot (app), path, DIM (path), 0);
if (err)
return err;
/* Due to the above select we need to re-select our application. */
diff --git a/scd/app-p15.c b/scd/app-p15.c
index ee7c9436d..44ef9fa99 100644
--- a/scd/app-p15.c
+++ b/scd/app-p15.c
@@ -52,6 +52,7 @@ typedef enum
CARD_TYPE_TCOS,
CARD_TYPE_MICARDO,
CARD_TYPE_CARDOS_50,
+ CARD_TYPE_CARDOS_53,
CARD_TYPE_BELPIC /* Belgian eID card specs. */
}
card_type_t;
@@ -95,11 +96,18 @@ static struct
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 */
+ { 11, X("\x3b\xd2\x18\x00\x81\x31\xfe\x58\xc9\x03\x16"),
+ CARD_TYPE_CARDOS_53 }, /* CardOS 5.3 */
{ 0 }
};
#undef X
+/* Macro to test for CardOS 5.0 and 5.3. */
+#define IS_CARDOS_5(a) ((a)->app_local->card_type == CARD_TYPE_CARDOS_50 \
+ || (a)->app_local->card_type == CARD_TYPE_CARDOS_53)
+
+
/* The AID of PKCS15. */
static char const pkcs15_aid[] = { 0xA0, 0, 0, 0, 0x63,
0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35 };
@@ -477,33 +485,56 @@ select_and_read_binary (int slot, unsigned short efid, const char *efid_desc,
}
err = iso7816_read_binary_ext (slot, 0, 0, 0, buffer, buflen, &sw);
- if (err && sw == 0x6981)
+ if (err)
+ log_error ("p15: error reading %s (0x%04X): %s (sw=%04X)\n",
+ efid_desc, efid, gpg_strerror (err), sw);
+ return err;
+}
+
+
+/* If EFID is not 0 do a select and then read the record RECNO.
+ * EFID_DESC is a description of the EF to be used with error
+ * messages. On success BUFFER and BUFLEN contain the entire content
+ * of the EF. The caller must free BUFFER only on success. */
+static gpg_error_t
+select_and_read_record (int slot, unsigned short efid, int recno,
+ const char *efid_desc,
+ unsigned char **buffer, size_t *buflen)
+{
+ gpg_error_t err;
+ int sw;
+
+ if (efid)
{
- /* Command was not possible for file structure. Try to read the
- * first record instead. */
- err = iso7816_read_record_ext (slot, 1, 1, 0, buffer, buflen, &sw);
+ err = iso7816_select_file (slot, efid, 0);
if (err)
{
- log_error ("p15: error reading %s (0x%04X)"
- " after fallback to record mode: %s (sw=%04X)\n",
- efid_desc, efid, gpg_strerror (err), sw);
+ log_error ("p15: error selecting %s (0x%04X): %s\n",
+ efid_desc, efid, gpg_strerror (err));
return err;
}
- /* On a CardOS based card I noticed that the record started with
- * a byte 0x01 followed by another byte with the length of the
- * record. Detect this here and remove this prefix. */
- if (*buflen > 2 && (*buffer)[0] == 1 && (*buffer)[1] == *buflen - 2)
- {
- memmove (*buffer, *buffer + 2, *buflen - 2);
- *buflen = *buflen - 2;
- }
}
- else if (err)
+
+ err = iso7816_read_record_ext (slot, recno, 1, 0, buffer, buflen, &sw);
+ if (err)
{
- log_error ("p15: error reading %s (0x%04X): %s (sw=%04X)\n",
- efid_desc, efid, gpg_strerror (err), sw);
+ if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+ log_info ("p15: reading %s (0x%04X) record %d: %s\n",
+ efid_desc, efid, recno, gpg_strerror (err));
+ else
+ log_error ("p15: error reading %s (0x%04X) record %d: %s (sw=%04X)\n",
+ efid_desc, efid, recno, gpg_strerror (err), sw);
return err;
}
+ /* On CardOS with a Linear TLV file structure the records starts
+ * with some tag (often the record number) followed by the length
+ * byte for this record. Detect and remove this prefix. */
+ if (*buflen > 2 && (*buffer)[0] != 0x30 && (*buffer)[1] == *buflen - 2)
+ {
+ memmove (*buffer, *buffer + 2, *buflen - 2);
+ *buflen = *buflen - 2;
+ }
+
return 0;
}
@@ -519,27 +550,28 @@ select_ef_by_path (app_t app, const unsigned short *path, size_t pathlen)
if (!pathlen)
return gpg_error (GPG_ERR_INV_VALUE);
- if (pathlen && *path != 0x3f00 )
- log_error ("p15: warning: relative path selection not yet implemented\n");
-
if (app->app_local->direct_path_selection)
{
- err = iso7816_select_path (app_get_slot (app), path+1, pathlen-1);
+ if (pathlen && *path == 0x3f00 )
+ {
+ path++;
+ pathlen--;
+ }
+
+ /* CardOS 5 supports P0=0x09 to select stating at the CDF. */
+ err = iso7816_select_path (app_get_slot (app), path, pathlen,
+ IS_CARDOS_5(app));
if (err)
{
log_error ("p15: error selecting path ");
- for (j=0; j < pathlen; j++)
- log_printf ("%04hX", path[j]);
- log_printf (": %s\n", gpg_strerror (err));
- return err;
+ goto err_print_path;
}
}
else
{
- /* FIXME: Need code to remember the last PATH so that we can decide
- what select commands to send in case the path does not start off
- with 3F00. We might also want to use direct path selection if
- supported by the card. */
+ if (pathlen && *path != 0x3f00 )
+ log_error ("p15: warning: relative path select not yet implemented\n");
+
for (i=0; i < pathlen; i++)
{
err = iso7816_select_file (app_get_slot (app),
@@ -547,16 +579,20 @@ select_ef_by_path (app_t app, const unsigned short *path, size_t pathlen)
if (err)
{
log_error ("p15: error selecting part %d from path ", i);
- for (j=0; j < pathlen; j++)
- log_printf ("%04hX", path[j]);
- log_printf (": %s\n", gpg_strerror (err));
- return err;
+ goto err_print_path;
}
}
}
return 0;
+
+ err_print_path:
+ for (j=0; j < pathlen; j++)
+ log_printf ("%s%04hX", j? "/":"", path[j]);
+ log_printf (": %s\n", gpg_strerror (err));
+ return err;
}
+
/* Parse a cert Id string (or a key Id string) and return the binary
object Id string in a newly allocated buffer stored at R_OBJID and
R_OBJIDLEN. On Error NULL will be stored there and an error code
@@ -956,12 +992,17 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
int class, tag, constructed, ndef;
prkdf_object_t prkdflist = NULL;
int i;
+ int recno = 1;
if (!fid)
return gpg_error (GPG_ERR_NO_DATA); /* No private keys. */
- err = select_and_read_binary (app_get_slot (app), fid, "PrKDF",
- &buffer, &buflen);
+ if (IS_CARDOS_5 (app))
+ err = select_and_read_record (app_get_slot (app), fid, recno, "PrKDF",
+ &buffer, &buflen);
+ else
+ err = select_and_read_binary (app_get_slot (app), fid, "PrKDF",
+ &buffer, &buflen);
if (err)
return err;
@@ -972,7 +1013,8 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
/* Loop over the records. We stop as soon as we detect a new record
starting with 0x00 or 0xff as these values are commonly used to
- pad data blocks and are no valid ASN.1 encoding. */
+ pad data blocks and are no valid ASN.1 encoding. Note the
+ special handling for record mode at the end of the loop. */
while (n && *p && *p != 0xff)
{
const unsigned char *pp;
@@ -992,8 +1034,17 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
- if (!err && (objlen > n || tag != TAG_SEQUENCE))
+ if (err)
+ ;
+ else if (objlen > n)
err = gpg_error (GPG_ERR_INV_OBJ);
+ else if (tag == TAG_SEQUENCE)
+ ;
+ else if (class == CLASS_CONTEXT && tag == 0)
+ ; /* Seen with CardOS. */
+ else
+ err = gpg_error (GPG_ERR_INV_OBJ);
+
if (err)
{
log_error ("p15: error parsing PrKDF record: %s\n",
@@ -1378,7 +1429,7 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
prkdf->next = prkdflist;
prkdflist = prkdf;
prkdf = NULL;
- continue; /* Ready. */
+ goto next_record; /* Ready with this record. */
parse_error:
log_error ("p15: error parsing PrKDF record (%d): %s - skipped\n",
@@ -1390,6 +1441,24 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
xfree (prkdf);
}
err = 0;
+
+ next_record:
+ /* If the card uses a record oriented file structure, read the
+ * next record. Otherwise we keep on parsing the current buffer. */
+ recno++;
+ if (IS_CARDOS_5 (app))
+ {
+ xfree (buffer); buffer = NULL;
+ err = select_and_read_record (app_get_slot (app), 0, recno, "PrKDF",
+ &buffer, &buflen);
+ if (err) {
+ if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+ err = 0;
+ goto leave;
+ }
+ p = buffer;
+ n = buflen;
+ }
} /* End looping over all records. */
leave:
@@ -1417,12 +1486,17 @@ read_ef_cdf (app_t app, unsigned short fid, cdf_object_t *result)
int class, tag, constructed, ndef;
cdf_object_t cdflist = NULL;
int i;
+ int recno = 1;
if (!fid)
return gpg_error (GPG_ERR_NO_DATA); /* No certificates. */
- err = select_and_read_binary (app_get_slot (app), fid, "CDF",
- &buffer, &buflen);
+ if (IS_CARDOS_5 (app))
+ err = select_and_read_record (app_get_slot (app), fid, recno, "CDF",
+ &buffer, &buflen);
+ else
+ err = select_and_read_binary (app_get_slot (app), fid, "CDF",
+ &buffer, &buflen);
if (err)
return err;
@@ -1431,7 +1505,8 @@ read_ef_cdf (app_t app, unsigned short fid, cdf_object_t *result)
/* Loop over the records. We stop as soon as we detect a new record
starting with 0x00 or 0xff as these values are commonly used to
- pad data blocks and are no valid ASN.1 encoding. */
+ pad data blocks and are no valid ASN.1 encoding. Note the
+ special handling for record mode at the end of the loop. */
while (n && *p && *p != 0xff)
{
const unsigned char *pp;
@@ -1627,14 +1702,32 @@ read_ef_cdf (app_t app, unsigned short fid, cdf_object_t *result)
cdf->next = cdflist;
cdflist = cdf;
cdf = NULL;
- continue; /* Ready. */
+ goto next_record; /* Ready with this record. */
parse_error:
log_error ("p15: error parsing CDF record (%d): %s - skipped\n",
where, errstr? errstr : gpg_strerror (err));
xfree (cdf);
err = 0;
- } /* End looping over all records. */
+
+ next_record:
+ /* If the card uses a record oriented file structure, read the
+ * next record. Otherwise we keep on parsing the current buffer. */
+ recno++;
+ if (IS_CARDOS_5 (app))
+ {
+ xfree (buffer); buffer = NULL;
+ err = select_and_read_record (app_get_slot (app), 0, recno, "CDF",
+ &buffer, &buflen);
+ if (err) {
+ if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+ err = 0;
+ goto leave;
+ }
+ p = buffer;
+ n = buflen;
+ }
+ } /* End loop over all records. */
leave:
xfree (buffer);
@@ -1694,12 +1787,17 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
int class, tag, constructed, ndef;
aodf_object_t aodflist = NULL;
int i;
+ int recno = 1;
if (!fid)
return gpg_error (GPG_ERR_NO_DATA); /* No authentication objects. */
- err = select_and_read_binary (app_get_slot (app), fid, "AODF",
- &buffer, &buflen);
+ if (IS_CARDOS_5 (app))
+ err = select_and_read_record (app_get_slot (app), fid, recno, "AODF",
+ &buffer, &buflen);
+ else
+ err = select_and_read_binary (app_get_slot (app), fid, "AODF",
+ &buffer, &buflen);
if (err)
return err;
@@ -1710,7 +1808,8 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
/* Loop over the records. We stop as soon as we detect a new record
starting with 0x00 or 0xff as these values are commonly used to
- pad data blocks and are no valid ASN.1 encoding. */
+ pad data blocks and are no valid ASN.1 encoding. Note the
+ special handling for record mode at the end of the loop. */
while (n && *p && *p != 0xff)
{
const unsigned char *pp;
@@ -1721,10 +1820,33 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
unsigned long ul;
const char *s;
+ where = __LINE__;
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
- if (!err && (objlen > n || tag != TAG_SEQUENCE))
+ if (err)
+ ;
+ else if (objlen > n)
err = gpg_error (GPG_ERR_INV_OBJ);
+ else if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE)
+ ; /* PinAttributes */
+ else if (class == CLASS_CONTEXT)
+ {
+ switch (tag)
+ {
+ case 0: errstr = "biometric auth types are not supported"; break;
+ case 1: errstr = "authKey auth types are not supported"; break;
+ case 2: errstr = "external auth type are not supported"; break;
+ default: errstr = "unknown privateKeyObject"; break;
+ }
+ goto parse_error;
+ }
+ else
+ {
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ goto parse_error;
+ }
+
+
if (err)
{
log_error ("p15: error parsing AODF record: %s\n",
@@ -1856,28 +1978,16 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
- if (!err && objlen > nn)
+ if (err)
+ ;
+ else if (!err && objlen > nn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ else if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE)
+ ; /* A typeAttribute always starts with a sequence */
+ else
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
- if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE)
- ; /* PinAttributes */
- else if (class == CLASS_CONTEXT)
- {
- switch (tag)
- {
- case 0: errstr = "biometric auth types are not supported"; break;
- case 1: errstr = "authKey auth types are not supported"; break;
- case 2: errstr = "external auth type are not supported"; break;
- default: errstr = "unknown privateKeyObject"; break;
- }
- goto parse_error;
- }
- else
- {
- err = gpg_error (GPG_ERR_INV_OBJ);
- goto parse_error;
- }
nn = objlen;
@@ -2281,7 +2391,7 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
aodf->next = aodflist;
aodflist = aodf;
aodf = NULL;
- continue; /* Ready. */
+ goto next_record; /* Ready with this record. */
no_core:
err = gpg_error_from_syserror ();
@@ -2293,6 +2403,24 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
where, errstr? errstr : gpg_strerror (err));
err = 0;
release_aodf_object (aodf);
+
+ next_record:
+ /* If the card uses a record oriented file structure, read the
+ * next record. Otherwise we keep on parsing the current buffer. */
+ recno++;
+ if (IS_CARDOS_5 (app))
+ {
+ xfree (buffer); buffer = NULL;
+ err = select_and_read_record (app_get_slot (app), 0, recno, "AODF",
+ &buffer, &buflen);
+ if (err) {
+ if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+ err = 0;
+ goto leave;
+ }
+ p = buffer;
+ n = buflen;
+ }
} /* End looping over all records. */
leave:
@@ -3486,7 +3614,7 @@ verify_pin (app_t app,
pin_reference = aodf->pin_reference_valid? aodf->pin_reference : 0;
- if (app->app_local->card_type == CARD_TYPE_CARDOS_50)
+ if (IS_CARDOS_5 (app))
{
/* We know that this card supports a verify status check. Note
* that in contrast to PIV cards ISO7816_VERIFY_NOT_NEEDED is
@@ -4265,7 +4393,7 @@ app_select_p15 (app_t app)
Using the 2f02 just works. */
unsigned short path[1] = { 0x2f00 };
- rc = iso7816_select_path (slot, path, 1);
+ rc = iso7816_select_path (slot, path, 1, 0);
if (!rc)
{
direct = 1;
@@ -4273,7 +4401,7 @@ app_select_p15 (app_t app)
if (def_home_df)
{
path[0] = def_home_df;
- rc = iso7816_select_path (slot, path, 1);
+ rc = iso7816_select_path (slot, path, 1, 0);
}
}
}
@@ -4338,6 +4466,16 @@ app_select_p15 (app_t app)
app->app_local->card_product = CARD_PRODUCT_UNKNOWN;
/* Store whether we may and should use direct path selection. */
+ switch (card_type)
+ {
+ case CARD_TYPE_CARDOS_50:
+ case CARD_TYPE_CARDOS_53:
+ direct = 1;
+ break;
+ default:
+ /* Use whatever has been determined above. */
+ break;
+ }
app->app_local->direct_path_selection = direct;
/* Read basic information and thus check whether this is a real
diff --git a/scd/iso7816.c b/scd/iso7816.c
index f14c14f69..c68aab075 100644
--- a/scd/iso7816.c
+++ b/scd/iso7816.c
@@ -185,9 +185,12 @@ iso7816_select_file (int slot, int tag, int is_dir)
}
-/* Do a select file command with a direct path. */
+/* Do a select file command with a direct path. If FROM_CDF is set
+ * the starting point is the current direcory file (feature depends on
+ * the card). */
gpg_error_t
-iso7816_select_path (int slot, const unsigned short *path, size_t pathlen)
+iso7816_select_path (int slot, const unsigned short *path, size_t pathlen,
+ int from_cdf)
{
int sw, p0, p1;
unsigned char buffer[100];
@@ -202,7 +205,7 @@ iso7816_select_path (int slot, const unsigned short *path, size_t pathlen)
buffer[buflen++] = *path;
}
- p0 = 0x08;
+ p0 = from_cdf? 0x09 : 0x08;
p1 = 0x0c; /* No FC return. */
sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE,
p0, p1, buflen, (char*)buffer );
diff --git a/scd/iso7816.h b/scd/iso7816.h
index d68ee57f2..06ee5eaa1 100644
--- a/scd/iso7816.h
+++ b/scd/iso7816.h
@@ -68,7 +68,8 @@ gpg_error_t iso7816_select_application_ext (int slot,
gpg_error_t iso7816_select_mf (int slot);
gpg_error_t iso7816_select_file (int slot, int tag, int is_dir);
gpg_error_t iso7816_select_path (int slot,
- const unsigned short *path, size_t pathlen);
+ const unsigned short *path, size_t pathlen,
+ int from_cdf);
gpg_error_t iso7816_list_directory (int slot, int list_dirs,
unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_send_apdu (int slot, int extended_mode,