diff options
author | Werner Koch <wk@gnupg.org> | 2005-09-05 16:36:36 +0200 |
---|---|---|
committer | Werner Koch <wk@gnupg.org> | 2005-09-05 16:36:36 +0200 |
commit | 1b2f7cbe3ba731c5d94990b3f019b8c0bf641372 (patch) | |
tree | 5e09a115940b2dcde6a1f340619551f7bf9c9938 /scd/app-p15.c | |
parent | Use a default argument for --write-env-file. (diff) | |
download | gnupg2-1b2f7cbe3ba731c5d94990b3f019b8c0bf641372.tar.xz gnupg2-1b2f7cbe3ba731c5d94990b3f019b8c0bf641372.zip |
Basically made Belgian EID cards work.
Signature creation has not yet been tested.
Also other changes to better cope with T=0 cards.
Diffstat (limited to 'scd/app-p15.c')
-rw-r--r-- | scd/app-p15.c | 249 |
1 files changed, 218 insertions, 31 deletions
diff --git a/scd/app-p15.c b/scd/app-p15.c index f03e5d5f0..c8d38850b 100644 --- a/scd/app-p15.c +++ b/scd/app-p15.c @@ -74,7 +74,17 @@ static struct #undef X -/* The Pin Types as defined in pkcs#15 v1.1 */ +/* The AID of PKCS15. */ +static char const pkcs15_aid[] = { 0xA0, 0, 0, 0, 0x63, + 0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35 }; + +/* The Belgian eID variant - they didn't understood why a shared AID + is useful for a standard. Oh well. */ +static char const pkcs15be_aid[] = { 0xA0, 0, 0, 0x01, 0x77, + 0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35 }; + + +/* The PIN types as defined in pkcs#15 v1.1 */ typedef enum { PIN_TYPE_BCD = 0, @@ -261,6 +271,9 @@ struct app_local_s /* The type of the card. */ card_type_t card_type; + /* Flag indicating whether we may use direct path selection. */ + int direct_path_selection; + /* Structure with the EFIDs of the objects described in the ODF file. */ struct @@ -276,6 +289,10 @@ struct app_local_s unsigned short auth_objects; } odf; + /* The PKCS#15 serialnumber from EF(TokeiNFo) or NULL. Malloced. */ + unsigned char *serialno; + size_t serialnolen; + /* Information on all certificates. */ cdf_object_t certificate_info; /* Information on all trusted certificates. */ @@ -363,6 +380,7 @@ do_deinit (app_t app) release_cdflist (app->app_local->useful_certificate_info); release_prkdflist (app->app_local->private_key_info); release_aodflist (app->app_local->auth_object_info); + xfree (app->app_local->serialno); xfree (app->app_local); app->app_local = NULL; } @@ -406,26 +424,44 @@ select_ef_by_path (app_t app, const unsigned short *path, size_t pathlen) gpg_error_t err; int i, j; - /* 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) + return gpg_error (GPG_ERR_INV_VALUE); + if (pathlen && *path != 0x3f00 ) log_debug ("WARNING: relative path selection not yet implemented\n"); - - for (i=0; i < pathlen; i++) + + if (app->app_local->direct_path_selection) { - err = iso7816_select_file (app->slot, path[i], - !(i+1 == pathlen), NULL, NULL); + err = iso7816_select_path (app->slot, path+1, pathlen-1, NULL, NULL); if (err) { - log_error ("error selecting part %d from path ", i); + log_error ("error selecting path "); for (j=0; j < pathlen; j++) log_printf ("%04hX", path[j]); log_printf (": %s\n", gpg_strerror (err)); return err; } } + 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. */ + for (i=0; i < pathlen; i++) + { + err = iso7816_select_file (app->slot, path[i], + !(i+1 == pathlen), NULL, NULL); + if (err) + { + log_error ("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; + } + } + } return 0; } @@ -586,12 +622,13 @@ 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\x50\x15", 9) - && app->app_local->home_df == 0x5015 ) + && !memcmp (p+1, "\x0a\x30\x08\x04\x06\x3F\x00", 7) + && app->app_local->home_df == ((p[8]<<8)|p[9]) ) { - /* This format using a full path is used by a self-created - test card of mine. I have not checked whether this is - legal. We assume a home DF of 0x5015 here. */ + /* We only allow a full path if all files are at the same + level and below the home directory. The extend this we + would need to make use of new data type capable of + keeping a full path. */ offset = 10; } else @@ -2158,12 +2195,81 @@ TokenFlags ::= BIT STRING { */ -/* static gpg_error_t */ -/* read_ef_tokeninfo (app_t app) */ -/* { */ -/* unsigned short efid = 0x5032; */ -/* return 0; */ -/* } */ +static gpg_error_t +read_ef_tokeninfo (app_t app) +{ + gpg_error_t err; + unsigned char *buffer = NULL; + size_t buflen; + const unsigned char *p; + size_t n, objlen, hdrlen; + int class, tag, constructed, ndef; + unsigned long ul; + + err = select_and_read_binary (app->slot, 0x5032, "TokenInfo", + &buffer, &buflen); + if (err) + return err; + + p = buffer; + n = buflen; + + err = parse_ber_header (&p, &n, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > n || tag != TAG_SEQUENCE)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + { + log_error ("error parsing TokenInfo: %s\n", gpg_strerror (err)); + goto leave; + } + + n = objlen; + + /* Version. */ + err = parse_ber_header (&p, &n, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > n || tag != TAG_INTEGER)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto leave; + + for (ul=0; objlen; objlen--) + { + ul <<= 8; + ul |= (*p++) & 0xff; + n--; + } + if (ul) + { + log_error ("invalid version %lu in TokenInfo\n", ul); + err = gpg_error (GPG_ERR_INV_OBJ); + goto leave; + } + + /* serialNumber. */ + err = parse_ber_header (&p, &n, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > n || tag != TAG_OCTET_STRING || !objlen)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto leave; + + xfree (app->app_local->serialno); + app->app_local->serialno = xtrymalloc (objlen); + if (!app->app_local->serialno) + { + err = gpg_error_from_errno (errno); + goto leave; + } + memcpy (app->app_local->serialno, p, objlen); + app->app_local->serialnolen = objlen; + log_printhex ("Serialnumber from EF(TokenInfo) is:", p, objlen); + + leave: + xfree (buffer); + return err; +} /* Get all the basic information from the pkcs#15 card, check the @@ -2174,11 +2280,25 @@ read_p15_info (app_t app) { gpg_error_t err; - /* Fixme: We might need to read the tokeninfo to get a non-standard - ODF FID. */ - + if (!read_ef_tokeninfo (app)) + { + /* If we don't have a serial number yet but the TokenInfo provides + one, use that. */ + if (!app->serialno && app->app_local->serialno) + { + app->serialno = app->app_local->serialno; + app->serialnolen = app->app_local->serialnolen; + app->app_local->serialno = NULL; + app->app_local->serialnolen = 0; + err = app_munge_serialno (app); + if (err) + return err; + } + } + /* Read the ODF so that we know the location of all directory files. */ + /* Fixme: We might need to get a non-standard ODF FID from TokenInfo. */ err = read_ef_odf (app, 0x5031); if (err) return err; @@ -2895,22 +3015,88 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, } + +/* Assume that EF(DIR) has been selected. Read its content and figure + out the home EF of pkcs#15. Return that home DF or 0 if not + found. */ +static unsigned short +read_home_df (int slot) +{ + gpg_error_t err; + unsigned char *buffer; + const unsigned char *p, *pp; + size_t buflen, n, nn; + unsigned short result = 0; + + err = iso7816_read_binary (slot, 0, 0, &buffer, &buflen); + if (err) + { + log_error ("error reading EF{DIR}: %s\n", gpg_strerror (err)); + return 0; + } + + /* FIXME: We need to scan all records. */ + p = find_tlv (buffer, buflen, 0x61, &n); + if (p && n) + { + pp = find_tlv (p, n, 0x4f, &nn); + if (pp + && ((nn == sizeof pkcs15_aid && !memcmp (pp, pkcs15_aid, nn)) + ||(nn == sizeof pkcs15be_aid && !memcmp (pp, pkcs15be_aid, nn)))) + { + pp = find_tlv (p, n, 0x50, &nn); + if (pp) /* fixme: Filter log value? */ + log_info ("pkcs#15 application label from EF(DIR) is `%.*s'\n", + (int)nn, pp); + pp = find_tlv (p, n, 0x51, &nn); + if (pp && nn == 4 && *pp == 0x3f && !pp[1]) + { + result = ((pp[2] << 8) | pp[3]); + log_info ("pkcs#15 application directory is 0x%04hX\n", result); + } + } + } + xfree (buffer); + return result; +} -/* Select the PKCS#15 application on the card in SLOT. */ +/* + Select the PKCS#15 application on the card in SLOT. + */ gpg_error_t app_select_p15 (app_t app) { - static char const aid[] = { 0xA0, 0, 0, 0, 0x63, - 0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35 }; int slot = app->slot; int rc; unsigned short def_home_df = 0; card_type_t card_type = CARD_TYPE_UNKNOWN; + int direct = 0; - rc = iso7816_select_application (slot, aid, sizeof aid); + rc = iso7816_select_application (slot, pkcs15_aid, sizeof pkcs15_aid); if (rc) - { + rc = iso7816_select_application (slot, pkcs15be_aid, sizeof pkcs15be_aid); + if (rc) + { /* Not found: Try to locate it from 2F00. We use direct path + selection here because it seems that the Belgian eID card + does only allow for that. Many other cards supports this + selection method too. */ + unsigned short path[1] = { 0x2f00 }; + + rc = iso7816_select_path (app->slot, path, 1, NULL, NULL); + if (!rc) + { + direct = 1; + def_home_df = read_home_df (slot); + if (def_home_df) + { + path[0] = def_home_df; + rc = iso7816_select_path (app->slot, path, 1, NULL, NULL); + } + } + } + if (rc) + { /* Still not found: Try the default DF. */ def_home_df = 0x5015; rc = iso7816_select_file (slot, def_home_df, 1, NULL, NULL); } @@ -2958,6 +3144,9 @@ app_select_p15 (app_t app) the common APP structure. */ app->app_local->card_type = card_type; + /* Store whether we may and should use direct path selection. */ + app->app_local->direct_path_selection = direct; + /* Read basic information and thus check whether this is a real card. */ rc = read_p15_info (app); @@ -2989,8 +3178,6 @@ app_select_p15 (app_t app) app->serialno = p; } } - else /* Use standard munging code. */ - rc = app_munge_serialno (app); app->fnc.deinit = do_deinit; app->fnc.learn_status = do_learn_status; |