diff --git a/src/libopensc/card-openpgp.c b/src/libopensc/card-openpgp.c index e819cf62..68367232 100644 --- a/src/libopensc/card-openpgp.c +++ b/src/libopensc/card-openpgp.c @@ -45,6 +45,7 @@ static struct sc_atr_table pgp_atrs[] = { { "3b:fa:13:00:ff:81:31:80:45:00:31:c1:73:c0:01:00:00:90:00:b1", NULL, "OpenPGP card v1.0/1.1", SC_CARD_TYPE_OPENPGP_V1, 0, NULL }, { "3b:da:18:ff:81:b1:fe:75:1f:03:00:31:c5:73:c0:01:40:00:90:00:0c", NULL, "CryptoStick v1.2 (OpenPGP v2.0)", SC_CARD_TYPE_OPENPGP_V2, 0, NULL }, + { "3b:da:11:ff:81:b1:fe:55:1f:03:00:31:84:73:80:01:80:00:90:00:e4", NULL, "Gnuk v1.0.x (OpenPGP v2.0)", SC_CARD_TYPE_OPENPGP_GNUK, 0, NULL }, { NULL, NULL, NULL, 0, 0, NULL } }; @@ -112,9 +113,9 @@ enum _card_state { CARD_STATE_ACTIVATED = 0x05 }; -struct blob { - struct blob * next; /* pointer to next sibling */ - struct blob * parent; /* pointer to parent */ +typedef struct pgp_blob { + struct pgp_blob * next; /* pointer to next sibling */ + struct pgp_blob * parent; /* pointer to parent */ struct do_info *info; sc_file_t * file; @@ -123,8 +124,8 @@ struct blob { unsigned char * data; unsigned int len; - struct blob * files; /* pointer to 1st child */ -}; + struct pgp_blob * files; /* pointer to 1st child */ +} pgp_blob_t; struct do_info { unsigned int id; /* ID of the DO in question */ @@ -142,17 +143,47 @@ struct do_info { static int pgp_get_card_features(sc_card_t *card); static int pgp_finish(sc_card_t *card); -static void pgp_iterate_blobs(struct blob *, int, void (*func)()); +static void pgp_iterate_blobs(pgp_blob_t *, int, void (*func)()); -static int pgp_get_blob(sc_card_t *card, struct blob *blob, - unsigned int id, struct blob **ret); -static struct blob * pgp_new_blob(sc_card_t *, struct blob *, unsigned int, sc_file_t *); -static void pgp_free_blob(struct blob *); +static int pgp_get_blob(sc_card_t *card, pgp_blob_t *blob, + unsigned int id, pgp_blob_t **ret); +static pgp_blob_t * pgp_new_blob(sc_card_t *, pgp_blob_t *, unsigned int, sc_file_t *); +static void pgp_free_blob(pgp_blob_t *); static int pgp_get_pubkey(sc_card_t *, unsigned int, u8 *, size_t); static int pgp_get_pubkey_pem(sc_card_t *, unsigned int, u8 *, size_t); +/* The DO holding X.509 certificate is constructed but does not contain child DO. + * We should notice this when building fake file system later. */ +#define DO_CERT 0x7f21 +/* Control Reference Template of private keys. Ref: Section 4.3.3.7 of OpenPGP card v2 spec. + * Here we seen it as DO just for convenient */ +#define DO_SIGN 0xb600 +#define DO_ENCR 0xb800 +#define DO_AUTH 0xa400 +/* These DO does not exist. They are defined and used just for ease of implementation */ +#define DO_SIGN_SYM 0xb601 +#define DO_ENCR_SYM 0xb801 +#define DO_AUTH_SYM 0xa401 +/* Private DO's */ +#define DO_PRIV1 0x0101 +#define DO_PRIV2 0x0102 +#define DO_PRIV3 0x0103 +#define DO_PRIV4 0x0104 +/* Cardholder information DO's */ +#define DO_CARDHOLDER 0x65 +#define DO_NAME 0x5b +#define DO_LANG_PREF 0x5f2d +#define DO_SEX 0x5f35 + + +/* Maximum length for response buffer when reading pubkey. This value is calculated with + * 4096-bit key length */ +#define MAXLEN_RESP_PUBKEY 527 +/* Gnuk only support 1 key length (2048 bit) */ +#define MAXLEN_RESP_PUBKEY_GNUK 271 + static struct do_info pgp1_objects[] = { /* OpenPGP card spec 1.1 */ { 0x004f, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL }, { 0x005b, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data }, @@ -193,12 +224,12 @@ static struct do_info pgp1_objects[] = { /* OpenPGP card spec 1.1 */ { 0x5f35, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data }, { 0x5f50, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data }, { 0x7f49, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, NULL, NULL }, - { 0xa400, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL }, - { 0xa401, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL }, - { 0xb600, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL }, - { 0xb601, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL }, - { 0xb800, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL }, - { 0xb801, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL }, + { DO_AUTH, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL }, + { DO_AUTH_SYM, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL }, + { DO_SIGN, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL }, + { DO_SIGN_SYM, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL }, + { DO_ENCR, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL }, + { DO_ENCR_SYM, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL }, { 0, 0, 0, NULL, NULL }, }; @@ -247,29 +278,25 @@ static struct do_info pgp2_objects[] = { /* OpenPGP card spec 2.0 */ { 0x5f52, SIMPLE, READ_ALWAYS | WRITE_NEVER, sc_get_data, NULL }, /* The 7F21 is constructed DO in spec, but in practice, its content can be retrieved * as simple DO (no need to parse TLV). */ - { 0x7f21, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data }, + { DO_CERT, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data }, { 0x7f48, CONSTRUCTED, READ_NEVER | WRITE_NEVER, NULL, NULL }, { 0x7f49, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, NULL, NULL }, - { 0xa400, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL }, + { DO_AUTH, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL }, /* The 0xA401, 0xB601, 0xB801 are just symbolic, it does not represent any real DO. * However, their R/W access condition may block the process of importing key in pkcs15init. * So we set their accesses condition as WRITE_PIN3 (writable). */ - { 0xa401, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL }, - { 0xb600, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL }, - { 0xb601, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL }, - { 0xb800, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL }, - { 0xb801, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL }, + { DO_AUTH_SYM, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL }, + { DO_SIGN, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL }, + { DO_SIGN_SYM, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL }, + { DO_ENCR, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL }, + { DO_ENCR_SYM, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL }, { 0, 0, 0, NULL, NULL }, }; -/* The DO holding X.509 certificate is constructed but does not contain child DO. - * We should notice this when building fake file system later. */ -#define DO_CERT 0x7f21 - #define DRVDATA(card) ((struct pgp_priv_data *) ((card)->drv_data)) struct pgp_priv_data { - struct blob * mf; - struct blob * current; /* currently selected file */ + pgp_blob_t * mf; + pgp_blob_t * current; /* currently selected file */ enum _version bcd_version; struct do_info *pgp_objects; @@ -307,7 +334,9 @@ pgp_init(sc_card_t *card) sc_file_t *file = NULL; struct do_info *info; int r; - struct blob *child = NULL; + pgp_blob_t *child = NULL; + + LOG_FUNC_CALLED(card->ctx); priv = calloc (1, sizeof *priv); if (!priv) @@ -317,11 +346,11 @@ pgp_init(sc_card_t *card) card->cla = 0x00; /* set pointer to correct list of card objects */ - priv->pgp_objects = (card->type == SC_CARD_TYPE_OPENPGP_V2) + priv->pgp_objects = (card->type == SC_CARD_TYPE_OPENPGP_V2 || card->type == SC_CARD_TYPE_OPENPGP_GNUK) ? pgp2_objects : pgp1_objects; /* set detailed card version */ - priv->bcd_version = (card->type == SC_CARD_TYPE_OPENPGP_V2) + priv->bcd_version = (card->type == SC_CARD_TYPE_OPENPGP_V2 || card->type == SC_CARD_TYPE_OPENPGP_GNUK) ? OPENPGP_CARD_2_0 : OPENPGP_CARD_1_1; /* select application "OpenPGP" */ @@ -362,8 +391,8 @@ pgp_init(sc_card_t *card) /* Populate MF - add matching blobs listed in the pgp_objects table. */ for (info = priv->pgp_objects; (info != NULL) && (info->id > 0); info++) { - if (((info->access & READ_MASK) == READ_ALWAYS) && - (info->get_fn != NULL)) { + if (((info->access & READ_MASK) != READ_NEVER) && + (info->get_fn != NULL)) { child = pgp_new_blob(card, priv->mf, info->id, sc_file_new()); /* catch out of memory condition */ @@ -389,7 +418,8 @@ pgp_get_card_features(sc_card_t *card) unsigned char *hist_bytes = card->atr.value; size_t atr_len = card->atr.len; size_t i = 0; - struct blob *blob, *blob6e, *blob73; + unsigned int j = 0; + pgp_blob_t *blob, *blob6e, *blob73; /* parse card capabilities from historical bytes */ while ((i < atr_len) && (hist_bytes[i] != 0x73)) @@ -436,7 +466,8 @@ pgp_get_card_features(sc_card_t *card) if ((pgp_get_blob(card, blob73, 0x00c0, &blob) >= 0) && (blob->data != NULL) && (blob->len > 0)) { /* in v2.0 bit 0x04 in first byte means "algorithm attributes changeable */ - if ((blob->data[0] & 0x04) && (card->type == SC_CARD_TYPE_OPENPGP_V2)) + if ((blob->data[0] & 0x04) && + (card->type == SC_CARD_TYPE_OPENPGP_V2 || card->type == SC_CARD_TYPE_OPENPGP_GNUK)) priv->ext_caps |= EXT_CAP_ALG_ATTR_CHANGEABLE; /* bit 0x08 in first byte means "support for private use DOs" */ if (blob->data[0] & 0x08) @@ -453,7 +484,8 @@ pgp_get_card_features(sc_card_t *card) priv->ext_caps |= EXT_CAP_GET_CHALLENGE; } /* in v2.0 bit 0x80 in first byte means "support Secure Messaging" */ - if ((blob->data[0] & 0x80) && (card->type == SC_CARD_TYPE_OPENPGP_V2)) + if ((blob->data[0] & 0x80) && + (card->type == SC_CARD_TYPE_OPENPGP_V2 || card->type == SC_CARD_TYPE_OPENPGP_GNUK)) priv->ext_caps |= EXT_CAP_SM; if ((priv->bcd_version >= OPENPGP_CARD_2_0) && (blob->len >= 10)) { @@ -469,13 +501,13 @@ pgp_get_card_features(sc_card_t *card) /* get max. PIN length from "CHV status bytes" DO */ if ((pgp_get_blob(card, blob73, 0x00c4, &blob) >= 0) && - (blob->data != NULL) && (blob->len > 1)) { + (blob->data != NULL) && (blob->len > 1)) { /* 2nd byte in "CHV status bytes" DO means "max. PIN length" */ card->max_pin_len = blob->data[1]; } /* get supported algorithms & key lengths from "algorithm attributes" DOs */ - for (i = 0x00c1; i <= 0x00c3; i++) { + for (j = 0x00c1; j <= 0x00c3; j++) { unsigned long flags; /* Is this correct? */ @@ -487,8 +519,8 @@ pgp_get_card_features(sc_card_t *card) /* Can be generated in card */ flags |= SC_ALGORITHM_ONBOARD_KEY_GEN; - if ((pgp_get_blob(card, blob73, i, &blob) >= 0) && - (blob->data != NULL) && (blob->len >= 4)) { + if ((pgp_get_blob(card, blob73, j, &blob) >= 0) && + (blob->data != NULL) && (blob->len >= 4)) { if (blob->data[0] == 0x01) { /* Algorithm ID [RFC4880]: RSA */ unsigned int keylen = bebytes2ushort(blob->data + 1); /* Measured in bit */ @@ -524,7 +556,7 @@ pgp_finish(sc_card_t *card) /* internal: fill a blob's data */ static int -pgp_set_blob(struct blob *blob, const u8 *data, size_t len) +pgp_set_blob(pgp_blob_t *blob, const u8 *data, size_t len) { if (blob->data) free(blob->data); @@ -539,7 +571,7 @@ pgp_set_blob(struct blob *blob, const u8 *data, size_t len) return SC_ERROR_OUT_OF_MEMORY; blob->data = tmp; - blob->len = len; + blob->len = (unsigned int)len; if (data != NULL) memcpy(blob->data, data, len); } @@ -618,16 +650,16 @@ pgp_attach_acl(sc_card_t *card, sc_file_t *file, struct do_info *info) } /* internal: append a blob to the list of children of a given parent blob */ -static struct blob * -pgp_new_blob(sc_card_t *card, struct blob *parent, unsigned int file_id, +static pgp_blob_t * +pgp_new_blob(sc_card_t *card, pgp_blob_t *parent, unsigned int file_id, sc_file_t *file) { - struct blob *blob = NULL; + pgp_blob_t *blob = NULL; if (file == NULL) return NULL; - if ((blob = calloc(1, sizeof(struct blob))) != NULL) { + if ((blob = calloc(1, sizeof(pgp_blob_t))) != NULL) { struct pgp_priv_data *priv = DRVDATA (card); struct do_info *info; @@ -641,7 +673,7 @@ pgp_new_blob(sc_card_t *card, struct blob *parent, unsigned int file_id, blob->parent = parent; if (parent != NULL) { - struct blob **p; + pgp_blob_t **p; /* set file's path = parent's path + file's id */ blob->file->path = parent->file->path; @@ -679,11 +711,11 @@ pgp_new_blob(sc_card_t *card, struct blob *parent, unsigned int file_id, /* internal: free a blob including its content */ static void -pgp_free_blob(struct blob *blob) +pgp_free_blob(pgp_blob_t *blob) { if (blob) { if (blob->parent) { - struct blob **p; + pgp_blob_t **p; /* remove blob from list of parent's children */ for (p = &blob->parent->files; *p != NULL && *p != blob; p = &(*p)->next) @@ -703,14 +735,14 @@ pgp_free_blob(struct blob *blob) /* internal: iterate through the blob tree, calling a function for each blob */ static void -pgp_iterate_blobs(struct blob *blob, int level, void (*func)()) +pgp_iterate_blobs(pgp_blob_t *blob, int level, void (*func)()) { if (blob) { if (level > 0) { - struct blob *child = blob->files; + pgp_blob_t *child = blob->files; while (child != NULL) { - struct blob *next = child->next; + pgp_blob_t *next = child->next; pgp_iterate_blobs(child, level-1, func); child = next; @@ -723,8 +755,10 @@ pgp_iterate_blobs(struct blob *blob, int level, void (*func)()) /* internal: read a blob's contents from card */ static int -pgp_read_blob(sc_card_t *card, struct blob *blob) +pgp_read_blob(sc_card_t *card, pgp_blob_t *blob) { + struct pgp_priv_data *priv = DRVDATA (card); + if (blob->data != NULL) return SC_SUCCESS; if (blob->info == NULL) @@ -734,7 +768,22 @@ pgp_read_blob(sc_card_t *card, struct blob *blob) u8 buffer[2048]; size_t buf_len = (card->caps & SC_CARD_CAP_APDU_EXT) ? sizeof(buffer) : 256; - int r = blob->info->get_fn(card, blob->id, buffer, buf_len); + int r = SC_SUCCESS; + + /* Buffer length for certificate */ + if (blob->id == DO_CERT && priv->max_cert_size > 0) { + buf_len = MIN(priv->max_cert_size, sizeof(buffer)); + } + + /* Buffer length for Gnuk pubkey */ + if (card->type == SC_CARD_TYPE_OPENPGP_GNUK && + (blob->id == DO_AUTH || blob->id == DO_SIGN || blob->id == DO_ENCR + || blob->id == DO_AUTH_SYM || blob->id == DO_SIGN_SYM + || blob->id == DO_ENCR_SYM)) { + buf_len = MAXLEN_RESP_PUBKEY_GNUK; + } + + r = blob->info->get_fn(card, blob->id, buffer, buf_len); if (r < 0) { /* an error occurred */ blob->status = r; @@ -754,7 +803,7 @@ pgp_read_blob(sc_card_t *card, struct blob *blob) * The OpenPGP card has a TLV encoding according ASN.1 BER-encoding rules. */ static int -pgp_enumerate_blob(sc_card_t *card, struct blob *blob) +pgp_enumerate_blob(sc_card_t *card, pgp_blob_t *blob) { const u8 *in; int r; @@ -771,7 +820,7 @@ pgp_enumerate_blob(sc_card_t *card, struct blob *blob) unsigned int cla, tag, tmptag; size_t len; const u8 *data = in; - struct blob *new; + pgp_blob_t *new; r = sc_asn1_read_tag(&data, blob->len - (in - blob->data), &cla, &tag, &len); @@ -801,10 +850,10 @@ pgp_enumerate_blob(sc_card_t *card, struct blob *blob) /* internal: find a blob by ID below a given parent, filling its contents when necessary */ static int -pgp_get_blob(sc_card_t *card, struct blob *blob, unsigned int id, - struct blob **ret) +pgp_get_blob(sc_card_t *card, pgp_blob_t *blob, unsigned int id, + pgp_blob_t **ret) { - struct blob *child; + pgp_blob_t *child; int r; if ((r = pgp_enumerate_blob(card, blob)) < 0) @@ -818,15 +867,32 @@ pgp_get_blob(sc_card_t *card, struct blob *blob, unsigned int id, } } + /* This part is for "NOT FOUND" cases */ + + /* Special case: + * Gnuk does not have default value for children of DO 65 (DOs 5B, 5F2D, 5F35) + * So, if these blob was not found, we create it. */ + if (blob->id == DO_CARDHOLDER && (id == DO_NAME || id == DO_LANG_PREF || id == DO_SEX)) { + sc_log(card->ctx, "Create blob %X under %X", id, blob->id); + child = pgp_new_blob(card, blob, id, sc_file_new()); + if (child) { + pgp_set_blob(child, NULL, 0); + *ret = child; + return SC_SUCCESS; + } + else + sc_log(card->ctx, "Not enough memory to create blob for DO %X"); + } + return SC_ERROR_FILE_NOT_FOUND; } /* Internal: search recursively for a blob by ID below a given root */ static int -pgp_seek_blob(sc_card_t *card, struct blob *root, unsigned int id, - struct blob **ret) +pgp_seek_blob(sc_card_t *card, pgp_blob_t *root, unsigned int id, + pgp_blob_t **ret) { - struct blob *child; + pgp_blob_t *child; int r; if ((r = pgp_get_blob(card, root, id, ret)) == 0) @@ -848,11 +914,11 @@ pgp_seek_blob(sc_card_t *card, struct blob *root, unsigned int id, } /* internal: find a blob by tag - pgp_seek_blob with optimizations */ -static struct blob * +static pgp_blob_t * pgp_find_blob(sc_card_t *card, unsigned int tag) { struct pgp_priv_data *priv = DRVDATA(card); - struct blob *blob = NULL; + pgp_blob_t *blob = NULL; int r; /* Check if current selected blob is which we want to test*/ @@ -906,7 +972,7 @@ static int pgp_select_file(sc_card_t *card, const sc_path_t *path, sc_file_t **ret) { struct pgp_priv_data *priv = DRVDATA(card); - struct blob *blob; + pgp_blob_t *blob; unsigned int path_start = 0; unsigned int n; sc_path_t dummy_path; @@ -938,7 +1004,6 @@ pgp_select_file(sc_card_t *card, const sc_path_t *path, sc_file_t **ret) * So we set its size to be the same as max certificate size the card supports. */ (*ret)->size = priv->max_cert_size; } - priv->current = NULL; LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } @@ -955,7 +1020,6 @@ pgp_select_file(sc_card_t *card, const sc_path_t *path, sc_file_t **ret) /* This file ID is refered when importing key&certificate via pkcs15init, like above. * We pretend to successfully find this inexistent file. */ if (id == 0x4402 || id == 0x5f48) { - priv->current = NULL; if (ret == NULL) /* No need to return file */ LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); @@ -967,7 +1031,6 @@ pgp_select_file(sc_card_t *card, const sc_path_t *path, sc_file_t **ret) } if (r < 0) { /* failure */ - priv->current = NULL; LOG_FUNC_RETURN(card->ctx, r); } } @@ -987,7 +1050,7 @@ static int pgp_list_files(sc_card_t *card, u8 *buf, size_t buflen) { struct pgp_priv_data *priv = DRVDATA(card); - struct blob *blob; + pgp_blob_t *blob; unsigned int k; int r; @@ -1023,7 +1086,7 @@ pgp_read_binary(sc_card_t *card, unsigned int idx, u8 *buf, size_t count, unsigned long flags) { struct pgp_priv_data *priv = DRVDATA(card); - struct blob *blob; + pgp_blob_t *blob; int r; LOG_FUNC_CALLED(card->ctx); @@ -1047,7 +1110,7 @@ pgp_read_binary(sc_card_t *card, unsigned int idx, count = blob->len - idx; memcpy(buf, blob->data + idx, count); - LOG_FUNC_RETURN(card->ctx, count); + LOG_FUNC_RETURN(card->ctx, (int)count); } @@ -1065,12 +1128,18 @@ static int pgp_get_pubkey(sc_card_t *card, unsigned int tag, u8 *buf, size_t buf_len) { sc_apdu_t apdu; + u8 apdu_case = SC_APDU_CASE_4; u8 idbuf[2]; int r; sc_log(card->ctx, "called, tag=%04x\n", tag); - sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x47, 0x81, 0); + /* With Gnuk token, force to use short APDU */ + if (card->type == SC_CARD_TYPE_OPENPGP_GNUK) { + apdu_case = SC_APDU_CASE_4_SHORT; + } + + sc_format_apdu(card, &apdu, apdu_case, 0x47, 0x81, 0); apdu.lc = 2; apdu.data = ushort2bebytes(idbuf, tag); apdu.datalen = 2; @@ -1084,7 +1153,7 @@ pgp_get_pubkey(sc_card_t *card, unsigned int tag, u8 *buf, size_t buf_len) r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, r, "Card returned error"); - LOG_FUNC_RETURN(card->ctx, apdu.resplen); + LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen); } @@ -1093,7 +1162,7 @@ static int pgp_get_pubkey_pem(sc_card_t *card, unsigned int tag, u8 *buf, size_t buf_len) { struct pgp_priv_data *priv = DRVDATA(card); - struct blob *blob, *mod_blob, *exp_blob; + pgp_blob_t *blob, *mod_blob, *exp_blob; sc_pkcs15_pubkey_t pubkey; u8 *data; size_t len; @@ -1102,11 +1171,11 @@ pgp_get_pubkey_pem(sc_card_t *card, unsigned int tag, u8 *buf, size_t buf_len) sc_log(card->ctx, "called, tag=%04x\n", tag); if ((r = pgp_get_blob(card, priv->mf, tag & 0xFFFE, &blob)) < 0 - || (r = pgp_get_blob(card, blob, 0x7F49, &blob)) < 0 - || (r = pgp_get_blob(card, blob, 0x0081, &mod_blob)) < 0 - || (r = pgp_get_blob(card, blob, 0x0082, &exp_blob)) < 0 - || (r = pgp_read_blob(card, mod_blob)) < 0 - || (r = pgp_read_blob(card, exp_blob)) < 0) + || (r = pgp_get_blob(card, blob, 0x7F49, &blob)) < 0 + || (r = pgp_get_blob(card, blob, 0x0081, &mod_blob)) < 0 + || (r = pgp_get_blob(card, blob, 0x0082, &exp_blob)) < 0 + || (r = pgp_read_blob(card, mod_blob)) < 0 + || (r = pgp_read_blob(card, exp_blob)) < 0) LOG_TEST_RET(card->ctx, r, "error getting elements"); memset(&pubkey, 0, sizeof(pubkey)); @@ -1124,7 +1193,7 @@ pgp_get_pubkey_pem(sc_card_t *card, unsigned int tag, u8 *buf, size_t buf_len) memcpy(buf, data, len); free(data); - LOG_FUNC_RETURN(card->ctx, len); + LOG_FUNC_RETURN(card->ctx, (int)len); } @@ -1146,28 +1215,157 @@ pgp_get_data(sc_card_t *card, unsigned int tag, u8 *buf, size_t buf_len) LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); + + /* For Gnuk card, if there is no certificate, it returns error instead of empty data. + * So, for this case, we ignore error and consider success */ + if (r == SC_ERROR_DATA_OBJECT_NOT_FOUND && card->type == SC_CARD_TYPE_OPENPGP_GNUK + && (tag == DO_CERT || tag == DO_PRIV1 || tag == DO_PRIV2 || tag == DO_PRIV3 || tag == DO_PRIV4)) { + r = SC_SUCCESS; + apdu.resplen = 0; + } LOG_TEST_RET(card->ctx, r, "Card returned error"); - LOG_FUNC_RETURN(card->ctx, apdu.resplen); + LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen); +} + + +/* Internal: Write certificate for Gnuk */ +static int gnuk_write_certificate(sc_card_t *card, const u8 *buf, size_t length) +{ + sc_context_t *ctx = card->ctx; + size_t i = 0; + sc_apdu_t apdu; + u8 *part; + size_t plen; + /* Two round_ variables below are to build APDU data + * with even length for Gnuk */ + u8 roundbuf[256]; + size_t roundlen = 0; + int r = SC_SUCCESS; + + LOG_FUNC_CALLED(ctx); + + /* If null data is passed, delete certificate */ + if (buf == NULL || length == 0) { + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xD6, 0x85, 0); + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + /* Check response */ + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "Certificate writing failed"); + } + + /* Ref: gnuk_put_binary_libusb.py and gnuk_token.py in Gnuk source tree */ + /* Split data to segments of 256 bytes. Send each segment via command chaining, + * with particular P1 byte for each segment */ + while (i*256 < length) { + part = (u8 *)buf + i*256; + plen = MIN(length - i*256, 256); + + sc_log(card->ctx, "Write part %d from offset 0x%X, len %d", i+1, part, plen); + + if (i == 0) { + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xD6, 0x85, 0); + } + else { + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xD6, i, 0); + } + apdu.flags |= SC_APDU_FLAGS_CHAINING; + + /* If the last part has odd length, we add zero padding to make it even. + * Gnuk does not allow data with odd length */ + if (plen < 256 && (plen % 2) != 0) { + roundlen = plen + 1; + memset(roundbuf, 0, roundlen); + memcpy(roundbuf, part, plen); + apdu.data = roundbuf; + apdu.datalen = apdu.lc = roundlen; + } + else { + apdu.data = part; + apdu.datalen = apdu.lc = plen; + } + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + /* Check response */ + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "UPDATE BINARY returned error"); + + /* To next part */ + i++; + } + LOG_FUNC_RETURN(card->ctx, (int)length); +} + + +/* Internal: Use PUT DATA command to write */ +static int +pgp_put_data_plain(sc_card_t *card, unsigned int tag, const u8 *buf, size_t buf_len) +{ + struct pgp_priv_data *priv = DRVDATA(card); + sc_context_t *ctx = card->ctx; + sc_apdu_t apdu; + u8 ins = 0xDA; + u8 p1 = tag >> 8; + u8 p2 = tag & 0xFF; + u8 apdu_case = SC_APDU_CASE_3; + int r; + + LOG_FUNC_CALLED(ctx); + + /* Extended Header list (004D DO) needs a variant of PUT DATA command */ + if (tag == 0x004D) { + ins = 0xDB; + p1 = 0x3F; + p2 = 0xFF; + } + + /* Build APDU */ + if (buf != NULL && buf_len > 0) { + /* Force short APDU for Gnuk */ + if (card->type == SC_CARD_TYPE_OPENPGP_GNUK) { + apdu_case = SC_APDU_CASE_3_SHORT; + } + sc_format_apdu(card, &apdu, apdu_case, ins, p1, p2); + + /* if card/reader does not support extended APDUs, but chaining, then set it */ + if (((card->caps & SC_CARD_CAP_APDU_EXT) == 0) && (priv->ext_caps & EXT_CAP_CHAINING)) + apdu.flags |= SC_APDU_FLAGS_CHAINING; + + apdu.data = (u8 *)buf; + apdu.datalen = buf_len; + apdu.lc = buf_len; + } + else { + /* This case is to empty DO */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, ins, p1, p2); + } + + /* Send APDU to card */ + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, r, "APDU transmit failed"); + /* Check response */ + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + + if (r < 0) + LOG_FUNC_RETURN(ctx, r); + + LOG_FUNC_RETURN(ctx, (int)buf_len); } /* ABI: PUT DATA */ static int pgp_put_data(sc_card_t *card, unsigned int tag, const u8 *buf, size_t buf_len) { - sc_apdu_t apdu; struct pgp_priv_data *priv = DRVDATA(card); - struct blob *affected_blob = NULL; + pgp_blob_t *affected_blob = NULL; struct do_info *dinfo = NULL; - u8 ins = 0xDA; - u8 p1 = tag >> 8; - u8 p2 = tag & 0xFF; int r; LOG_FUNC_CALLED(card->ctx); /* Check if the tag is writable */ - affected_blob = pgp_find_blob(card, tag); + if (priv->current->id != tag) + affected_blob = pgp_find_blob(card, tag); /* Non-readable DOs have no represented blob, we have to check from pgp_get_info_by_tag */ if (affected_blob == NULL) @@ -1194,35 +1392,14 @@ pgp_put_data(sc_card_t *card, unsigned int tag, const u8 *buf, size_t buf_len) LOG_FUNC_RETURN(card->ctx, SC_ERROR_WRONG_LENGTH); } - /* Extended Header list (004D DO) needs a variant of PUT DATA command */ - if (tag == 0x004D) { - ins = 0xDB; - p1 = 0x3F; - p2 = 0xFF; - } - - /* Build APDU */ - if (buf != NULL && buf_len > 0) { - sc_format_apdu(card, &apdu, SC_APDU_CASE_3, ins, p1, p2); - - /* if card/reader does not support extended APDUs, but chaining, then set it */ - if (((card->caps & SC_CARD_CAP_APDU_EXT) == 0) && (priv->ext_caps & EXT_CAP_CHAINING)) - apdu.flags |= SC_APDU_FLAGS_CHAINING; - - apdu.data = buf; - apdu.datalen = buf_len; - apdu.lc = buf_len; + if (tag == DO_CERT && card->type == SC_CARD_TYPE_OPENPGP_GNUK) { + /* Gnuk need a special way to write certificate. */ + r = gnuk_write_certificate(card, buf, buf_len); } else { - sc_format_apdu(card, &apdu, SC_APDU_CASE_1, ins, p1, p2); + r = pgp_put_data_plain(card, tag, buf, buf_len); } - /* Send APDU to card */ - r = sc_transmit_apdu(card, &apdu); - LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); - /* Check response */ - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - /* Instruct more in case of error */ if (r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Please verify PIN first."); @@ -1238,7 +1415,7 @@ pgp_put_data(sc_card_t *card, unsigned int tag, const u8 *buf, size_t buf_len) /* pgp_set_blob()'s failures do not impact pgp_put_data()'s result */ } - LOG_FUNC_RETURN(card->ctx, buf_len); + LOG_FUNC_RETURN(card->ctx, (int)buf_len); } @@ -1336,6 +1513,7 @@ pgp_compute_signature(sc_card_t *card, const u8 *data, struct pgp_priv_data *priv = DRVDATA(card); sc_security_env_t *env = &priv->sec_env; sc_apdu_t apdu; + u8 apdu_case = SC_APDU_CASE_4; int r; LOG_FUNC_CALLED(card->ctx); @@ -1344,14 +1522,19 @@ pgp_compute_signature(sc_card_t *card, const u8 *data, LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "invalid operation"); + /* Force short APDU for Gnuk Token */ + if (card->type == SC_CARD_TYPE_OPENPGP_GNUK) { + apdu_case = SC_APDU_CASE_4_SHORT; + } + switch (env->key_ref[0]) { case 0x00: /* signature key */ /* PSO SIGNATURE */ - sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x2A, 0x9E, 0x9A); + sc_format_apdu(card, &apdu, apdu_case, 0x2A, 0x9E, 0x9A); break; case 0x02: /* authentication key */ /* INTERNAL AUTHENTICATE */ - sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x88, 0, 0); + sc_format_apdu(card, &apdu, apdu_case, 0x88, 0, 0); break; case 0x01: default: @@ -1360,7 +1543,7 @@ pgp_compute_signature(sc_card_t *card, const u8 *data, } apdu.lc = data_len; - apdu.data = data; + apdu.data = (u8 *)data; apdu.datalen = data_len; apdu.le = ((outlen >= 256) && !(card->caps & SC_CARD_CAP_APDU_EXT)) ? 256 : outlen; apdu.resp = out; @@ -1372,7 +1555,7 @@ pgp_compute_signature(sc_card_t *card, const u8 *data, r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, r, "Card returned error"); - LOG_FUNC_RETURN(card->ctx, apdu.resplen); + LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen); } @@ -1384,6 +1567,7 @@ pgp_decipher(sc_card_t *card, const u8 *in, size_t inlen, struct pgp_priv_data *priv = DRVDATA(card); sc_security_env_t *env = &priv->sec_env; sc_apdu_t apdu; + u8 apdu_case = SC_APDU_CASE_4; u8 *temp = NULL; int r; @@ -1408,7 +1592,7 @@ pgp_decipher(sc_card_t *card, const u8 *in, size_t inlen, case 0x01: /* Decryption key */ case 0x02: /* authentication key */ /* PSO DECIPHER */ - sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x2A, 0x80, 0x86); + sc_format_apdu(card, &apdu, apdu_case, 0x2A, 0x80, 0x86); break; case 0x00: /* signature key */ default: @@ -1417,8 +1601,13 @@ pgp_decipher(sc_card_t *card, const u8 *in, size_t inlen, "invalid key reference"); } + /* Gnuk only supports short APDU, so we need to use command chaining */ + if (card->type == SC_CARD_TYPE_OPENPGP_GNUK) { + apdu.flags |= SC_APDU_FLAGS_CHAINING; + } + apdu.lc = inlen; - apdu.data = in; + apdu.data = (u8 *)in; apdu.datalen = inlen; apdu.le = ((outlen >= 256) && !(card->caps & SC_CARD_CAP_APDU_EXT)) ? 256 : outlen; apdu.resp = out; @@ -1431,7 +1620,7 @@ pgp_decipher(sc_card_t *card, const u8 *in, size_t inlen, r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, r, "Card returned error"); - LOG_FUNC_RETURN(card->ctx, apdu.resplen); + LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen); } #ifdef ENABLE_OPENSSL @@ -1442,7 +1631,7 @@ static int pgp_update_new_algo_attr(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_info) { struct pgp_priv_data *priv = DRVDATA(card); - struct blob *algo_blob; + pgp_blob_t *algo_blob; unsigned int old_modulus_len; /* Measured in bit */ unsigned int old_exponent_len; const unsigned int tag = 0x00C0 | key_info->keytype; @@ -1466,8 +1655,8 @@ pgp_update_new_algo_attr(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_ } /* To generate key with new key size */ else if (old_modulus_len != key_info->modulus_len) { - algo_blob->data[1] = key_info->modulus_len >> 8; - algo_blob->data[2] = key_info->modulus_len; + algo_blob->data[1] = (unsigned char)key_info->modulus_len >> 8; + algo_blob->data[2] = (unsigned char)key_info->modulus_len; changed = 1; } @@ -1477,8 +1666,8 @@ pgp_update_new_algo_attr(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_ key_info->exponent_len = old_exponent_len; } else if (old_exponent_len != key_info->exponent_len) { - algo_blob->data[3] = key_info->exponent_len >> 8; - algo_blob->data[4] = key_info->exponent_len; + algo_blob->data[3] = (unsigned char)key_info->exponent_len >> 8; + algo_blob->data[4] = (unsigned char)key_info->exponent_len; changed = 1; } @@ -1522,7 +1711,7 @@ static int pgp_store_creationtime(sc_card_t *card, u8 key_id, time_t *outtime) strftime(timestring, timestrlen, "%c %Z", gmtime(&createtime)); sc_log(card->ctx, "Creation time %s.", timestring); /* Code borrowed from GnuPG */ - ulong2bebytes(buf, createtime); + ulong2bebytes(buf, (unsigned long)createtime); r = pgp_put_data(card, 0x00CD + key_id, buf, 4); LOG_TEST_RET(card->ctx, r, "Cannot write to DO"); LOG_FUNC_RETURN(card->ctx, r); @@ -1547,7 +1736,7 @@ pgp_calculate_and_store_fingerprint(sc_card_t *card, time_t ctime, u8 *p; /* Use this pointer to set fp_buffer content */ size_t pk_packet_len; unsigned int tag; - struct blob *fpseq_blob; + pgp_blob_t *fpseq_blob; u8 *newdata; int r; @@ -1574,19 +1763,19 @@ pgp_calculate_and_store_fingerprint(sc_card_t *card, time_t ctime, } p[0] = 0x99; /* http://tools.ietf.org/html/rfc4880 page 71 */ - ushort2bebytes(++p, pk_packet_len); + ushort2bebytes(++p, (unsigned short)pk_packet_len); /* Start pk_packet */ p += 2; *p = 4; /* Version 4 key */ - ulong2bebytes(++p, ctime); /* Creation time */ + ulong2bebytes(++p, (unsigned long)ctime); /* Creation time */ p += 4; *p = 1; /* RSA */ /* Algorithm-specific fields */ - ushort2bebytes(++p, key_info->modulus_len); + ushort2bebytes(++p, (unsigned short)key_info->modulus_len); p += 2; memcpy(p, modulus, mlen); p += mlen; - ushort2bebytes(++p, key_info->exponent_len); + ushort2bebytes(++p, (unsigned short)key_info->exponent_len); p += 2; memcpy(p, exponent, elen); p = NULL; @@ -1636,7 +1825,7 @@ pgp_update_pubkey_blob(sc_card_t *card, u8* modulus, size_t modulus_len, u8* exponent, size_t exponent_len, u8 key_id) { struct pgp_priv_data *priv = DRVDATA(card); - struct blob *pk_blob; + pgp_blob_t *pk_blob; unsigned int blob_id; sc_pkcs15_pubkey_t pubkey; u8 *data = NULL; @@ -1646,11 +1835,11 @@ pgp_update_pubkey_blob(sc_card_t *card, u8* modulus, size_t modulus_len, LOG_FUNC_CALLED(card->ctx); if (key_id == SC_OPENPGP_KEY_SIGN) - blob_id = 0xB601; + blob_id = DO_SIGN_SYM; else if (key_id == SC_OPENPGP_KEY_ENCR) - blob_id = 0xB801; + blob_id = DO_ENCR_SYM; else if (key_id == SC_OPENPGP_KEY_AUTH) - blob_id = 0xA401; + blob_id = DO_AUTH_SYM; else { sc_log(card->ctx, "Unknown key id %X.", key_id); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); @@ -1766,7 +1955,7 @@ static int pgp_update_card_algorithms(sc_card_t *card, sc_cardctl_openpgp_keygen /* Get the algorithm corresponding to the key ID */ algo = card->algorithms + (id - 1); /* Update new key length attribute */ - algo->key_length = key_info->modulus_len; + algo->key_length = (unsigned int)key_info->modulus_len; LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } @@ -1783,6 +1972,7 @@ static int pgp_gen_key(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_in u8 apdu_case; u8 *apdu_data; size_t apdu_le; + size_t resplen = 0; int r = SC_SUCCESS; LOG_FUNC_CALLED(card->ctx); @@ -1802,6 +1992,11 @@ static int pgp_gen_key(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_in LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } + if (card->type == SC_CARD_TYPE_OPENPGP_GNUK && key_info->modulus_len != 2048) { + sc_log(card->ctx, "Gnuk does not support other key length than 2048."); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + } + /* Set attributes for new-generated key */ r = pgp_update_new_algo_attr(card, key_info); LOG_TEST_RET(card->ctx, r, "Cannot set attributes for new-generated key"); @@ -1809,30 +2004,36 @@ static int pgp_gen_key(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_in /* Test whether we will need extended APDU. 1900 is an * arbitrary modulus length which for sure fits into a short APDU. * This idea is borrowed from GnuPG code. */ - if (card->caps & SC_CARD_CAP_APDU_EXT && key_info->modulus_len > 1900) { + if (card->caps & SC_CARD_CAP_APDU_EXT + && key_info->modulus_len > 1900 + && card->type != SC_CARD_TYPE_OPENPGP_GNUK) { /* We won't store to apdu variable yet, because it will be reset in * sc_format_apdu() */ apdu_le = card->max_recv_size; apdu_case = SC_APDU_CASE_4_EXT; } else { - apdu_le = 256; apdu_case = SC_APDU_CASE_4_SHORT; + apdu_le = 256; + resplen = MAXLEN_RESP_PUBKEY; + } + if (card->type == SC_CARD_TYPE_OPENPGP_GNUK) { + resplen = MAXLEN_RESP_PUBKEY_GNUK; } /* Prepare APDU */ - sc_format_apdu(card, &apdu, apdu_case, 0x47, 0x80, 0); + sc_format_apdu(card, &apdu, apdu_case, 0x47, 0x80, 0); apdu.data = apdu_data; apdu.datalen = 2; /* Data = B600 */ apdu.lc = 2; apdu.le = apdu_le; /* Buffer to receive response */ - apdu.resp = calloc(apdu.le, 1); + apdu.resplen = (resplen > 0) ? resplen : apdu_le; + apdu.resp = calloc(apdu.resplen, 1); if (apdu.resp == NULL) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY); } - apdu.resplen = apdu.le; /* Send */ sc_log(card->ctx, "Waiting for the card to generate key..."); @@ -1883,7 +2084,7 @@ pgp_build_tlv(sc_context_t *ctx, unsigned int tag, u8 *data, size_t len, u8 **ou highest_order--; if (highest_order >= 4) cla = 0x00; - else + else cla = tag >> 8*highest_order; /* Restore class bits */ *out[0] |= cla; @@ -1906,10 +2107,10 @@ set_taglength_tlv(u8 *buffer, unsigned int tag, size_t length) *p++ = (tag >> 8) & 0xFF; *p++ = tag; if (length < 128) - *p++ = length; + *p++ = (u8)length; else if (length < 256) { *p++ = 0x81; - *p++ = length; + *p++ = (u8)length; } else { if (length > 0xffff) @@ -1953,7 +2154,7 @@ pgp_build_extended_header_list(sc_card_t *card, sc_cardctl_openpgp_keystore_info u8 *p = NULL; u8 *components[] = {key_info->e, key_info->p, key_info->q, key_info->n}; size_t componentlens[] = {key_info->e_len, key_info->p_len, key_info->q_len, key_info->n_len}; - unsigned int componenttags[] = {0x91, 0x92, 0x93, 0x95}; + unsigned int componenttags[] = {0x91, 0x92, 0x93, 0x97}; char *componentnames[] = { "public exponent", "prime p", @@ -1962,7 +2163,7 @@ pgp_build_extended_header_list(sc_card_t *card, sc_cardctl_openpgp_keystore_info }; size_t comp_to_add = 3; size_t req_e_len = 0; /* The exponent length specified in Algorithm Attributes */ - struct blob *alat_blob; + pgp_blob_t *alat_blob; u8 i; int r; @@ -2171,12 +2372,72 @@ out: #endif /* ENABLE_OPENSSL */ +/** + * Erase card + **/ +static int pgp_erase_card(sc_card_t *card) +{ + sc_context_t *ctx = card->ctx; + /* Special series of commands to erase OpenPGP card, + * according to https://www.crypto-stick.com/en/faq + * (How to reset a Crypto Stick? question). + * Gnuk is known not to support this feature. */ + u8 apdu_binaries[10][13] = { + {0, 0x20, 0, 0x81, 0x08, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40}, + {0, 0x20, 0, 0x81, 0x08, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40}, + {0, 0x20, 0, 0x81, 0x08, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40}, + {0, 0x20, 0, 0x81, 0x08, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40}, + {0, 0x20, 0, 0x83, 0x08, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40}, + {0, 0x20, 0, 0x83, 0x08, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40}, + {0, 0x20, 0, 0x83, 0x08, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40}, + {0, 0x20, 0, 0x83, 0x08, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40}, + {0, 0xe6, 0, 0}, + {0, 0x44, 0, 0} + }; + u8 apdu_lens[10] = {13, 13, 13, 13, 13, 13, 13, 13, 4, 4}; + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; + sc_apdu_t apdu; + u8 i, l, r; + + LOG_FUNC_CALLED(ctx); + + /* Check card version */ + if (card->type != SC_CARD_TYPE_OPENPGP_V2) { + sc_log(ctx, "Card is not OpenPGP v2"); + LOG_FUNC_RETURN(ctx, SC_ERROR_NO_CARD_SUPPORT); + } + sc_log(ctx, "Card is OpenPGP v2. Erase card."); + + /* Iterate over 10 commands above */ + for (i = 0; i < sizeof(apdu_lens); i++) { + /* Length of the binary array of the current command */ + l = apdu_lens[i]; + /* Print the command to console */ + printf("Sending %d: ", i); + for (r = 0; r < l; r++) + printf("%02X ", apdu_binaries[i][r]); + printf("\n"); + + /* Build APDU from binary array */ + r = sc_bytes2apdu(card->ctx, apdu_binaries[i], l, &apdu); + if (r) { + sc_log(ctx, "Failed to build APDU"); + LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); + } + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + + /* Send APDU to card */ + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, r, "Transmiting APDU failed"); + } + LOG_FUNC_RETURN(ctx, r); +} + /* ABI: card ctl: perform special card-specific operations */ static int pgp_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) { -#ifdef ENABLE_OPENSSL int r; -#endif /* ENABLE_OPENSSL */ LOG_FUNC_CALLED(card->ctx); @@ -2197,17 +2458,62 @@ static int pgp_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) LOG_FUNC_RETURN(card->ctx, r); break; #endif /* ENABLE_OPENSSL */ + case SC_CARDCTL_ERASE_CARD: + r = pgp_erase_card(card); + LOG_FUNC_RETURN(card->ctx, r); + break; } LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } + +/* Internal: Delete key */ +static int +gnuk_delete_key(sc_card_t *card, u8 key_id) +{ + sc_context_t *ctx = card->ctx; + int r = SC_SUCCESS; + char *data = NULL; + + LOG_FUNC_CALLED(ctx); + + if (key_id < 1 || key_id > 3) { + sc_log(ctx, "Key ID %d is invalid. Should be 1, 2 or 3.", key_id); + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + } + + /* Delete fingerprint */ + sc_log(ctx, "Delete fingerprints"); + r = pgp_put_data(card, 0xC6 + key_id, NULL, 0); + LOG_TEST_RET(ctx, r, "Failed to delete fingerprints"); + /* Delete creation time */ + sc_log(ctx, "Delete creation time"); + r = pgp_put_data(card, 0xCD + key_id, NULL, 0); + LOG_TEST_RET(ctx, r, "Failed to delete creation time"); + + /* Rewrite Extended Header List */ + sc_log(ctx, "Rewrite Extended Header List"); + + if (key_id == 1) + data = "\x4D\x02\xB6"; + else if (key_id == 2) + data = "\x4D\x02\xB8"; + else if (key_id == 3) + data = "\x4D\x02\xA4"; + + r = pgp_put_data(card, 0x4D, (const u8 *)data, strlen((const char *)data) + 1); + + LOG_FUNC_RETURN(ctx, r); +} + + /* ABI: DELETE FILE */ static int pgp_delete_file(sc_card_t *card, const sc_path_t *path) { struct pgp_priv_data *priv = DRVDATA(card); - struct blob *blob; + pgp_blob_t *blob; sc_file_t *file; int r; @@ -2224,10 +2530,20 @@ pgp_delete_file(sc_card_t *card, const sc_path_t *path) if (blob == priv->mf) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); - if (file->id == 0xB601 || file->id == 0xB801 || file->id == 0xA401) { + if (card->type != SC_CARD_TYPE_OPENPGP_GNUK && + (file->id == DO_SIGN_SYM || file->id == DO_ENCR_SYM || file->id == DO_AUTH_SYM)) { /* These tags are just symbolic. We don't really delete it. */ r = SC_SUCCESS; } + else if (card->type == SC_CARD_TYPE_OPENPGP_GNUK && file->id == DO_SIGN_SYM) { + r = gnuk_delete_key(card, 1); + } + else if (card->type == SC_CARD_TYPE_OPENPGP_GNUK && file->id == DO_ENCR_SYM) { + r = gnuk_delete_key(card, 2); + } + else if (card->type == SC_CARD_TYPE_OPENPGP_GNUK && file->id == DO_AUTH_SYM) { + r = gnuk_delete_key(card, 3); + } else { /* call pgp_put_data() with zero-sized NULL-buffer to zap the DO contents */ r = pgp_put_data(card, file->id, NULL, 0); @@ -2246,7 +2562,7 @@ pgp_update_binary(sc_card_t *card, unsigned int idx, const u8 *buf, size_t count, unsigned long flags) { struct pgp_priv_data *priv = DRVDATA(card); - struct blob *blob = priv->current; + pgp_blob_t *blob = priv->current; int r = SC_SUCCESS; LOG_FUNC_CALLED(card->ctx); diff --git a/src/libopensc/cards.h b/src/libopensc/cards.h index 93a39670..5c756af6 100644 --- a/src/libopensc/cards.h +++ b/src/libopensc/cards.h @@ -106,6 +106,7 @@ enum { SC_CARD_TYPE_OPENPGP_BASE = 9000, SC_CARD_TYPE_OPENPGP_V1, SC_CARD_TYPE_OPENPGP_V2, + SC_CARD_TYPE_OPENPGP_GNUK, /* jcop driver */ SC_CARD_TYPE_JCOP_BASE = 10000, diff --git a/src/libopensc/pkcs15-openpgp.c b/src/libopensc/pkcs15-openpgp.c index 044ed163..69442050 100644 --- a/src/libopensc/pkcs15-openpgp.c +++ b/src/libopensc/pkcs15-openpgp.c @@ -34,6 +34,7 @@ #include "log.h" int sc_pkcs15emu_openpgp_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); +static int sc_pkcs15emu_openpgp_add_data(sc_pkcs15_card_t *); #define PGP_USER_PIN_FLAGS (SC_PKCS15_PIN_FLAG_CASE_SENSITIVE \ @@ -43,6 +44,8 @@ int sc_pkcs15emu_openpgp_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); | SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED \ | SC_PKCS15_PIN_FLAG_SO_PIN) +#define PGP_NUM_PRIVDO 4 + typedef struct _pgp_pin_cfg { const char *label; int reference; @@ -153,7 +156,8 @@ sc_pkcs15emu_openpgp_init(sc_pkcs15_card_t *p15card) u8 c4data[10]; u8 c5data[70]; int r, i; - const pgp_pin_cfg_t *pin_cfg = (card->type == SC_CARD_TYPE_OPENPGP_V2) ? pin_cfg_v2 : pin_cfg_v1; + const pgp_pin_cfg_t *pin_cfg = (card->type == SC_CARD_TYPE_OPENPGP_V2 || card->type == SC_CARD_TYPE_OPENPGP_GNUK) + ? pin_cfg_v2 : pin_cfg_v1; sc_path_t path; sc_file_t *file; @@ -356,6 +360,9 @@ sc_pkcs15emu_openpgp_init(sc_pkcs15_card_t *p15card) goto failed; } + /* PKCS#15 DATA object from OpenPGP private DOs */ + r = sc_pkcs15emu_openpgp_add_data(p15card); + return 0; failed: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Failed to initialize OpenPGP emulation: %s\n", @@ -363,9 +370,57 @@ failed: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Failed to initialize OpenPGP e return r; } +static int +sc_pkcs15emu_openpgp_add_data(sc_pkcs15_card_t *p15card) +{ + sc_context_t *ctx = p15card->card->ctx; + int i, r; + + LOG_FUNC_CALLED(ctx); + /* There is 4 private DO from 0101 to 0104 */ + for (i = 1; i <= PGP_NUM_PRIVDO; i++) { + sc_pkcs15_data_info_t dat_info; + sc_pkcs15_object_t dat_obj; + char name[8]; + char path[9]; + u8 content[254]; + memset(&dat_info, 0, sizeof(dat_info)); + memset(&dat_obj, 0, sizeof(dat_obj)); + + sprintf(name, "PrivDO%d", i); + sprintf(path, "3F00010%d", i); + + /* Check if the DO can be read. + * We won't expose pkcs15 DATA object if DO is empty. + */ + r = read_file(p15card->card, path, content, sizeof(content)); + if (r <= 0 ) { + sc_log(ctx, "No data get from DO 010%d", i); + /* Skip */ + continue; + } + sc_format_path(path, &dat_info.path); + strlcpy(dat_obj.label, name, sizeof(dat_obj.label)); + strlcpy(dat_info.app_label, name, sizeof(dat_info.app_label)); + + /* Add DATA object to slot protected by PIN2 (PW1 with Ref 0x82) */ + dat_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE | SC_PKCS15_CO_FLAG_MODIFIABLE; + dat_obj.auth_id.len = 1; + if (i == 1 || i == 3) + dat_obj.auth_id.value[0] = 2; + else + dat_obj.auth_id.value[0] = 3; + + sc_log(ctx, "Add %s data object", name); + r = sc_pkcs15emu_add_data_object(p15card, &dat_obj, &dat_info); + } + LOG_FUNC_RETURN(ctx, r); +} + static int openpgp_detect_card(sc_pkcs15_card_t *p15card) { - if (p15card->card->type == SC_CARD_TYPE_OPENPGP_V1 || p15card->card->type == SC_CARD_TYPE_OPENPGP_V2) + if (p15card->card->type == SC_CARD_TYPE_OPENPGP_V1 || p15card->card->type == SC_CARD_TYPE_OPENPGP_V2 + || p15card->card->type == SC_CARD_TYPE_OPENPGP_GNUK) return SC_SUCCESS; else return SC_ERROR_WRONG_CARD; diff --git a/src/libopensc/pkcs15-syn.c b/src/libopensc/pkcs15-syn.c index fea39ac8..b886366f 100644 --- a/src/libopensc/pkcs15-syn.c +++ b/src/libopensc/pkcs15-syn.c @@ -117,6 +117,7 @@ int sc_pkcs15_is_emulation_only(sc_card_t *card) case SC_CARD_TYPE_GEMSAFEV1_PTEID: case SC_CARD_TYPE_OPENPGP_V1: case SC_CARD_TYPE_OPENPGP_V2: + case SC_CARD_TYPE_OPENPGP_GNUK: case SC_CARD_TYPE_SC_HSM: case SC_CARD_TYPE_DNIE_BASE: case SC_CARD_TYPE_DNIE_BLANK: diff --git a/src/pkcs15init/pkcs15-openpgp.c b/src/pkcs15init/pkcs15-openpgp.c index 4da415fe..f5d6089f 100755 --- a/src/pkcs15init/pkcs15-openpgp.c +++ b/src/pkcs15init/pkcs15-openpgp.c @@ -236,13 +236,16 @@ static int openpgp_emu_update_tokeninfo(sc_profile_t *profile, sc_pkcs15_card_t } static int openpgp_store_data(struct sc_pkcs15_card *p15card, struct sc_profile *profile, - struct sc_pkcs15_object *obj, struct sc_pkcs15_der *content, + struct sc_pkcs15_object *obj, struct sc_pkcs15_der *content, struct sc_path *path) { sc_card_t *card = p15card->card; + sc_context_t *ctx = card->ctx; sc_file_t *file; sc_pkcs15_cert_info_t *cinfo; sc_pkcs15_id_t *cid; + sc_pkcs15_data_info_t *dinfo; + u8 buf[254]; int r; LOG_FUNC_CALLED(card->ctx); @@ -276,10 +279,42 @@ static int openpgp_store_data(struct sc_pkcs15_card *p15card, struct sc_profile r = sc_select_file(card, path, &file); LOG_TEST_RET(card->ctx, r, "Cannot select cert file"); r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); + sc_log(card->ctx, "Data to write is %d long", content->len); if (r >= 0 && content->len) + r = sc_put_data(p15card->card, 0x7F21, (const unsigned char *) content->value, content->len); + break; + + case SC_PKCS15_TYPE_DATA_OBJECT: + dinfo = (sc_pkcs15_data_info_t *) obj->data; + /* dinfo->app_label contains filename */ + sc_log(ctx, "===== App label %s", dinfo->app_label); + /* Currently, we only support DO 0101. The reason is that when initializing this + * pkcs15 emulation, PIN authentication is not applied and we can expose only this DO, + * which is "read always". + * If we support other DOs, they will not be exposed, and not helpful to user. + * I haven't found a way to refresh the list of exposed DOs after verifying PIN yet. + * http://sourceforge.net/mailarchive/message.php?msg_id=30646373 + **/ + sc_log(ctx, "About to write to DO 0101"); + sc_format_path("0101", path); + r = sc_select_file(card, path, &file); + LOG_TEST_RET(card->ctx, r, "Cannot select private DO"); + r = sc_read_binary(card, 0, buf, sizeof(buf), 0); + if (r < 0) { + sc_log(ctx, "Cannot read DO 0101"); + break; + } + if (r > 0) { + sc_log(ctx, "DO 0101 is full."); + r = SC_ERROR_TOO_MANY_OBJECTS; + break; + } + r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); + if (r >= 0 && content->len) { r = sc_update_binary(p15card->card, 0, (const unsigned char *) content->value, content->len, 0); + } break; default: diff --git a/src/tools/openpgp-tool.c b/src/tools/openpgp-tool.c index 00ca0715..7acf68d9 100644 --- a/src/tools/openpgp-tool.c +++ b/src/tools/openpgp-tool.c @@ -21,8 +21,17 @@ #include "config.h" #include +/* For dup() and dup2() functions */ #ifndef _WIN32 #include +#else +/* + * Windows: + * https://msdn.microsoft.com/en-us/library/8syseb29.aspx + * https://msdn.microsoft.com/en-us/library/886kc0as.aspx + */ +#include +#include #endif #include #include @@ -32,14 +41,16 @@ #include "libopensc/asn1.h" #include "libopensc/cards.h" #include "libopensc/cardctl.h" +#include "libopensc/log.h" #include "libopensc/errors.h" #include "util.h" #include "libopensc/log.h" -#define OPT_RAW 256 -#define OPT_PRETTY 257 -#define OPT_VERIFY 258 -#define OPT_PIN 259 +#define OPT_RAW 256 +#define OPT_PRETTY 257 +#define OPT_VERIFY 258 +#define OPT_PIN 259 +#define OPT_DELKEY 260 /* define structures */ struct ef_name_map { @@ -75,6 +86,8 @@ static int opt_verify = 0; static char *verifytype = NULL; static int opt_pin = 0; static const char *pin = NULL; +static int opt_erase = 0; +static int opt_delkey = 0; static int opt_dump_do = 0; static u8 do_dump_idx; @@ -93,8 +106,10 @@ static const struct option options[] = { { "help", no_argument, NULL, 'h' }, { "verbose", no_argument, NULL, 'v' }, { "version", no_argument, NULL, 'V' }, + { "erase", no_argument, NULL, 'E' }, { "verify", required_argument, NULL, OPT_VERIFY }, { "pin", required_argument, NULL, OPT_PIN }, + { "del-key", required_argument, NULL, OPT_DELKEY }, { "do", required_argument, NULL, 'd' }, { NULL, 0, NULL, 0 } }; @@ -112,8 +127,10 @@ static const char *option_help[] = { /* h */ "Print this help message", /* v */ "Verbose operation. Use several times to enable debug output.", /* V */ "Show version number", +/* E */ "Erase (reset) the card", "Verify PIN (CHV1, CHV2, CHV3...)", "PIN string", + "Delete key (1, 2, 3 or all)" /* d */ "Dump private data object number (i.e. PRIVATE-DO-)" }; @@ -168,16 +185,16 @@ static char *prettify_language(char *str) { if (str != NULL) { switch (strlen(str)) { - case 8: memmove(str+7, str+6, 1+strlen(str+6)); + case 8: memmove(str+7, str+6, 1+strlen(str+6)); str[6] = ','; /* fall through */ - case 6: memmove(str+5, str+4, 1+strlen(str+4)); + case 6: memmove(str+5, str+4, 1+strlen(str+4)); str[4] = ','; /* fall through */ - case 4: memmove(str+3, str+2, 1+strlen(str+2)); + case 4: memmove(str+3, str+2, 1+strlen(str+2)); str[2] = ','; /* fall through */ - case 2: return str; + case 2: return str; } } return NULL; @@ -189,10 +206,10 @@ static char *prettify_gender(char *str) { if (str != NULL) { switch (*str) { - case '0': return "unknown"; - case '1': return "male"; - case '2': return "female"; - case '9': return "not applicable"; + case '0': return "unknown"; + case '1': return "male"; + case '2': return "female"; + case '9': return "not applicable"; } } return NULL; @@ -289,6 +306,15 @@ static int decode_options(int argc, char **argv) show_version(); exit(EXIT_SUCCESS); break; + case 'E': + opt_erase++; + break; + case OPT_DELKEY: + opt_delkey++; + if (strcmp(optarg, "all") != 0) /* Arg string is not 'all' */ + key_id = optarg[0] - '0'; + else /* Arg string is 'all' */ + key_id = 'a'; case 'd': do_dump_idx = optarg[0] - '0'; opt_dump_do++; @@ -312,7 +338,7 @@ static int do_userinfo(sc_card_t *card) for (i = 0; openpgp_data[i].ef != NULL; i++) { sc_path_t path; sc_file_t *file; - int count; + unsigned int count; int r; sc_format_path(openpgp_data[i].ef, &path); @@ -322,24 +348,24 @@ static int do_userinfo(sc_card_t *card) return EXIT_FAILURE; } - count = file->size; + count = (unsigned int)file->size; if (!count) continue; - if (count > (int)sizeof(buf) - 1) { + if (count > (unsigned int)sizeof(buf) - 1) { fprintf(stderr, "Too small buffer to read the OpenPGP data\n"); return EXIT_FAILURE; } - - r = sc_read_binary(card, 0, buf, count, 0); - if (r < 0) { + + r = sc_read_binary(card, 0, buf, (size_t)count, 0); + if (r < 0) { fprintf(stderr, "%s: read failed - %s\n", openpgp_data[i].ef, sc_strerror(r)); return EXIT_FAILURE; - } - if (r != count) { - fprintf(stderr, "%s: expecting %d, got only %d bytes\n", openpgp_data[i].ef, count, r); + } + if (r != count) { + fprintf(stderr, "%s: expecting %d, got only %d bytes\n", openpgp_data[i].ef, count, r); return EXIT_FAILURE; - } + } buf[count] = '\0'; @@ -369,14 +395,22 @@ static int do_dump_do(sc_card_t *card, unsigned int tag) if(opt_raw) { r = 0; + #ifndef _WIN32 tmp = dup(fileno(stdout)); + #else + tmp = _dup(_fileno(stdout)); + #endif if (tmp < 0) return EXIT_FAILURE; fp = freopen(NULL, "wb", stdout); - if(fp) { - r = fwrite(buffer, sizeof(char), sizeof(buffer), fp); + if (fp) { + r = (int)fwrite(buffer, sizeof(char), sizeof(buffer), fp); } + #ifndef _WIN32 dup2(tmp, fileno(stdout)); + #else + _dup2(tmp, _fileno(stdout)); + #endif clearerr(stdout); close(tmp); if (sizeof(buffer) != r) @@ -444,11 +478,103 @@ int do_verify(sc_card_t *card, char *type, const char *pin) data.pin_type = SC_AC_CHV; data.pin_reference = type[3] - '0'; data.pin1.data = (unsigned char *) pin; - data.pin1.len = strlen(pin); + data.pin1.len = (int)strlen(pin); r = sc_pin_cmd(card, &data, &tries_left); return r; } +/** + * Delete key, for OpenPGP card. + * This function is not complete and is reserved for future version (> 2) of OpenPGP card. + **/ +int delete_key_openpgp(sc_card_t *card, u8 key_id) +{ + char *del_fingerprint = "00:DA:00:C6:14:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00"; + char *del_creationtime = "00:DA:00:CD:04:00:00:00:00"; + /* We need to replace the 4th byte later */ + char *apdustring = NULL; + u8 buf[SC_MAX_APDU_BUFFER_SIZE]; + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; + sc_apdu_t apdu; + size_t len0; + int i; + int r = SC_SUCCESS; + + for (i = 0; i < 2; i++) { + if (i == 0) /* Reset fingerprint */ + apdustring = del_fingerprint; + else /* Reset creation time */ + apdustring = del_creationtime; + /* Convert the string to binary array */ + len0 = sizeof(buf); + sc_hex_to_bin(apdustring, buf, &len0); + + /* Replace DO tag, subject to key ID */ + buf[3] = buf[3] + key_id; + + /* Build APDU from binary array */ + r = sc_bytes2apdu(card->ctx, buf, len0, &apdu); + if (r) { + fprintf(stderr, "Failed to build APDU: %s\n", sc_strerror(r)); + return r; + } + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + + /* Send APDU to card */ + r = sc_transmit_apdu(card, &apdu); + if (r) { + fprintf(stderr, "Transmiting APDU failed: %s\n", sc_strerror(r)); + return r; + } + } + /* TODO: Rewrite Extended Header List. + * Not support by OpenGPG v2 yet */ + return r; +} + +int do_delete_key(sc_card_t *card, u8 key_id) +{ + sc_path_t path; + int r = SC_SUCCESS; + + /* Currently, only Gnuk supports deleting keys */ + if (card->type != SC_CARD_TYPE_OPENPGP_GNUK) { + printf("Only Gnuk supports deleting keys. General OpenPGP doesn't."); + return SC_ERROR_NOT_SUPPORTED; + } + + if (key_id < 1 || (key_id > 3 && key_id != 'a')) { + printf("Error: Invalid key id %d", key_id); + return SC_ERROR_INVALID_ARGUMENTS; + } + if (key_id == 1 || key_id == 'a') { + sc_format_path("B601", &path); + r |= sc_delete_file(card, &path); + } + if (key_id == 2 || key_id == 'a') { + sc_format_path("B801", &path); + r |= sc_delete_file(card, &path); + } + if (key_id == 3 || key_id == 'a') { + sc_format_path("A401", &path); + r |= sc_delete_file(card, &path); + } + return r; +} + +int do_erase(sc_card_t *card) +{ + int r; + /* Check card version */ + if (card->type != SC_CARD_TYPE_OPENPGP_V2) { + printf("Do not erase card which is not OpenPGP v2\n"); + } + printf("Erase card\n"); + r = sc_card_ctl(card, SC_CARDCTL_ERASE_CARD, NULL); + return r; +} + int main(int argc, char **argv) { sc_context_t *ctx = NULL; @@ -487,8 +613,10 @@ int main(int argc, char **argv) /* check card type */ if ((card->type != SC_CARD_TYPE_OPENPGP_V1) && - (card->type != SC_CARD_TYPE_OPENPGP_V2)) { + (card->type != SC_CARD_TYPE_OPENPGP_V2) && + (card->type != SC_CARD_TYPE_OPENPGP_GNUK)) { util_error("not an OpenPGP card"); + fprintf(stderr, "Card type %X\n", card->type); exit_status = EXIT_FAILURE; goto out; } @@ -520,12 +648,22 @@ int main(int argc, char **argv) sc_unlock(card); sc_disconnect_card(card); sc_release_context(ctx); + #ifndef _WIN32 execv(exec_program, largv); + #else + _execv(exec_program, largv); + #endif /* we should not get here */ perror("execv()"); exit(EXIT_FAILURE); } + if (opt_delkey) + exit_status |= do_delete_key(card, key_id); + + if (opt_erase) + exit_status |= do_erase(card); + out: sc_unlock(card); sc_disconnect_card(card);