diff --git a/src/libopensc/asn1.c b/src/libopensc/asn1.c index dccd43d6..eace72d8 100644 --- a/src/libopensc/asn1.c +++ b/src/libopensc/asn1.c @@ -116,6 +116,24 @@ void sc_copy_asn1_entry(const struct sc_asn1_entry *src, dest->name = NULL; } +size_t sc_count_bit_string_size(const void * buf, size_t bufsize) +{ + const u8 *p = (const u8 *) buf + bufsize - 1; + u8 c; + size_t skip = 0; + int i; + + while (p >= (const u8 *) buf && *p == 0) { + skip += 8; + p--; + } + if (p < (const u8 *) buf) + return 0; + c = *p; + for (i = 0; (c >> (7-i)) == 0; i++); + return bufsize * 8 - (skip + i); +} + static void sc_asn1_print_octet_string(const u8 * buf, size_t buflen) { int i; @@ -403,10 +421,41 @@ int sc_asn1_decode_bit_string_ni(const u8 * inbuf, size_t inlen, return decode_bit_string(inbuf, inlen, outbuf, outlen, 0); } -static int encode_bit_string(const u8 * inbuf, size_t inlen, u8 **outbuf, +static int encode_bit_string(const u8 * inbuf, size_t bits_left, u8 **outbuf, size_t *outlen, int invert) { - return SC_ERROR_NOT_SUPPORTED; + const u8 *in = inbuf; + u8 *out; + size_t bytes; + int skipped = 0; + + bytes = (bits_left + 7)/8 + 1; + *outbuf = out = malloc(bytes); + if (out == NULL) + return SC_ERROR_OUT_OF_MEMORY; + *outlen = bytes; + out += 1; + while (bits_left) { + int i, bits_to_go = 8; + + *out = 0; + if (bits_left < 8) { + bits_to_go = bits_left; + skipped = 8 - bits_left; + } + if (invert) { + for (i = 0; i < bits_to_go; i++) + *out |= ((*in >> i) & 1) << (7 - i); + } else { + *out = *in; + if (bits_left < 8) + return SC_ERROR_NOT_SUPPORTED; /* FIXME */ + } + bits_left -= bits_to_go; + } + out = *outbuf; + out[0] = skipped; + return 0; } int sc_asn1_decode_integer(const u8 * inbuf, size_t inlen, int *out) @@ -425,18 +474,25 @@ int sc_asn1_decode_integer(const u8 * inbuf, size_t inlen, int *out) static int asn1_encode_integer(int in, u8 ** obj, size_t * objsize) { - int i = sizeof(in) * 8; - u8 *p; + int i = sizeof(in) * 8, skip = 1; + u8 *p, b; *obj = p = malloc(sizeof(in)); if (*obj == NULL) return SC_ERROR_OUT_OF_MEMORY; - *objsize = sizeof(in); do { i -= 8; - *p++ = (in >> i) & 0xFF; + b = in >> i; + if (b == 0 && skip) + continue; + skip = 0; + *p++ = b; } while (i > 0); - + *objsize = p - *obj; + if (*objsize == 0) { + *objsize = 1; + (*obj)[0] = 0; + } return 0; } @@ -679,15 +735,17 @@ static int asn1_encode_p15_object(struct sc_context *ctx, const struct sc_pkcs15 int r; const struct sc_pkcs15_common_obj_attr *com_attr = obj->com_attr; struct sc_asn1_entry asn1_c_attr[6], asn1_p15_obj[5]; - size_t flags_len = sizeof(com_attr->flags); + size_t flags_len; size_t label_len = strlen(com_attr->label); sc_copy_asn1_entry(c_asn1_com_obj_attr, asn1_c_attr); sc_copy_asn1_entry(c_asn1_p15_obj, asn1_p15_obj); if (label_len != 0) sc_format_asn1_entry(asn1_c_attr + 0, (void *) com_attr->label, &label_len, 1); - if (com_attr->flags) + if (com_attr->flags) { + flags_len = sc_count_bit_string_size(&com_attr->flags, sizeof(com_attr->flags)); sc_format_asn1_entry(asn1_c_attr + 1, (void *) &com_attr->flags, &flags_len, 1); + } if (com_attr->auth_id.len) sc_format_asn1_entry(asn1_c_attr + 2, (void *) &com_attr->auth_id, NULL, 1); if (com_attr->user_consent) @@ -964,7 +1022,7 @@ static int asn1_encode_entry(struct sc_context *ctx, const struct sc_asn1_entry case SC_ASN1_BIT_STRING_NI: case SC_ASN1_BIT_STRING: assert(len != NULL); - if (entry->type == SC_ASN1_BIT_STRING_NI) + if (entry->type == SC_ASN1_BIT_STRING) r = encode_bit_string((const u8 *) parm, *len, &buf, &buflen, 1); else r = encode_bit_string((const u8 *) parm, *len, &buf, &buflen, 0); @@ -980,8 +1038,8 @@ static int asn1_encode_entry(struct sc_context *ctx, const struct sc_asn1_entry buflen = *len; memcpy(buf, parm, buflen); break; -#if 0 - case SC_ASN1_OBJECT: +#if 0 + case SC_ASN1_OBJECT: if (parm != NULL) r = sc_asn1_decode_object_id(obj, objlen, (struct sc_object_id *) parm); break; @@ -1028,7 +1086,6 @@ static int asn1_encode_entry(struct sc_context *ctx, const struct sc_asn1_entry return 0; } - static int asn1_encode(struct sc_context *ctx, const struct sc_asn1_entry *asn1, u8 **ptr, size_t *size, int depth) { diff --git a/src/libopensc/card-flex.c b/src/libopensc/card-flex.c index 7016324d..b08a49bf 100644 --- a/src/libopensc/card-flex.c +++ b/src/libopensc/card-flex.c @@ -22,7 +22,7 @@ #include "sc-log.h" static const char *flex_atrs[] = { - "3B:95:94:40:FF:63:01:01:02:01", /* CryptoFlex 16k */ + "3B:95:94:40:FF:63:01:01:02:01", /* Cryptoflex 16k */ "3B:19:14:55:90:01:02:02:00:05:04:B0", NULL }; @@ -30,7 +30,7 @@ static const char *flex_atrs[] = { static struct sc_card_operations flex_ops; static const struct sc_card_driver flex_drv = { NULL, - "Schlumberger Multiflex/CryptoFlex", + "Schlumberger Multiflex/Cryptoflex", "slb", &flex_ops }; @@ -240,7 +240,7 @@ static int flex_select_file(struct sc_card *card, const struct sc_path *path, return SC_ERROR_UNKNOWN_REPLY; if (apdu.resp[0] == 0x6F) { - error(card->ctx, "unsupported: Multiflex returned FCI\n"); + error(card->ctx, "unsupported: card returned FCI\n"); return SC_ERROR_UNKNOWN_REPLY; /* FIXME */ } @@ -284,6 +284,146 @@ static int flex_list_files(struct sc_card *card, u8 *buf, size_t buflen) return count; } +static int flex_delete_file(struct sc_card *card, const struct sc_path *path) +{ + int r; + u8 sbuf[2]; + struct sc_apdu apdu; + + SC_FUNC_CALLED(card->ctx, 1); + if (path->type != SC_PATH_TYPE_FILE_ID && path->len != 2) { + error(card->ctx, "File type has to be SC_PATH_TYPE_FILE_ID\n"); + SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS); + } + sbuf[0] = path->value[0]; + sbuf[1] = path->value[1]; + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE4, 0x00, 0x00); + apdu.cla = 0xF0; /* Override CLA byte */ + apdu.lc = 2; + apdu.datalen = 2; + apdu.data = sbuf; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + return sc_sw_to_errorcode(card, apdu.sw1, apdu.sw2); +} + +static int acl_to_ac(unsigned int acl) +{ + int i; + unsigned int acl_table[16] = { + /* 0 */ SC_AC_NONE, SC_AC_CHV1, SC_AC_CHV2, SC_AC_PRO, + /* 4 */ SC_AC_AUT, SC_AC_UNKNOWN, SC_AC_CHV1 | SC_AC_PRO, + /* 7 */ SC_AC_CHV2 | SC_AC_PRO, SC_AC_CHV1 | SC_AC_AUT, + /* 9 */ SC_AC_CHV2 | SC_AC_AUT, SC_AC_UNKNOWN, SC_AC_UNKNOWN, + /* c */ SC_AC_UNKNOWN, SC_AC_UNKNOWN, SC_AC_UNKNOWN, + /* f */ SC_AC_NEVER }; + + for (i = 0; i < sizeof(acl_table)/sizeof(acl_table[0]); i++) + if (acl == acl_table[i]) + return i; + return -1; +} + +static int encode_file_structure(struct sc_card *card, const struct sc_file *file, + u8 *buf, size_t *buflen) +{ + u8 *p = buf; + int r, r2; + + p[0] = 0xFF; + p[1] = 0xFF; + p[2] = file->size >> 8; + p[3] = file->size & 0xFF; + p[4] = file->id >> 8; + p[5] = file->id & 0xFF; + if (file->type == SC_FILE_TYPE_DF) + p[6] = 0x38; + else + switch (file->ef_structure) { + case SC_FILE_EF_TRANSPARENT: + p[6] = 0x01; + break; + case SC_FILE_EF_LINEAR_FIXED: + p[6] = 0x02; + break; + case SC_FILE_EF_LINEAR_VARIABLE: + p[6] = 0x04; + break; + case SC_FILE_EF_CYCLIC: + p[6] = 0x06; + break; + default: + return -1; + } + p[7] = 0xFF; /* allow Decrease and Increase */ + if (file->type == SC_FILE_TYPE_DF) { + r = acl_to_ac(file->acl[SC_AC_OP_LIST_FILES]); + SC_TEST_RET(card->ctx, r, "Invalid ACL value"); + p[8] = (r & 0x0F) << 8; + r = acl_to_ac(file->acl[SC_AC_OP_DELETE]); + SC_TEST_RET(card->ctx, r, "Invalid ACL value"); + r2 = acl_to_ac(file->acl[SC_AC_OP_CREATE]); + SC_TEST_RET(card->ctx, r2, "Invalid ACL value"); + p[9] = ((r & 0x0F) << 8) | (r2 & 0x0F); + p[10] = 0; + } else { + r = acl_to_ac(file->acl[SC_AC_OP_READ]); + SC_TEST_RET(card->ctx, r, "Invalid ACL value"); + r2 = acl_to_ac(file->acl[SC_AC_OP_UPDATE]); + SC_TEST_RET(card->ctx, r2, "Invalid ACL value"); + p[8] = ((r & 0x0F) << 8) | (r2 & 0x0F); + p[9] = 0; /* FIXME */ + r = acl_to_ac(file->acl[SC_AC_OP_INVALIDATE]); + SC_TEST_RET(card->ctx, r, "Invalid ACL value"); + r2 = acl_to_ac(file->acl[SC_AC_OP_REHABILITATE]); + SC_TEST_RET(card->ctx, r2, "Invalid ACL value"); + p[10] = ((r & 0x0F) << 8) | (r2 & 0x0F); + } + p[11] = (file->status & SC_FILE_STATUS_INVALIDATED) ? 0x00 : 0x01; + if (file->type != SC_FILE_TYPE_DF && + (file->ef_structure == SC_FILE_EF_LINEAR_FIXED || + file->ef_structure == SC_FILE_EF_CYCLIC)) + p[12] = 0x04; + else + p[12] = 0x03; + p[13] = p[14] = p[15] = 0; /* FIXME */ + if (p[12] == 0x04) { + p[16] = file->record_length; + *buflen = 17; + } else + *buflen = 16; + + return 0; +} + +static int flex_create_file(struct sc_card *card, const struct sc_file *file) +{ + u8 sbuf[18]; + size_t sendlen; + int r, rec_nr; + struct sc_apdu apdu; + + r = encode_file_structure(card, file, sbuf, &sendlen); + if (r) { + error(card->ctx, "File structure encoding failed.\n"); + return SC_ERROR_INVALID_ARGUMENTS; + } + if (file->type != SC_FILE_TYPE_DF && file->ef_structure != SC_FILE_EF_TRANSPARENT) + rec_nr = file->record_count; + else + rec_nr = 0; + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, rec_nr); + apdu.cla = 0xF0; + apdu.data = sbuf; + apdu.datalen = sendlen; + apdu.lc = sendlen; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + return sc_sw_to_errorcode(card, apdu.sw1, apdu.sw2); +} + static const struct sc_card_driver * sc_get_driver(void) { const struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); @@ -294,6 +434,8 @@ static const struct sc_card_driver * sc_get_driver(void) flex_ops.finish = flex_finish; flex_ops.select_file = flex_select_file; flex_ops.list_files = flex_list_files; + flex_ops.delete_file = flex_delete_file; + flex_ops.create_file = flex_create_file; return &flex_drv; } diff --git a/src/libopensc/card.c b/src/libopensc/card.c index e7c9ba72..a53de78d 100644 --- a/src/libopensc/card.c +++ b/src/libopensc/card.c @@ -620,6 +620,45 @@ int sc_write_binary(struct sc_card *card, unsigned int idx, SC_FUNC_RETURN(card->ctx, 2, r); } +int sc_update_binary(struct sc_card *card, unsigned int idx, + const u8 *buf, size_t count, unsigned long flags) +{ + int r; + + assert(card != NULL && card->ops != NULL && buf != NULL); + if (card->ctx->debug >= 2) + debug(card->ctx, "sc_update_binary: %d bytes at index %d\n", count, idx); + if (card->ops->update_binary == NULL) + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_NOT_SUPPORTED); + if (count > SC_APDU_CHOP_SIZE && !(card->caps & SC_CARD_CAP_APDU_EXT)) { + int bytes_written = 0; + const u8 *p = buf; + + r = sc_lock(card); + SC_TEST_RET(card->ctx, r, "sc_lock() failed"); + while (count > 0) { + int n = count > SC_APDU_CHOP_SIZE ? SC_APDU_CHOP_SIZE : count; + r = sc_update_binary(card, idx, p, n, flags); + if (r < 0) { + sc_unlock(card); + SC_TEST_RET(card->ctx, r, "sc_read_binary() failed"); + } + p += r; + idx += r; + bytes_written += r; + count -= r; + if (r == 0) { + sc_unlock(card); + SC_FUNC_RETURN(card->ctx, 2, bytes_written); + } + } + sc_unlock(card); + SC_FUNC_RETURN(card->ctx, 2, bytes_written); + } + r = card->ops->update_binary(card, idx, buf, count, flags); + SC_FUNC_RETURN(card->ctx, 2, r); +} + int sc_select_file(struct sc_card *card, const struct sc_path *in_path, struct sc_file *file) diff --git a/src/libopensc/internal.h b/src/libopensc/internal.h index 9883e870..440c597b 100644 --- a/src/libopensc/internal.h +++ b/src/libopensc/internal.h @@ -32,6 +32,7 @@ /* Internal use only */ int sc_sw_to_errorcode(struct sc_card *card, int sw1, int sw2); +size_t sc_count_bit_string_size(const void * buf, size_t bufsize); /* Default timeout value for SCardGetStatusChange */ #ifndef SC_CUSTOM_STATUS_TIMEOUT diff --git a/src/libopensc/iso7816.c b/src/libopensc/iso7816.c index 6faeb2f3..2dbb2802 100644 --- a/src/libopensc/iso7816.c +++ b/src/libopensc/iso7816.c @@ -98,6 +98,30 @@ static int iso7816_write_binary(struct sc_card *card, SC_FUNC_RETURN(card->ctx, 3, count); } +static int iso7816_update_binary(struct sc_card *card, + unsigned int idx, const u8 *buf, + size_t count, unsigned long flags) +{ + struct sc_apdu apdu; + int r; + + if (count > SC_APDU_CHOP_SIZE) { + error(card->ctx, "Too large buffer supplied\n"); + return SC_ERROR_CMD_TOO_LONG; + } + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xD6, + (idx >> 8) & 0x7F, idx & 0xFF); + apdu.lc = count; + apdu.datalen = count; + apdu.data = buf; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + SC_TEST_RET(card->ctx, sc_sw_to_errorcode(card, apdu.sw1, apdu.sw2), + "Card returned error"); + SC_FUNC_RETURN(card->ctx, 3, count); +} + static unsigned int byte_to_acl(u8 byte) { switch (byte >> 4) { @@ -538,6 +562,7 @@ const struct sc_card_driver * sc_get_iso7816_driver(void) iso_ops.read_binary = iso7816_read_binary; iso_ops.read_record = iso7816_read_record; iso_ops.write_binary = iso7816_write_binary; + iso_ops.update_binary = iso7816_update_binary; iso_ops.select_file = iso7816_select_file; iso_ops.get_challenge = iso7816_get_challenge; iso_ops.create_file = iso7816_create_file; diff --git a/src/libopensc/opensc-pkcs15.h b/src/libopensc/opensc-pkcs15.h index bea3c9db..d12b3c30 100644 --- a/src/libopensc/opensc-pkcs15.h +++ b/src/libopensc/opensc-pkcs15.h @@ -34,10 +34,7 @@ extern "C" { #define SC_PKCS15_MAX_PRKEYS 2 #define SC_PKCS15_MAX_LABEL_SIZE 32 #define SC_PKCS15_MAX_ID_SIZE 16 -#define SC_PKCS15_MAX_CDFS 4 /* Certificate Directory - * Files */ -#define SC_PKCS15_MAX_AODFS 4 /* Authentication Object - * Directory Files */ +#define SC_PKCS15_MAX_DFS 4 #define SC_PKCS15_MAX_CERTS 4 /* Total certificates */ struct sc_pkcs15_id { @@ -45,6 +42,8 @@ struct sc_pkcs15_id { size_t len; }; +#define SC_PKCS15_CO_FLAG_OBJECT_SEEN 0x80000000 /* for PKCS #11 module */ + struct sc_pkcs15_common_obj_attr { char label[SC_PKCS15_MAX_LABEL_SIZE]; /* zero terminated */ int flags; @@ -137,14 +136,33 @@ struct sc_pkcs15_prkey_info { int modulus_length; }; +#define SC_PKCS15_PRKDF 0 +#define SC_PKCS15_PUKDF 1 +#define SC_PKCS15_PUKDF_TRUSTED 2 +#define SC_PKCS15_SKDF 3 +#define SC_PKCS15_CDF 4 +#define SC_PKCS15_CDF_TRUSTED 5 +#define SC_PKCS15_CDF_USEFUL 6 +#define SC_PKCS15_DODF 7 +#define SC_PKCS15_AODF 8 +#define SC_PKCS15_DF_TYPE_COUNT 9 + + +struct sc_pkcs15_df { + struct sc_file *file[SC_PKCS15_MAX_DFS]; + int count, record_length; +}; + struct sc_pkcs15_card { struct sc_card *card; char *label; /* fields from TokenInfo: */ int version; char *serial_number, *manufacturer_id; - int flags; + unsigned long flags; struct sc_pkcs15_algorithm_info alg_info[1]; + /* FIXME: this could be done better with some C pre-processor + * magic */ struct sc_pkcs15_cert_info cert_info[SC_PKCS15_MAX_CERTS]; int cert_count; struct sc_pkcs15_prkey_info prkey_info[SC_PKCS15_MAX_PRKEYS]; @@ -152,15 +170,10 @@ struct sc_pkcs15_card { struct sc_pkcs15_pin_info pin_info[SC_PKCS15_MAX_PINS]; int pin_count; + /* FIXME: Move file_dir somewhere else, perhaps to sc_card */ struct sc_file file_dir, file_app; - /* in app DF */ struct sc_file file_tokeninfo, file_odf; - struct sc_file file_prkdf; - struct sc_file file_cdf[SC_PKCS15_MAX_CDFS]; - int cdf_count; - struct sc_file file_aodf[SC_PKCS15_MAX_AODFS]; - int aodf_count; - struct sc_file file_dodf; + struct sc_pkcs15_df df[SC_PKCS15_DF_TYPE_COUNT]; int use_cache; }; diff --git a/src/libopensc/opensc.h b/src/libopensc/opensc.h index 53131c15..e2306c14 100644 --- a/src/libopensc/opensc.h +++ b/src/libopensc/opensc.h @@ -182,6 +182,9 @@ struct sc_file { int status; /* Status flags */ unsigned int acl[SC_MAX_AC_OPS]; /* Access Control List */ + int record_length; /* In case of fixed-length or cyclic EF */ + int record_count; /* Valid, if not transparent EF or DF */ + u8 sec_attr[SC_MAX_SEC_ATTR_SIZE]; size_t sec_attr_len; u8 prop_attr[SC_MAX_PROP_ATTR_SIZE]; @@ -458,6 +461,8 @@ int sc_read_binary(struct sc_card *card, unsigned int idx, u8 * buf, size_t count, unsigned long flags); int sc_write_binary(struct sc_card *card, unsigned int idx, const u8 * buf, size_t count, unsigned long flags); +int sc_update_binary(struct sc_card *card, unsigned int idx, const u8 * buf, + size_t count, unsigned long flags); int sc_read_record(struct sc_card *card, unsigned int rec_nr, u8 * buf, size_t count, unsigned long flags); int sc_get_challenge(struct sc_card *card, u8 * rndout, size_t len); diff --git a/src/libopensc/pkcs15-cert.c b/src/libopensc/pkcs15-cert.c index 407baef2..5d5a4493 100644 --- a/src/libopensc/pkcs15-cert.c +++ b/src/libopensc/pkcs15-cert.c @@ -389,7 +389,7 @@ int sc_pkcs15_create_cdf(struct sc_pkcs15_card *p15card, bufsize += tmpsize; } sc_hex_dump(p15card->card->ctx, buf, bufsize, str, sizeof(str)); - printf("\n%s\n", str); + printf("CDF:\n%s\n", str); return 0; } @@ -445,15 +445,26 @@ static int get_certs_from_file(struct sc_pkcs15_card *card, int sc_pkcs15_enum_certificates(struct sc_pkcs15_card *card) { - int r = 0, i; + int r = 0, i, j, type; + const int df_types[] = { + SC_PKCS15_CDF, SC_PKCS15_CDF_TRUSTED, SC_PKCS15_CDF_USEFUL + }; + const int nr_types = sizeof(df_types)/sizeof(df_types[0]); + assert(card != NULL); if (card->cert_count) return card->cert_count; /* already enumerated */ r = sc_lock(card->card); SC_TEST_RET(card->card->ctx, r, "sc_lock() failed"); - for (i = 0; i < card->cdf_count; i++) { - r = get_certs_from_file(card, &card->file_cdf[i]); + for (j = 0; j < nr_types; j++) { + type = df_types[j]; + + for (i = 0; i < card->df[type].count; i++) { + r = get_certs_from_file(card, card->df[type].file[i]); + if (r != 0) + break; + } if (r != 0) break; } diff --git a/src/libopensc/pkcs15-pin.c b/src/libopensc/pkcs15-pin.c index b3541d31..bc715cd6 100644 --- a/src/libopensc/pkcs15-pin.c +++ b/src/libopensc/pkcs15-pin.c @@ -1,5 +1,5 @@ /* - * pkcs15-pin.c: PKCS#15 PIN functions + * pkcs15-pin.c: PKCS #15 PIN functions * * Copyright (C) 2001 Juha Yrjölä * @@ -133,8 +133,12 @@ static int get_pins_from_file(struct sc_pkcs15_card *card, int sc_pkcs15_enum_pins(struct sc_pkcs15_card *p15card) { - int r, i; + int r, i, j; struct sc_context *ctx = p15card->card->ctx; + const int df_types[] = { + SC_PKCS15_AODF + }; + const int nr_types = sizeof(df_types)/sizeof(df_types[0]); assert(p15card != NULL); SC_FUNC_CALLED(ctx, 1); @@ -149,10 +153,11 @@ int sc_pkcs15_enum_pins(struct sc_pkcs15_card *p15card) p15card->pin_count = 0; r = sc_lock(p15card->card); SC_TEST_RET(p15card->card->ctx, r, "sc_lock() failed"); - for (i = 0; i < p15card->aodf_count; i++) { - r = get_pins_from_file(p15card, &p15card->file_aodf[i]); - if (r != 0) - break; + for (j = 0; r == 0 && j < nr_types; j++) { + int type = df_types[j]; + + for (i = 0; r == 0 && i < p15card->df[type].count; i++) + r = get_pins_from_file(p15card, p15card->df[type].file[i]); } sc_unlock(p15card->card); if (r != 0) diff --git a/src/libopensc/pkcs15-prkey.c b/src/libopensc/pkcs15-prkey.c index df896583..955c96e7 100644 --- a/src/libopensc/pkcs15-prkey.c +++ b/src/libopensc/pkcs15-prkey.c @@ -130,17 +130,24 @@ static int get_prkeys_from_file(struct sc_pkcs15_card *card, int sc_pkcs15_enum_private_keys(struct sc_pkcs15_card *card) { - int r, i; - assert(card != NULL); + int r, i, j; + struct sc_context *ctx = card->card->ctx; + const int df_types[] = { + SC_PKCS15_PRKDF + }; + const int nr_types = sizeof(df_types)/sizeof(df_types[0]); + assert(card != NULL); + SC_FUNC_CALLED(ctx, 1); if (card->prkey_count) return card->prkey_count; /* already enumerated */ r = sc_lock(card->card); SC_TEST_RET(card->card->ctx, r, "sc_lock() failed"); - for (i = 0; i < 1; i++) { - r = get_prkeys_from_file(card, &card->file_prkdf); - if (r != 0) - break; + for (j = 0; r == 0 && j < nr_types; j++) { + int type = df_types[j]; + + for (i = 0; r == 0 && i < card->df[type].count; i++) + r = get_prkeys_from_file(card, card->df[type].file[i]); } sc_unlock(card->card); if (r != 0) diff --git a/src/libopensc/pkcs15.c b/src/libopensc/pkcs15.c index 7f6b571d..258131b4 100644 --- a/src/libopensc/pkcs15.c +++ b/src/libopensc/pkcs15.c @@ -55,6 +55,23 @@ void sc_pkcs15_print_card(const struct sc_pkcs15_card *card) printf("\n"); } +static const struct sc_asn1_entry c_asn1_toki[] = { + { "version", SC_ASN1_INTEGER, ASN1_INTEGER, 0, NULL }, + { "serialNumber", SC_ASN1_OCTET_STRING, ASN1_OCTET_STRING, 0, NULL }, + { "manufacturerID", SC_ASN1_UTF8STRING, ASN1_UTF8STRING, SC_ASN1_OPTIONAL, NULL }, + { "label", SC_ASN1_UTF8STRING, SC_ASN1_CTX | 0, SC_ASN1_OPTIONAL, NULL }, + { "tokenflags", SC_ASN1_BIT_STRING, ASN1_BIT_STRING, 0, NULL }, + { "seInfo", SC_ASN1_SEQUENCE, SC_ASN1_CONS | ASN1_SEQUENCE, SC_ASN1_OPTIONAL, NULL }, + { "recordInfo", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_CTX | 1, SC_ASN1_OPTIONAL, NULL }, + { "supportedAlgorithms", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_CTX | 2, SC_ASN1_OPTIONAL, NULL }, + { NULL } +}; + +static const struct sc_asn1_entry c_asn1_tokeninfo[] = { + { "TokenInfo", SC_ASN1_STRUCT, SC_ASN1_CONS | ASN1_SEQUENCE, 0, NULL }, + { NULL } +}; + void parse_tokeninfo(struct sc_pkcs15_card *card, const u8 * buf, size_t buflen) { int i, r; @@ -62,25 +79,20 @@ void parse_tokeninfo(struct sc_pkcs15_card *card, const u8 * buf, size_t buflen) int serial_len = sizeof(serial); u8 mnfid[128]; int mnfid_len = sizeof(mnfid); + u8 label[128]; + int label_len = sizeof(label); int flags_len = sizeof(card->flags); + struct sc_asn1_entry asn1_toki[9], asn1_tokeninfo[2]; - struct sc_asn1_entry asn1_tokeninfo[] = { - { "version", SC_ASN1_INTEGER, ASN1_INTEGER, 0, &card->version }, - { "serialNumber", SC_ASN1_OCTET_STRING, ASN1_OCTET_STRING, 0, serial, &serial_len }, - { "manufacturerID", SC_ASN1_UTF8STRING, ASN1_UTF8STRING, SC_ASN1_OPTIONAL, mnfid, &mnfid_len }, - { "label", SC_ASN1_UTF8STRING, SC_ASN1_CTX | 0, SC_ASN1_OPTIONAL, NULL }, - { "tokenflags", SC_ASN1_BIT_STRING, ASN1_BIT_STRING, 0, &card->flags, &flags_len }, - { "seInfo", SC_ASN1_SEQUENCE, SC_ASN1_CONS | ASN1_SEQUENCE, SC_ASN1_OPTIONAL, NULL }, - { "recordInfo", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_CTX | 1, SC_ASN1_OPTIONAL, NULL }, - { "supportedAlgorithms", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_CTX | 2, SC_ASN1_OPTIONAL, NULL }, - { NULL } - }; - - buf = sc_asn1_verify_tag(card->card->ctx, buf, buflen, SC_ASN1_CONS | ASN1_SEQUENCE, &buflen); - if (buf == NULL) { - error(card->card->ctx, "invalid EF(TokenInfo)\n"); - goto err; - } + sc_copy_asn1_entry(c_asn1_toki, asn1_toki); + sc_copy_asn1_entry(c_asn1_tokeninfo, asn1_tokeninfo); + sc_format_asn1_entry(asn1_toki + 0, &card->version, NULL, 0); + sc_format_asn1_entry(asn1_toki + 1, serial, &serial_len, 0); + sc_format_asn1_entry(asn1_toki + 2, mnfid, &mnfid_len, 0); + sc_format_asn1_entry(asn1_toki + 3, label, &label_len, 0); + sc_format_asn1_entry(asn1_toki + 4, &card->flags, &flags_len, 0); + sc_format_asn1_entry(asn1_tokeninfo, asn1_toki, NULL, 0); + r = sc_asn1_decode(card->card->ctx, asn1_tokeninfo, buf, buflen, NULL, NULL); if (r) { error(card->card->ctx, "ASN.1 parsing failed: %s\n", sc_strerror(r)); @@ -101,6 +113,12 @@ void parse_tokeninfo(struct sc_pkcs15_card *card, const u8 * buf, size_t buflen) else card->manufacturer_id = strdup("(unknown)"); } + if (card->label == NULL) { + if (asn1_tokeninfo[2].flags & SC_ASN1_PRESENT) + card->manufacturer_id = strdup((char *) mnfid); + else + card->manufacturer_id = strdup("(unknown)"); + } return; err: if (card->serial_number == NULL) @@ -110,35 +128,115 @@ err: return; } +int encode_tokeninfo(struct sc_pkcs15_card *card, u8 ** buf, size_t *buflen) +{ + int i, r; + u8 serial[128]; + int serial_len = 0; + int mnfid_len; + int label_len; + int flags_len; + int version = card->version; + + struct sc_asn1_entry asn1_toki[9], asn1_tokeninfo[2]; + + sc_copy_asn1_entry(c_asn1_toki, asn1_toki); + sc_copy_asn1_entry(c_asn1_tokeninfo, asn1_tokeninfo); + version--; + sc_format_asn1_entry(asn1_toki + 0, &version, NULL, 1); + if (card->serial_number != NULL) { + if (strlen(card->serial_number)/2 > sizeof(serial)) + return SC_ERROR_BUFFER_TOO_SMALL; + for (i = 0; card->serial_number[i] != 0; i += 2) { + int c; + if (sscanf(&card->serial_number[i], "%02X", &c) != 1) + return SC_ERROR_INVALID_ARGUMENTS; + serial[i/2] = c & 0xFF; + serial_len++; + } + sc_format_asn1_entry(asn1_toki + 1, serial, &serial_len, 1); + } + if (card->manufacturer_id != NULL) { + mnfid_len = strlen(card->manufacturer_id); + sc_format_asn1_entry(asn1_toki + 2, card->manufacturer_id, &mnfid_len, 1); + } + if (card->label != NULL) { + label_len = strlen(card->label); + sc_format_asn1_entry(asn1_toki + 3, card->label, &label_len, 1); + } + if (card->flags) { + flags_len = sc_count_bit_string_size(&card->flags, sizeof(card->flags)); + sc_format_asn1_entry(asn1_toki + 4, &card->flags, &flags_len, 1); + } + sc_format_asn1_entry(asn1_tokeninfo, asn1_toki, NULL, 1); + + r = sc_asn1_encode(card->card->ctx, asn1_tokeninfo, buf, buflen); + if (r) { + error(card->card->ctx, "sc_asn1_encode() failed: %s\n", sc_strerror(r)); + return r; + } + return 0; +} + +int sc_pkcs15_create_tokeninfo(struct sc_pkcs15_card *card) +{ + int r; + u8 *buf; + size_t buflen; + u8 line[10240]; + + r = encode_tokeninfo(card, &buf, &buflen); + if (r) { + error(card->card->ctx, "Error encoding EF(TokenInfo): %s\n", sc_strerror(r)); + return r; + } + sc_hex_dump(card->card->ctx, buf, buflen, line, sizeof(line)); + printf("%s\n", line); + return 0; +} + +static const struct sc_asn1_entry c_asn1_ddo[] = { + { "oid", SC_ASN1_OBJECT, ASN1_OBJECT, 0, NULL }, + { "odfPath", SC_ASN1_PATH, SC_ASN1_CONS | ASN1_SEQUENCE, SC_ASN1_OPTIONAL, NULL }, + { "tokenInfoPath", SC_ASN1_PATH, SC_ASN1_CONS | SC_ASN1_CTX | 0, SC_ASN1_OPTIONAL, NULL }, + { "unusedPath", SC_ASN1_PATH, SC_ASN1_CONS | SC_ASN1_CTX | 1, SC_ASN1_OPTIONAL, NULL }, + { NULL } +}; +static const struct sc_asn1_entry c_asn1_dirrecord[] = { + { "aid", SC_ASN1_OCTET_STRING, SC_ASN1_APP | 15, 0, NULL }, + { "label", SC_ASN1_UTF8STRING, SC_ASN1_APP | 16, SC_ASN1_OPTIONAL, NULL }, + { "path", SC_ASN1_OCTET_STRING, SC_ASN1_APP | 17, 0, NULL }, + { "ddo", SC_ASN1_STRUCT, SC_ASN1_APP | 19 | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL }, + { NULL } +}; +/* FIXME: this should be decoded elsewhere */ +static const struct sc_asn1_entry c_asn1_dir[] = { + { "dirRecord", SC_ASN1_STRUCT, SC_ASN1_APP | 1 | SC_ASN1_CONS, 0, NULL }, + { NULL } +}; + +static const u8 *aidref = (const u8 *) "\xA0\x00\x00\x00\x63PKCS-15"; +static const int aidref_len = 12; + static int parse_dir(const u8 * buf, size_t buflen, struct sc_pkcs15_card *card) { - const u8 *aidref = (const u8 *) "\xA0\x00\x00\x00\x63PKCS-15"; - const int aidref_len = 12; + struct sc_asn1_entry asn1_ddo[5], asn1_dirrecord[5], asn1_dir[2]; int r; u8 aid[128], label[128], path[128]; int aid_len = sizeof(aid), label_len = sizeof(label), path_len = sizeof(path); - struct sc_asn1_entry asn1_ddo[] = { - { "oid", SC_ASN1_OBJECT, ASN1_OBJECT, 0, NULL }, - { "odfPath", SC_ASN1_PATH, SC_ASN1_CONS | ASN1_SEQUENCE, SC_ASN1_OPTIONAL, &card->file_odf.path }, - { "tokenInfoPath", SC_ASN1_PATH, SC_ASN1_CONS | SC_ASN1_CTX | 0, SC_ASN1_OPTIONAL, &card->file_tokeninfo.path }, - { "unusedPath", SC_ASN1_PATH, SC_ASN1_CONS | SC_ASN1_CTX | 1, SC_ASN1_OPTIONAL, NULL }, - { NULL } - }; - struct sc_asn1_entry asn1_dir[] = { - { "aid", SC_ASN1_OCTET_STRING, SC_ASN1_APP | 15, 0, aid, &aid_len }, - { "label", SC_ASN1_UTF8STRING, SC_ASN1_APP | 16, SC_ASN1_OPTIONAL, label, &label_len }, - { "path", SC_ASN1_OCTET_STRING, SC_ASN1_APP | 17, 0, path, &path_len }, - { "ddo", SC_ASN1_STRUCT, SC_ASN1_APP | 19 | SC_ASN1_CONS, SC_ASN1_OPTIONAL, asn1_ddo }, - { NULL } - }; - - buf = sc_asn1_verify_tag(card->card->ctx, buf, buflen, SC_ASN1_APP | 1 | SC_ASN1_CONS, &buflen); - if (buf == NULL) { - error(card->card->ctx, "No [APPLICATION 1] tag in EF(DIR)\n"); - return -1; - } + sc_copy_asn1_entry(c_asn1_ddo, asn1_ddo); + sc_copy_asn1_entry(c_asn1_dirrecord, asn1_dirrecord); + sc_copy_asn1_entry(c_asn1_dir, asn1_dir); + sc_format_asn1_entry(asn1_dir + 0, asn1_dirrecord, NULL, 0); + sc_format_asn1_entry(asn1_dirrecord + 0, aid, &aid_len, 0); + sc_format_asn1_entry(asn1_dirrecord + 1, label, &label_len, 0); + sc_format_asn1_entry(asn1_dirrecord + 2, path, &path_len, 0); + sc_format_asn1_entry(asn1_dirrecord + 3, asn1_ddo, NULL, 0); + sc_format_asn1_entry(asn1_ddo + 1, &card->file_odf.path, NULL, 0); + sc_format_asn1_entry(asn1_ddo + 2, &card->file_tokeninfo.path, NULL, 0); + r = sc_asn1_decode(card->card->ctx, asn1_dir, buf, buflen, NULL, NULL); if (r) { error(card->card->ctx, "EF(DIR) parsing failed: %s\n", @@ -149,7 +247,7 @@ static int parse_dir(const u8 * buf, size_t buflen, struct sc_pkcs15_card *card) error(card->card->ctx, "AID in EF(DIR) is invalid\n"); return -1; } - if (asn1_dir[1].flags & SC_ASN1_PRESENT) + if (asn1_dirrecord[1].flags & SC_ASN1_PRESENT) card->label = strdup((char *) label); else card->label = strdup("(unknown)"); @@ -162,61 +260,193 @@ static int parse_dir(const u8 * buf, size_t buflen, struct sc_pkcs15_card *card) return 0; } +static int encode_dir(struct sc_pkcs15_card *card, u8 **buf, size_t *buflen) +{ + struct sc_asn1_entry asn1_ddo[5], asn1_dirrecord[5], asn1_dir[2]; + struct sc_context *ctx = card->card->ctx; + int r; + size_t label_len; + + sc_copy_asn1_entry(c_asn1_ddo, asn1_ddo); + sc_copy_asn1_entry(c_asn1_dirrecord, asn1_dirrecord); + sc_copy_asn1_entry(c_asn1_dir, asn1_dir); + sc_format_asn1_entry(asn1_dir + 0, asn1_dirrecord, NULL, 1); + sc_format_asn1_entry(asn1_dirrecord + 0, (void *) aidref, (void *) &aidref_len, 1); + if (card->label != NULL) { + label_len = strlen(card->label); + sc_format_asn1_entry(asn1_dirrecord + 1, card->label, &label_len, 1); + } + if (card->file_app.path.len == 0) { + error(ctx, "Application path not set.\n"); + return SC_ERROR_INVALID_ARGUMENTS; + } + sc_format_asn1_entry(asn1_dirrecord + 2, card->file_app.path.value, + &card->file_app.path.len, 1); +#if 0 + /* FIXME: encode DDO */ + sc_format_asn1_entry(asn1_dirrecord + 3, asn1_ddo, NULL, 0); + sc_format_asn1_entry(asn1_ddo + 1, &card->file_odf.path, NULL, 0); + sc_format_asn1_entry(asn1_ddo + 2, &card->file_tokeninfo.path, NULL, 0); +#endif + r = sc_asn1_encode(ctx, asn1_dir, buf, buflen); + if (r) { + error(card->card->ctx, "sc_asn1_encode() failed: %s\n", + sc_strerror(r)); + return r; + } + return 0; +} + + + +/* FIXME: This should be done using sc_update_binary(), + * and be generally wiser */ +int sc_pkcs15_create_dir(struct sc_pkcs15_card *p15card) +{ + struct sc_card *card = p15card->card; + struct sc_path path; + u8 *buf; + size_t bufsize; + int r; + u8 line[10240]; + + SC_FUNC_CALLED(card->ctx, 1); + sc_format_path("3F00", &path); + r = sc_select_file(card, &path, NULL); + SC_TEST_RET(card->ctx, r, "sc_select_file(MF) failed"); + r = encode_dir(p15card, &buf, &bufsize); + SC_TEST_RET(card->ctx, r, "EF(DIR) encoding failed"); + sc_hex_dump(p15card->card->ctx, buf, bufsize, line, sizeof(line)); + free(buf); + printf("%s", line); + + return 0; +} + +static const struct sc_asn1_entry c_asn1_odf[] = { + { "privateKeys", SC_ASN1_STRUCT, SC_ASN1_CTX | 0 | SC_ASN1_CONS, 0, NULL }, + { "certificates", SC_ASN1_STRUCT, SC_ASN1_CTX | 4 | SC_ASN1_CONS, 0, NULL }, + { "trustedCertificates", SC_ASN1_STRUCT, SC_ASN1_CTX | 5 | SC_ASN1_CONS, 0, NULL }, + { "dataObjects", SC_ASN1_STRUCT, SC_ASN1_CTX | 7 | SC_ASN1_CONS, 0, NULL }, + { "authObjects", SC_ASN1_STRUCT, SC_ASN1_CTX | 8 | SC_ASN1_CONS, 0, NULL }, + { NULL } +}; + +static const int odf_indexes[] = { + SC_PKCS15_PRKDF, + SC_PKCS15_CDF, + SC_PKCS15_CDF_TRUSTED, + SC_PKCS15_DODF, + SC_PKCS15_AODF, +}; + static int parse_odf(const u8 * buf, int buflen, struct sc_pkcs15_card *card) { const u8 *p = buf; size_t left = buflen; - int r; + int r, i; struct sc_path path; struct sc_asn1_entry asn1_obj_or_path[] = { { "path", SC_ASN1_PATH, SC_ASN1_CONS | SC_ASN1_SEQUENCE, 0, &path }, { NULL } }; - struct sc_asn1_entry asn1_odf[] = { - { "privateKeys", SC_ASN1_STRUCT, SC_ASN1_CTX | 0 | SC_ASN1_CONS, 0, asn1_obj_or_path }, - { "certificates", SC_ASN1_STRUCT, SC_ASN1_CTX | 4 | SC_ASN1_CONS, 0, asn1_obj_or_path }, - { "trustedCertificates", SC_ASN1_STRUCT, SC_ASN1_CTX | 5 | SC_ASN1_CONS, 0, asn1_obj_or_path }, - { "dataObjects", SC_ASN1_STRUCT, SC_ASN1_CTX | 7 | SC_ASN1_CONS, 0, asn1_obj_or_path }, - { "authObjects", SC_ASN1_STRUCT, SC_ASN1_CTX | 8 | SC_ASN1_CONS, 0, asn1_obj_or_path }, - { NULL } - }; + struct sc_asn1_entry asn1_odf[6]; + sc_copy_asn1_entry(c_asn1_odf, asn1_odf); + for (i = 0; asn1_odf[i].name != NULL; i++) + sc_format_asn1_entry(asn1_odf + i, asn1_obj_or_path, NULL, 0); while (left > 0) { + struct sc_pkcs15_df *df = NULL; + struct sc_file *file; + r = sc_asn1_decode_choice(card->card->ctx, asn1_odf, p, left, &p, &left); if (r == SC_ERROR_ASN1_END_OF_CONTENTS) break; if (r < 0) return r; - switch (r) { - case 0: - if (card->file_prkdf.path.len) - error(card->card->ctx, "warning: card has too many PrKDF's\n"); - card->file_prkdf.path = path; - break; - case 1: - case 2: - if (card->cdf_count == SC_PKCS15_MAX_CDFS) { - error(card->card->ctx, "too many CDFs on card\n"); + df = &card->df[odf_indexes[r]]; + if (df->count == SC_PKCS15_MAX_DFS) { + error(card->card->ctx, "too many DF's on card\n"); + continue; + } + file = malloc(sizeof(struct sc_file)); + if (file == NULL) + return SC_ERROR_OUT_OF_MEMORY; + memset(file, 0, sizeof(struct sc_file)); + file->path = path; + df->file[df->count] = file; + df->count++; + } + return 0; +} + +static int encode_odf(struct sc_pkcs15_card *card, u8 **buf, size_t *buflen) +{ + struct sc_path path; + struct sc_asn1_entry asn1_obj_or_path[] = { + { "path", SC_ASN1_PATH, SC_ASN1_CONS | SC_ASN1_SEQUENCE, 0, &path }, + }; + struct sc_asn1_entry *asn1_paths = NULL; + struct sc_asn1_entry *asn1_odf = NULL; + int df_count = 0, i, r, c = 0; + const int nr_indexes = sizeof(odf_indexes)/sizeof(odf_indexes[0]); + + for (i = 0; i < SC_PKCS15_DF_TYPE_COUNT; i++) + df_count += card->df[i].count; + asn1_odf = malloc(sizeof(struct sc_asn1_entry) * (df_count + 1)); + if (asn1_odf == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + goto err; + } + asn1_paths = malloc(sizeof(struct sc_asn1_entry) * (df_count * 2)); + if (asn1_paths == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + goto err; + } + for (i = 0; i < SC_PKCS15_DF_TYPE_COUNT; i++) { + struct sc_pkcs15_df *df = &card->df[i]; + int j, type = -1; + + if (!df->count) + continue; + for (j = 0; j < nr_indexes; j++) + if (odf_indexes[j] == i) { + type = j; break; } - card->file_cdf[card->cdf_count].path = path; - card->cdf_count++; - break; - case 3: - if (card->file_dodf.path.len) - error(card->card->ctx, "warning: card has too many DODF's\n"); - card->file_dodf.path = path; - break; - case 4: - if (card->aodf_count == SC_PKCS15_MAX_AODFS) { - error(card->card->ctx, "too many AODFs on card\n"); - break; - } - card->file_aodf[card->aodf_count].path = path; - card->aodf_count++; - break; + if (type == -1) { + error(card->card->ctx, "Unsupported DF type.\n"); + continue; + } + for (j = 0; j < df->count; j++) { + asn1_odf[c] = c_asn1_odf[type]; + sc_format_asn1_entry(asn1_odf + c, asn1_paths + 2*c, NULL, 1); + sc_copy_asn1_entry(asn1_obj_or_path, asn1_paths + 2*c); + sc_format_asn1_entry(asn1_paths + 2*c, &df->file[j]->path, NULL, 1); + c++; } } + asn1_odf[df_count].name = NULL; + r = sc_asn1_encode(card->card->ctx, asn1_odf, buf, buflen); +err: + if (asn1_paths != NULL) + free(asn1_paths); + if (asn1_odf != NULL) + free(asn1_odf); + return r; +} + +int sc_pkcs15_create_odf(struct sc_pkcs15_card *p15card) +{ + u8 *buf; + size_t buflen; + u8 line[10240]; + int r; + + r = encode_odf(p15card, &buf, &buflen); + SC_TEST_RET(p15card->card->ctx, r, "ODF encoding failed"); + sc_hex_dump(p15card->card->ctx, buf, buflen, line, sizeof(line)); + printf("ODF:\n%s", line); return 0; } @@ -235,6 +465,7 @@ int sc_pkcs15_bind(struct sc_card *card, struct sc_path tmppath; const struct sc_pkcs15_defaults *defaults = NULL; struct sc_context *ctx; + struct sc_file file; assert(sc_card_valid(card) && p15card_out != NULL); ctx = card->ctx; @@ -251,13 +482,13 @@ int sc_pkcs15_bind(struct sc_card *card, error(ctx, "sc_lock() failed: %s\n", sc_strerror(err)); goto error; } - err = sc_select_file(card, &tmppath, &p15card->file_dir); + err = sc_select_file(card, &tmppath, &file); if (err) { error(ctx, "Error selecting EF(DIR): %s\n", sc_strerror(err)); err = SC_ERROR_PKCS15_APP_NOT_FOUND; goto error; } - err = sc_read_binary(card, 0, buf, p15card->file_dir.size, 0); + err = sc_read_binary(card, 0, buf, file.size, 0); if (err < 0) { error(ctx, "Error reading EF(DIR): %s\n", sc_strerror(err)); goto error; @@ -283,10 +514,10 @@ int sc_pkcs15_bind(struct sc_card *card, } else tmppath = p15card->file_odf.path; - err = sc_select_file(card, &tmppath, &p15card->file_odf); + err = sc_select_file(card, &tmppath, &file); if (err) /* FIXME: finish writing error stuff */ goto error; - err = sc_read_binary(card, 0, buf, p15card->file_odf.size, 0); + err = sc_read_binary(card, 0, buf, file.size, 0); if (err < 0) goto error; if (err < 2) { @@ -308,10 +539,10 @@ int sc_pkcs15_bind(struct sc_card *card, defaults->defaults_func(p15card, defaults->arg); tmppath = p15card->file_tokeninfo.path; } - err = sc_select_file(card, &tmppath, &p15card->file_tokeninfo); + err = sc_select_file(card, &tmppath, &file); if (err) goto error; - err = sc_read_binary(card, 0, buf, p15card->file_tokeninfo.size, 0); + err = sc_read_binary(card, 0, buf, file.size, 0); if (err < 0) goto error; if (err <= 2) { @@ -346,8 +577,14 @@ int sc_pkcs15_detect(struct sc_card *card) int sc_pkcs15_unbind(struct sc_pkcs15_card *p15card) { + int i, j; + assert(p15card != NULL); SC_FUNC_CALLED(p15card->card->ctx, 1); + for (j = 0; j < SC_PKCS15_DF_TYPE_COUNT; j++) + for (i = 0; i < p15card->df[j].count; i++) + if (p15card->df[j].file[i]) + free(p15card->df[j].file[i]); free(p15card->label); free(p15card->serial_number); free(p15card->manufacturer_id); diff --git a/src/libopensc/pkcs15.h b/src/libopensc/pkcs15.h index bea3c9db..d12b3c30 100644 --- a/src/libopensc/pkcs15.h +++ b/src/libopensc/pkcs15.h @@ -34,10 +34,7 @@ extern "C" { #define SC_PKCS15_MAX_PRKEYS 2 #define SC_PKCS15_MAX_LABEL_SIZE 32 #define SC_PKCS15_MAX_ID_SIZE 16 -#define SC_PKCS15_MAX_CDFS 4 /* Certificate Directory - * Files */ -#define SC_PKCS15_MAX_AODFS 4 /* Authentication Object - * Directory Files */ +#define SC_PKCS15_MAX_DFS 4 #define SC_PKCS15_MAX_CERTS 4 /* Total certificates */ struct sc_pkcs15_id { @@ -45,6 +42,8 @@ struct sc_pkcs15_id { size_t len; }; +#define SC_PKCS15_CO_FLAG_OBJECT_SEEN 0x80000000 /* for PKCS #11 module */ + struct sc_pkcs15_common_obj_attr { char label[SC_PKCS15_MAX_LABEL_SIZE]; /* zero terminated */ int flags; @@ -137,14 +136,33 @@ struct sc_pkcs15_prkey_info { int modulus_length; }; +#define SC_PKCS15_PRKDF 0 +#define SC_PKCS15_PUKDF 1 +#define SC_PKCS15_PUKDF_TRUSTED 2 +#define SC_PKCS15_SKDF 3 +#define SC_PKCS15_CDF 4 +#define SC_PKCS15_CDF_TRUSTED 5 +#define SC_PKCS15_CDF_USEFUL 6 +#define SC_PKCS15_DODF 7 +#define SC_PKCS15_AODF 8 +#define SC_PKCS15_DF_TYPE_COUNT 9 + + +struct sc_pkcs15_df { + struct sc_file *file[SC_PKCS15_MAX_DFS]; + int count, record_length; +}; + struct sc_pkcs15_card { struct sc_card *card; char *label; /* fields from TokenInfo: */ int version; char *serial_number, *manufacturer_id; - int flags; + unsigned long flags; struct sc_pkcs15_algorithm_info alg_info[1]; + /* FIXME: this could be done better with some C pre-processor + * magic */ struct sc_pkcs15_cert_info cert_info[SC_PKCS15_MAX_CERTS]; int cert_count; struct sc_pkcs15_prkey_info prkey_info[SC_PKCS15_MAX_PRKEYS]; @@ -152,15 +170,10 @@ struct sc_pkcs15_card { struct sc_pkcs15_pin_info pin_info[SC_PKCS15_MAX_PINS]; int pin_count; + /* FIXME: Move file_dir somewhere else, perhaps to sc_card */ struct sc_file file_dir, file_app; - /* in app DF */ struct sc_file file_tokeninfo, file_odf; - struct sc_file file_prkdf; - struct sc_file file_cdf[SC_PKCS15_MAX_CDFS]; - int cdf_count; - struct sc_file file_aodf[SC_PKCS15_MAX_AODFS]; - int aodf_count; - struct sc_file file_dodf; + struct sc_pkcs15_df df[SC_PKCS15_DF_TYPE_COUNT]; int use_cache; }; diff --git a/src/libopensc/sc-internal.h b/src/libopensc/sc-internal.h index 9883e870..440c597b 100644 --- a/src/libopensc/sc-internal.h +++ b/src/libopensc/sc-internal.h @@ -32,6 +32,7 @@ /* Internal use only */ int sc_sw_to_errorcode(struct sc_card *card, int sw1, int sw2); +size_t sc_count_bit_string_size(const void * buf, size_t bufsize); /* Default timeout value for SCardGetStatusChange */ #ifndef SC_CUSTOM_STATUS_TIMEOUT diff --git a/src/tools/opensc-explorer.c b/src/tools/opensc-explorer.c index e68408ce..3b4a686a 100644 --- a/src/tools/opensc-explorer.c +++ b/src/tools/opensc-explorer.c @@ -42,10 +42,7 @@ const char *option_help[] = { NULL }; const char *cmds[] = { "ls", "cd", "debug", "cat", "info", "create", "delete", - "verify" -}; -const char *cmdusage[] = { - "", "", "", "", "", "", "", " " + "verify", "put", "get" }; const int nr_cmds = sizeof(cmds)/sizeof(cmds[0]); @@ -87,6 +84,22 @@ void check_ret(int r, int op, const char *err, const struct sc_file *file) fprintf(stderr, "ACL for operation: %s\n", acl_to_str(file->acl[op])); } +int arg_to_path(const char *arg, struct sc_path *path) +{ + char buf[6]; + + if (strlen(arg) != 4) { + printf("Wrong ID length.\n"); + return -1; + } + strcpy(buf, "I"); + strcat(buf, arg); + sc_format_path(buf, path); + if (path->len != 2) + return -1; + return 0; +} + void print_file(const struct sc_file *file) { const char *st; @@ -164,7 +177,6 @@ int do_cd(const char *arg) { struct sc_path path; struct sc_file file; - char buf[6]; int r; if (strcmp(arg, "..") == 0) { @@ -182,15 +194,7 @@ int do_cd(const char *arg) current_path = path; return 0; } - - if (strlen(arg) != 4) { - printf("Usage: cd \n"); - return -1; - } - strcpy(buf, "I"); - strcat(buf, arg); - sc_format_path(buf, &path); - if (path.len != 2) { + if (arg_to_path(arg, &path) != 0) { printf("Usage: cd \n"); return -1; } @@ -215,7 +219,6 @@ int do_cd(const char *arg) return 0; } - int do_cat(const char *arg) { u8 buf[256]; @@ -231,14 +234,7 @@ int do_cat(const char *arg) file = current_file; not_current = 0; } else { - if (strlen(arg) != 4) { - printf("Usage: cat [file_id]\n"); - return -1; - } - strcpy((char *) buf, "I"); - strcat((char *) buf, arg); - sc_format_path((char *) buf, &path); - if (path.len != 2) { + if (arg_to_path(arg, &path) != 0) { printf("Usage: cat [file_id]\n"); return -1; } @@ -291,17 +287,9 @@ int do_info(const char *arg) file = current_file; not_current = 0; } else { - char buf[6]; struct sc_path tmppath; - if (strlen(arg) != 4) { - printf("Usage: info [file_id]\n"); - return -1; - } - strcpy(buf, "I"); - strcat(buf, arg); - sc_format_path(buf, &tmppath); - if (tmppath.len != 2) { + if (arg_to_path(arg, &tmppath) != 0) { printf("Usage: info [file_id]\n"); return -1; } @@ -359,8 +347,24 @@ int do_info(const char *arg) "Linear fixed, SIMPLE-TLV", "Linear variable", "Cyclic", "Cyclic, SIMPLE-TLV", }; + const char *ops[] = { + "READ", "UPDATE", "WRITE", "ERASE", "REHABILITATE", + "INVALIDATE" + }; printf("%-15s%s\n", "EF structure:", structs[file.ef_structure]); + for (i = 0; i < sizeof(ops)/sizeof(ops[0]); i++) { + char buf[80]; + + sprintf(buf, "ACL for %s:", ops[i]); + printf("%-25s%s\n", buf, acl_to_str(file.acl[i])); + } } + if (file.prop_attr_len) { + printf("%-25s", "Proprietary attributes:"); + for (i = 0; i < file.prop_attr_len; i++) + printf("%02X ", file.prop_attr[i]); + printf("\n"); + } printf("\n"); if (not_current) { r = sc_select_file(card, ¤t_path, NULL); @@ -374,18 +378,12 @@ int do_info(const char *arg) int do_create(const char *arg, const char *arg2) { - char buf[6]; struct sc_path path; struct sc_file file; size_t size; int i, r; - if (strlen(arg) != 4) - goto usage; - strcpy(buf, "I"); - strcat(buf, arg); - sc_format_path(buf, &path); - if (path.len != 2) + if (arg_to_path(arg, &path) != 0) goto usage; if (sscanf(arg2, "%d", &size) != 1) goto usage; @@ -396,12 +394,20 @@ int do_create(const char *arg, const char *arg2) for (i = 0; i < SC_MAX_AC_OPS; i++) file.acl[i] = SC_AC_NONE; file.size = size; + file.status = SC_FILE_STATUS_ACTIVATED; r = sc_create_file(card, &file); if (r) { check_ret(r, SC_AC_OP_CREATE, "CREATE FILE failed", ¤t_file); return -1; } + /* Make sure we're back in the parent directory, because on some cards + * CREATE FILE also selects the newly created file. */ + r = sc_select_file(card, ¤t_path, NULL); + if (r) { + printf("unable to select parent file: %s\n", sc_strerror(r)); + die(1); + } return 0; usage: printf("Usage: create \n"); @@ -410,16 +416,10 @@ usage: int do_delete(const char *arg) { - char buf[6]; struct sc_path path; int r; - if (strlen(arg) != 4) - goto usage; - strcpy(buf, "I"); - strcat(buf, arg); - sc_format_path(buf, &path); - if (path.len != 2) + if (arg_to_path(arg, &path) != 0) goto usage; r = sc_delete_file(card, &path); if (r) { @@ -490,6 +490,139 @@ usage: return -1; } +int do_get(const char *arg, const char *arg2) +{ + u8 buf[256]; + int r, error = 0; + size_t count = 0; + unsigned int idx = 0; + struct sc_path path; + struct sc_file file; + const char *filename; + FILE *outf = NULL; + + if (arg_to_path(arg, &path) != 0) + goto usage; + if (strlen(arg2)) + filename = arg2; + else { + sprintf(buf, "%02X%02X", path.value[0], path.value[1]); + filename = buf; + } + outf = fopen(filename, "w"); + if (outf == NULL) { + perror(filename); + return -1; + } + r = sc_select_file(card, &path, &file); + if (r) { + check_ret(r, SC_AC_OP_SELECT, "unable to select file", ¤t_file); + return -1; + } + count = file.size; + while (count) { + int c = count > sizeof(buf) ? sizeof(buf) : count; + + r = sc_read_binary(card, idx, buf, c, 0); + if (r < 0) { + check_ret(r, SC_AC_OP_READ, "read failed", &file); + error = 1; + goto err; + } + if (r != c) { + printf("expecting %d, got only %d bytes.\n", c, r); + error = 1; + goto err; + } + fwrite(buf, c, 1, outf); + idx += c; + count -= c; + } + printf("Total of %d bytes read.\n", idx); +err: + r = sc_select_file(card, ¤t_path, NULL); + if (r) { + printf("unable to select parent file: %s\n", sc_strerror(r)); + die(1); + } + if (outf) + fclose(outf); + return -error; +usage: + printf("Usage: get [output file]\n"); + return -1; +} + +int do_put(const char *arg, const char *arg2) +{ + u8 buf[256]; + int r, error = 0; + size_t count = 0; + unsigned int idx = 0; + struct sc_path path; + struct sc_file file; + const char *filename; + FILE *outf = NULL; + + if (arg_to_path(arg, &path) != 0) + goto usage; + if (strlen(arg2)) + filename = arg2; + else { + sprintf(buf, "%02X%02X", path.value[0], path.value[1]); + filename = buf; + } + outf = fopen(filename, "r"); + if (outf == NULL) { + perror(filename); + return -1; + } + r = sc_select_file(card, &path, &file); + if (r) { + check_ret(r, SC_AC_OP_SELECT, "unable to select file", ¤t_file); + return -1; + } + count = file.size; + while (count) { + int c = count > sizeof(buf) ? sizeof(buf) : count; + + r = fread(buf, 1, c, outf); + if (r < 0) { + perror("fread"); + error = 1; + goto err; + } + if (r != c) + count = c = r; + r = sc_update_binary(card, idx, buf, c, 0); + if (r < 0) { + check_ret(r, SC_AC_OP_READ, "update failed", &file); + error = 1; + goto err; + } + if (r != c) { + printf("expecting %d, wrote only %d bytes.\n", c, r); + error = 1; + goto err; + } + idx += c; + count -= c; + } + printf("Total of %d bytes written.\n", idx); +err: + r = sc_select_file(card, ¤t_path, NULL); + if (r) { + printf("unable to select parent file: %s\n", sc_strerror(r)); + die(1); + } + if (outf) + fclose(outf); + return -error; +usage: + printf("Usage: put [output file]\n"); + return -1; +} + int handle_cmd(int cmd, const char *arg, const char *arg2) { int i; @@ -519,6 +652,10 @@ int handle_cmd(int cmd, const char *arg, const char *arg2) return do_delete(arg); case 7: return do_verify(arg, arg2); + case 8: + return do_put(arg, arg2); + case 9: + return do_get(arg, arg2); default: printf("Don't know how to handle command.\n"); } diff --git a/src/tools/util.c b/src/tools/util.c index 87815642..49d9b699 100644 --- a/src/tools/util.c +++ b/src/tools/util.c @@ -96,7 +96,7 @@ const char * acl_to_str(unsigned int acl) static char line[80]; if (acl == SC_AC_UNKNOWN) - return "UNKN"; + return "N/A"; if (acl == SC_AC_NEVER) return "NEVR"; if (acl == SC_AC_NONE)