Hello Viktor, On Tue, 2010-10-26 at 17:23 +0200, Viktor TARASOV wrote: > I've got this conclusion from the 'Unambiguous descriptions of ASN.1 > tagging' > (http://powerasn.ncottin.net/download/ASN1_Tagging.pdf) . > > I agree that it's not a supreme authority on the subject, > nevertheless the natively PKCS#15 cards that I'm using (Oberthur, > Morpho, Gemalto) have the encoding > of the 'lastUpdate' presented in the second dump.
Exactly that information I need. Thank you very much. Please can you try the attached patch with the other p15 cards you have. Only "pkcs15-tool -D" will produce meaningful output. And "pkcs15-init -C -c cardos -p pkcs15 --so-pin=xxxx" will encode a conforming TokenInfo (may work for other cards too). Notes: * the path of PKCS#15 Application (3F005015) is hard coded in pkcs15.c line 665 * you could remove line 756 in pkcs15.c but I assume that something will fail, because encoding of all other objects is wrong too Kind Regards Andre Zepezauer
Index: tools/pkcs15-tool.c =================================================================== --- tools/pkcs15-tool.c (revision 4829) +++ tools/pkcs15-tool.c (working copy) @@ -1093,37 +1093,202 @@ return 0; } -static int dump(void) +static char buf[1024]; + +static const char *get_parameters(sc_pkcs15_der_t der) { + if (der.len < (sizeof(buf) / 2 - 1)) { + sc_bin_to_hex(der.value, der.len, buf, sizeof(buf), ' '); + } else { + buf[0] = '\0'; + } - const char *flags[] = { - "Read-only", - "Login required", - "PRN generation", - "EID compliant" - }; + return buf; +} - int i, count = 0; +static const char *get_operations(int ops) +{ + memset(buf, '-', 8); + if (ops & 0x01) buf[0] = 'c'; // compute-checksum + if (ops & 0x02) buf[1] = 's'; // compute-signature + if (ops & 0x04) buf[2] = 'q'; // verify-checksum + if (ops & 0x08) buf[3] = 'v'; // verify-signature + if (ops & 0x10) buf[4] = 'e'; // encipher + if (ops & 0x20) buf[5] = 'd'; // decipher + if (ops & 0x40) buf[6] = 'h'; // hash + if (ops & 0x80) buf[7] = 'g'; // generate-key + buf[8] = 0; - printf("PKCS#15 Card [%s]:\n", p15card->tokeninfo->label); - printf("\tVersion : %d\n", p15card->tokeninfo->version); - printf("\tSerial number : %s\n", p15card->tokeninfo->serial_number); - printf("\tManufacturer ID: %s\n", p15card->tokeninfo->manufacturer_id); - if (p15card->tokeninfo->last_update) - printf("\tLast update : %s\n", p15card->tokeninfo->last_update); - if (p15card->tokeninfo->preferred_language) - printf("\tLanguage : %s\n", p15card->tokeninfo->preferred_language); - printf("\tFlags : "); - for (i = 0; i < 4; i++) { - if ((p15card->tokeninfo->flags >> i) & 1) { - if (count) - printf(", "); - printf("%s", flags[i]); - count++; + return buf; +} + +static const char *get_oid(struct sc_object_id oid) +{ + char comp[32]; /* temporary storage of decimal encoded integers */ + buf[0] = 0; + if (oid.value[0] != -1) { + sprintf(comp, "%d", oid.value[0]); + strcat(buf, comp); + int i = 1; + while (i < SC_MAX_OBJECT_ID_OCTETS && oid.value[i] != -1) { + sprintf(comp, ".%d", oid.value[i]); + strcat(buf, comp); + i++; } } - printf("\n\n"); + return buf; +} + +static void conditional_fprintf(FILE *fd, const char *str, const char *pre, const char *val) +{ + if (val) + fprintf(fd, str, pre, val); +} + +void strcpy_bp(u8 * dst, const char *src, size_t dstsize) +{ + size_t c; + + if (!dst || !src || !dstsize) + return; + + memset((char *)dst, ' ', dstsize); + + c = strlen(src) > dstsize ? dstsize : strlen(src); + + memcpy((char *)dst, src, c); +} + +static void fprint_algorithm_infos(FILE *fd, const sc_pkcs15_algorithm_info_t **alg, const char *pre) +{ + int i, num, max[6]; + + /* determine number of items */ + if (alg) { + num = -1; + while (alg[++num]); + } else { + num = 0; + } + + for (i = 0; i < 6; i++) + max[i] = 0; + + /* determine max width of columns */ + for (i = 0; i < num; i++) { + /* max[0] := reference / not required */ + if (alg[i]->algorithm > max[1]) + max[1] = alg[i]->algorithm; + if (strlen(get_parameters(alg[i]->parameters)) > max[2]) + max[2] = strlen(get_parameters(alg[i]->parameters)); + if (strlen(get_operations(alg[i]->operations)) > max[3]) + max[3] = strlen(get_operations(alg[i]->operations)); + if (strlen(get_oid(alg[i]->oid)) > max[4]) + max[4] = strlen(get_oid(alg[i]->oid)); + /* max[5] := alg_ref / not required */ + } + + char *par = calloc(1, max[2] + 1); + char *ops = calloc(1, max[3] + 1); + char *oid = calloc(1, max[4] + 1); + + /* write num rows to fd (one row for each algorithm) */ + for (i = 0; i < num; i++) { + strcpy_bp(par, get_parameters(alg[i]->parameters), max[2]); + strcpy_bp(ops, get_operations(alg[i]->operations), max[3]); + strcpy_bp(oid, get_oid(alg[i]->oid), max[4]); + if (max[1] < 256) { + fprintf(fd, "%s%3d: 0x%02x [%s] %s %s 0x%02x\n", pre, + alg[i]->reference, alg[i]->algorithm, par, ops, oid, alg[i]->alg_ref); + } else { + fprintf(fd, "%s%3d: 0x%04x [%s] %s %s 0x%02x\n", pre, + alg[i]->reference, alg[i]->algorithm, par, ops, oid, alg[i]->alg_ref); + } + } + + free(par); + free(ops); + free(oid); +} + + +void fprint_tokeninfo(FILE *fd, const sc_pkcs15_tokeninfo_t *ti, const char *pre) +{ + fprintf(fd, "%sVersion : %d\n", pre, ti->version); + conditional_fprintf(fd, "%sSerial Number : %s\n", pre, ti->serial_number); + conditional_fprintf(fd, "%sManufacturer : %s\n", pre, ti->manufacturer_id); + conditional_fprintf(fd, "%sLabel : %s\n", pre, ti->label); + if (ti->flags) { + const char *flags[] = { + "Read-only", + "Login required", + "PRN generation", + "EID compliant" + }; + int i, count = 0; + + fprintf(fd, "%sFlags : ", pre); + for (i = 0; i < 4; i++) { + if ((ti->flags >> i) & 1) { + if (count) + printf(", "); + fprintf(fd, "%s", flags[i]); + count++; + } + } + fprintf(fd, "\n"); + } + if (ti->record_info) { + char buf[7][10]; + int i, val[7]; + + val[0] = ti->record_info->od_record_length; + val[1] = ti->record_info->prkd_record_length; + val[2] = ti->record_info->pukd_record_length; + val[3] = ti->record_info->skd_record_length; + val[4] = ti->record_info->cd_record_length; + val[5] = ti->record_info->dcod_record_length; + val[6] = ti->record_info->aod_record_length; + + for (i = 0; i < 7; i++) { + if (val[i] < 0) { + sprintf(buf[i], "t"); /* transparent EF */ + } else if (val[i] == 0) { + sprintf(buf[i], "v"); /* variable length records */ + } else if (val[i] >= (1 << 16)) { + sprintf(buf[i], "?"); /* value too large */ + } else { + sprintf(buf[i], "%d", val[i]); /* fixed length records */ + } + } + + fprintf(fd, "%sRecord Info : o=%s pr=%s pu=%s s=%s c=%s d=%s a=%s\n", + pre, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); + } + if (ti->algorithm_infos[0] && (strlen(pre) < 128)) { + fprintf(fd, "%sAlgorithms : [reference: algorithm parameters operations oid algRef]\n", pre); + char buf[132]; buf[0] = 0; strcat(buf, pre); strcat(buf, "\t"); + fprint_algorithm_infos(fd, (const sc_pkcs15_algorithm_info_t **) ti->algorithm_infos, buf); + } + conditional_fprintf(fd, "%sIssuer Id : %s\n", pre, ti->issuer_id); + conditional_fprintf(fd, "%sHolder Id : %s\n", pre, ti->holder_id); + conditional_fprintf(fd, "%sLast Update : %s\n", pre, ti->last_update); + conditional_fprintf(fd, "%sLanguage : %s\n", pre, ti->preferred_language); +} + +static int dump(void) +{ + if (p15card->tokeninfo->version == 0) { + printf("TokenInfo [PKCS#15]\n"); + } else if (p15card->tokeninfo->version == 1) { + printf("TokenInfo [ISO/IEC 7816-15:2004]\n"); + } else { + printf("TokenInfo [unknown]\n"); + } + + fprint_tokeninfo(stdout, p15card->tokeninfo, "\t"); + printf("\n"); list_pins(); list_private_keys(); list_public_keys(); Index: libopensc/pkcs15.c =================================================================== --- libopensc/pkcs15.c (revision 4829) +++ libopensc/pkcs15.c (working copy) @@ -30,162 +30,194 @@ #include "pkcs15.h" #include "asn1.h" -static const struct sc_asn1_entry c_asn1_twlabel[] = { - { "twlabel", SC_ASN1_UTF8STRING, SC_ASN1_TAG_UTF8STRING, 0, NULL, NULL }, - { NULL, 0, 0, 0, NULL, NULL } -}; -static const struct sc_asn1_entry c_asn1_algorithm_info[7] = { - { "reference", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, - { "algorithmPKCS#11", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, - { "parameters", SC_ASN1_NULL, SC_ASN1_TAG_NULL, 0, NULL, NULL }, - { "supportedOperations",SC_ASN1_BIT_FIELD, SC_ASN1_TAG_BIT_STRING, 0, NULL, NULL }, - { "objId", SC_ASN1_OBJECT, SC_ASN1_TAG_OBJECT, 0, NULL, NULL }, - { "algRef", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, +static const struct sc_asn1_entry DEF_TokenInfo[] = { + { "version", SC_ASN1_INTEGER, 0, 0, NULL, NULL }, + { "serialNumber", SC_ASN1_OCTET_STRING, 0, SC_ASN1_OPTIONAL, NULL, NULL }, + { "manufacturerID", SC_ASN1_UTF8STRING, 0, SC_ASN1_OPTIONAL, NULL, NULL }, + { "label", SC_ASN1_UTF8STRING, SC_ASN1_CTX | 0, SC_ASN1_OPTIONAL, NULL, NULL }, + { "cardflags", SC_ASN1_BIT_FIELD, 0, 0, NULL, NULL }, + { "seInfo", SC_ASN1_SEQUENCE, 0, SC_ASN1_OPTIONAL, NULL, NULL }, + { "recordInfo", SC_ASN1_SEQUENCE, SC_ASN1_CTX | 1, SC_ASN1_OPTIONAL, NULL, NULL }, + { "supportedAlgorithms", SC_ASN1_SEQUENCE, SC_ASN1_CTX | 2, SC_ASN1_OPTIONAL, NULL, NULL }, + { "issuerId", SC_ASN1_UTF8STRING, SC_ASN1_CTX | 3, SC_ASN1_OPTIONAL, NULL, NULL }, + { "holderId", SC_ASN1_UTF8STRING, SC_ASN1_CTX | 4, SC_ASN1_OPTIONAL, NULL, NULL }, + { "lastUpdate", SC_ASN1_CHOICE, SC_ASN1_CTX | 5, SC_ASN1_OPTIONAL, NULL, NULL }, + { "preferredLanguage", SC_ASN1_PRINTABLESTRING, 0, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; -/* - * in src/libopensc/types.h SC_MAX_SUPPORTED_ALGORITHMS defined as 8 - */ -static const struct sc_asn1_entry c_asn1_supported_algorithms[SC_MAX_SUPPORTED_ALGORITHMS + 1] = { - { "algorithmInfo", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, - { "algorithmInfo", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, - { "algorithmInfo", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, - { "algorithmInfo", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, - { "algorithmInfo", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, - { "algorithmInfo", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, - { "algorithmInfo", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, - { "algorithmInfo", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, +static const struct sc_asn1_entry DEF_RecordInfo[] = { + { "oDRecordLength", SC_ASN1_INTEGER, SC_ASN1_CTX | 0, SC_ASN1_OPTIONAL, NULL, NULL }, + { "prKDRecordLength", SC_ASN1_INTEGER, SC_ASN1_CTX | 1, SC_ASN1_OPTIONAL, NULL, NULL }, + { "puKDRecordLength", SC_ASN1_INTEGER, SC_ASN1_CTX | 2, SC_ASN1_OPTIONAL, NULL, NULL }, + { "sKDRecordLength", SC_ASN1_INTEGER, SC_ASN1_CTX | 3, SC_ASN1_OPTIONAL, NULL, NULL }, + { "cDRecordLength", SC_ASN1_INTEGER, SC_ASN1_CTX | 4, SC_ASN1_OPTIONAL, NULL, NULL }, + { "dCODRecordLength", SC_ASN1_INTEGER, SC_ASN1_CTX | 5, SC_ASN1_OPTIONAL, NULL, NULL }, + { "aODRecordLength", SC_ASN1_INTEGER, SC_ASN1_CTX | 6, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; -static const struct sc_asn1_entry c_asn1_toki[] = { - { "version", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, - { "serialNumber", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING, SC_ASN1_OPTIONAL, NULL, NULL }, - { "manufacturerID", SC_ASN1_UTF8STRING, SC_ASN1_TAG_UTF8STRING, SC_ASN1_OPTIONAL, NULL, NULL }, - { "label", SC_ASN1_UTF8STRING, SC_ASN1_CTX | 0, SC_ASN1_OPTIONAL, NULL, NULL }, - /* XXX the Taiwanese ID card erroneously uses explicit tagging */ - { "label-tw", SC_ASN1_STRUCT, SC_ASN1_CTX | 0 | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, - { "tokenflags", SC_ASN1_BIT_FIELD, SC_ASN1_TAG_BIT_STRING, 0, NULL, NULL }, - { "seInfo", SC_ASN1_SE_INFO, SC_ASN1_CONS | SC_ASN1_TAG_SEQUENCE, SC_ASN1_OPTIONAL, NULL, NULL }, - { "recordInfo", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_CTX | 1, SC_ASN1_OPTIONAL, NULL, NULL }, - { "supportedAlgorithms", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_CTX | 2, SC_ASN1_OPTIONAL, NULL, NULL }, - { "issuerId", SC_ASN1_UTF8STRING, SC_ASN1_CTX | 3, SC_ASN1_OPTIONAL, NULL, NULL }, - { "holderId", SC_ASN1_UTF8STRING, SC_ASN1_CTX | 4, SC_ASN1_OPTIONAL, NULL, NULL }, - { "lastUpdate", SC_ASN1_GENERALIZEDTIME, SC_ASN1_CTX | 5, SC_ASN1_OPTIONAL, NULL, NULL }, - { "preferredLanguage", SC_ASN1_PRINTABLESTRING, SC_ASN1_TAG_PRINTABLESTRING, SC_ASN1_OPTIONAL, NULL, NULL }, +static const struct sc_asn1_entry DEF_AlgorithmInfo[] = { + { "reference", SC_ASN1_INTEGER, 0, 0, NULL, NULL }, + { "algorithm", SC_ASN1_INTEGER, 0, 0, NULL, NULL }, + { "parameters", SC_ASN1_NULL, 0, 0, NULL, NULL }, + { "supportedOperations", SC_ASN1_BIT_FIELD, 0, 0, NULL, NULL }, + { "objId", SC_ASN1_OBJECT, 0, SC_ASN1_OPTIONAL, NULL, NULL }, + { "algRef", SC_ASN1_INTEGER, 0, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; -static const struct sc_asn1_entry c_asn1_tokeninfo[] = { - { "TokenInfo", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_TAG_SEQUENCE, 0, NULL, NULL }, +static const struct sc_asn1_entry DEF_LastUpdate[] = { + { "generalizedTime", SC_ASN1_GENERALIZEDTIME, 0, 0, NULL, NULL }, +// { "referencedTime", SC_ASN1_CHOICE, 0, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; -int sc_pkcs15_parse_tokeninfo(sc_context_t *ctx, - sc_pkcs15_tokeninfo_t *ti, const u8 *buf, size_t blen) +static int decode_sequence(sc_context_t *ctx, struct sc_asn1_entry *DEF_TYPE, + const u8 *in, size_t in_len, const u8 **newp, size_t *len_left) { - int r, ii; - u8 serial[128]; - size_t serial_len = sizeof(serial); - u8 mnfid[SC_PKCS15_MAX_LABEL_SIZE]; - size_t mnfid_len = sizeof(mnfid); - u8 label[SC_PKCS15_MAX_LABEL_SIZE]; - size_t label_len = sizeof(label); - u8 last_update[32]; - size_t lupdate_len = sizeof(last_update) - 1; - size_t flags_len = sizeof(ti->flags); - struct sc_asn1_entry asn1_toki[14], asn1_tokeninfo[3], asn1_twlabel[3]; - u8 preferred_language[3]; - size_t lang_length = sizeof(preferred_language); - struct sc_asn1_entry asn1_supported_algorithms[SC_MAX_SUPPORTED_ALGORITHMS + 1], - asn1_algo_infos[SC_MAX_SUPPORTED_ALGORITHMS][7]; - size_t reference_len = sizeof(ti->supported_algos[0].reference); - size_t mechanism_len = sizeof(ti->supported_algos[0].mechanism); - size_t operations_len = sizeof(ti->supported_algos[0].operations); - size_t algo_ref_len = sizeof(ti->supported_algos[0].algo_ref); + struct sc_asn1_entry asn1_sequence[] = { + { "SEQUENCE", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_TAG_SEQUENCE, 0, DEF_TYPE, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; - memset(last_update, 0, sizeof(last_update)); - sc_copy_asn1_entry(c_asn1_twlabel, asn1_twlabel); - sc_copy_asn1_entry(c_asn1_toki, asn1_toki); - sc_copy_asn1_entry(c_asn1_tokeninfo, asn1_tokeninfo); - sc_format_asn1_entry(asn1_twlabel, label, &label_len, 0); - - for (ii=0; ii<SC_MAX_SUPPORTED_ALGORITHMS; ii++) - sc_copy_asn1_entry(c_asn1_algorithm_info, asn1_algo_infos[ii]); - sc_copy_asn1_entry(c_asn1_supported_algorithms, asn1_supported_algorithms); + return sc_asn1_decode(ctx, asn1_sequence, in, in_len, newp, len_left); +} - for (ii=0; ii<SC_MAX_SUPPORTED_ALGORITHMS; ii++) { - sc_format_asn1_entry(asn1_algo_infos[ii] + 0, &ti->supported_algos[ii].reference, &reference_len, 0); - sc_format_asn1_entry(asn1_algo_infos[ii] + 1, &ti->supported_algos[ii].mechanism, &mechanism_len, 0); - sc_format_asn1_entry(asn1_algo_infos[ii] + 2, NULL, NULL, 0); - sc_format_asn1_entry(asn1_algo_infos[ii] + 3, &ti->supported_algos[ii].operations, &operations_len, 0); - sc_format_asn1_entry(asn1_algo_infos[ii] + 4, &ti->supported_algos[ii].algo_id, NULL, 1); - sc_format_asn1_entry(asn1_algo_infos[ii] + 5, &ti->supported_algos[ii].algo_ref, &algo_ref_len, 0); - sc_format_asn1_entry(asn1_supported_algorithms + ii, asn1_algo_infos[ii], NULL, 0); + +int sc_pkcs15_parse_tokeninfo(sc_context_t *ctx, sc_pkcs15_tokeninfo_t *ti, const u8 *in, size_t in_len) +{ + u8 *buf_sn, *buf_si, *buf_ri, *buf_ai; + size_t buf_sn_len, buf_si_len, buf_ri_len, buf_ai_len, l, l_update; + struct sc_asn1_entry TokenInfo[14], RecordInfo[8], AlgorithmInfo[7], LastUpdate[3]; + + sc_pkcs15_record_info_t ri; + ri.od_record_length = -1; + ri.prkd_record_length = -1; + ri.pukd_record_length = -1; + ri.skd_record_length = -1; + ri.cd_record_length = -1; + ri.dcod_record_length = -1; + ri.aod_record_length = -1; + + /* assamble RecordInfo */ + sc_copy_asn1_entry(DEF_RecordInfo, RecordInfo); + sc_format_asn1_entry(RecordInfo + 0, &ri.od_record_length, NULL, 0); + sc_format_asn1_entry(RecordInfo + 1, &ri.prkd_record_length, NULL, 0); + sc_format_asn1_entry(RecordInfo + 2, &ri.pukd_record_length, NULL, 0); + sc_format_asn1_entry(RecordInfo + 3, &ri.skd_record_length, NULL, 0); + sc_format_asn1_entry(RecordInfo + 4, &ri.cd_record_length, NULL, 0); + sc_format_asn1_entry(RecordInfo + 5, &ri.dcod_record_length, NULL, 0); + sc_format_asn1_entry(RecordInfo + 6, &ri.aod_record_length, NULL, 0); + + /* assamble LastUpdate */ + sc_copy_asn1_entry(DEF_LastUpdate, LastUpdate); + sc_format_asn1_entry(LastUpdate + 0, &ti->last_update, &l_update, 0); + sc_format_asn1_entry(LastUpdate + 1, NULL, NULL, 0); + LastUpdate[0].flags |= SC_ASN1_ALLOC; + + /* assamble TokenInfo */ + sc_copy_asn1_entry(DEF_TokenInfo, TokenInfo); + sc_format_asn1_entry(TokenInfo + 0, &ti->version, NULL, 0); + sc_format_asn1_entry(TokenInfo + 1, &buf_sn, &buf_sn_len, 0); + sc_format_asn1_entry(TokenInfo + 2, &ti->manufacturer_id, &l, 0); + sc_format_asn1_entry(TokenInfo + 3, &ti->label, &l, 0); + sc_format_asn1_entry(TokenInfo + 4, &ti->flags, NULL, 0); + sc_format_asn1_entry(TokenInfo + 5, &buf_si, &buf_si_len, 0); + sc_format_asn1_entry(TokenInfo + 6, &buf_ri, &buf_ri_len, 0); + sc_format_asn1_entry(TokenInfo + 7, &buf_ai, &buf_ai_len, 0); + sc_format_asn1_entry(TokenInfo + 8, &ti->issuer_id, &l, 0); + sc_format_asn1_entry(TokenInfo + 9, &ti->holder_id, &l, 0); + sc_format_asn1_entry(TokenInfo + 10, LastUpdate, NULL, 0); + sc_format_asn1_entry(TokenInfo + 11, &ti->preferred_language, &l, 0); + + /* allocate everything where entry->arg != NULL */ + l = -1; + while (TokenInfo[++l].name) + if (TokenInfo[l].arg != NULL) + TokenInfo[l].flags |= SC_ASN1_ALLOC; + + if(decode_sequence(ctx, TokenInfo, in, in_len, NULL, NULL)) + goto error; + + /* encode serial number in hex, if present */ + if (TokenInfo[1].flags & SC_ASN1_PRESENT) { + l = buf_sn_len * 2 + 1; + ti->serial_number = malloc(l); + if (ti->serial_number == NULL) goto error; + sc_bin_to_hex(buf_sn, buf_sn_len, ti->serial_number, l, 0); + free(buf_sn); } - sc_format_asn1_entry(asn1_toki + 0, &ti->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, asn1_twlabel, NULL, 0); - sc_format_asn1_entry(asn1_toki + 5, &ti->flags, &flags_len, 0); - sc_format_asn1_entry(asn1_toki + 6, &ti->seInfo, &ti->num_seInfo, 0); - sc_format_asn1_entry(asn1_toki + 7, NULL, NULL, 0); - sc_format_asn1_entry(asn1_toki + 8, asn1_supported_algorithms, NULL, 0); - sc_format_asn1_entry(asn1_toki + 9, NULL, NULL, 0); - sc_format_asn1_entry(asn1_toki + 10, NULL, NULL, 0); - sc_format_asn1_entry(asn1_toki + 11, last_update, &lupdate_len, 0); - sc_format_asn1_entry(asn1_toki + 12, preferred_language, &lang_length, 0); - sc_format_asn1_entry(asn1_tokeninfo, asn1_toki, NULL, 0); - - r = sc_asn1_decode(ctx, asn1_tokeninfo, buf, blen, NULL, NULL); - if (r) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "ASN.1 parsing of EF(TokenInfo) failed: %s", - sc_strerror(r)); - return r; + /* TODO: process seInfo */ + if (TokenInfo[5].flags & SC_ASN1_PRESENT) { + u8 buf[4096]; + sc_hex_dump(ctx, 0, buf_si, buf_si_len, buf, sizeof(buf)); + printf("seInfo:\n%s\n", buf); + free(buf_si); } - ti->serial_number = malloc(serial_len * 2 + 1); - if (ti->serial_number == NULL) - return SC_ERROR_OUT_OF_MEMORY; - ti->serial_number[0] = 0; - for (ii = 0; ii < serial_len; ii++) { - char byte[3]; - sprintf(byte, "%02X", serial[ii]); - strcat(ti->serial_number, byte); + /* decode RecordInfo */ + if (TokenInfo[6].flags & SC_ASN1_PRESENT) { + if(sc_asn1_decode(ctx, RecordInfo, buf_ri, buf_ri_len, NULL, NULL)) + goto error;; + ti->record_info = malloc(sizeof(sc_pkcs15_record_info_t)); + if (ti->record_info == NULL) goto error; + *ti->record_info = ri; + free(buf_ri); } - if (ti->manufacturer_id == NULL) { - if (asn1_toki[2].flags & SC_ASN1_PRESENT) - ti->manufacturer_id = strdup((char *) mnfid); - else - ti->manufacturer_id = strdup("(unknown)"); - if (ti->manufacturer_id == NULL) - return SC_ERROR_OUT_OF_MEMORY; + + /* decode supportedAlgorithms */ + if (TokenInfo[7].flags & SC_ASN1_PRESENT) { + u8 *buf_orig = buf_ai; + sc_pkcs15_algorithm_info_t ai; + ai.parameters.len = 0; /* required as long not parsed (see below (AlgorithmInfo + 2)) */ + + sc_copy_asn1_entry(DEF_AlgorithmInfo, AlgorithmInfo); + sc_format_asn1_entry(AlgorithmInfo + 0, &ai.reference, &l, 0); + sc_format_asn1_entry(AlgorithmInfo + 1, &ai.algorithm, &l, 0); + sc_format_asn1_entry(AlgorithmInfo + 2, NULL, NULL, 0); + sc_format_asn1_entry(AlgorithmInfo + 3, &ai.operations, NULL, 0); + sc_format_asn1_entry(AlgorithmInfo + 4, &ai.oid, &l, 0); + sc_format_asn1_entry(AlgorithmInfo + 5, &ai.alg_ref, &l, 0); + + while (buf_ai_len) { + if(decode_sequence(ctx, AlgorithmInfo, buf_ai, buf_ai_len, (const u8 **) &buf_ai, &buf_ai_len)) + goto error; + + /* fix OPTIONAL attributes, if they are not present */ + if ((AlgorithmInfo[4].flags & SC_ASN1_PRESENT) == 0) + ai.oid.value[0] = -1; + if ((AlgorithmInfo[5].flags & SC_ASN1_PRESENT) == 0) + ai.alg_ref = -1; + + /* append one item to ti->algorithm_infos */ + int i = -1; while (ti->algorithm_infos[++i] != NULL); + ti->algorithm_infos = realloc(ti->algorithm_infos, sizeof(void *) * (i + 2)); + if (ti->algorithm_infos == NULL) goto error; + ti->algorithm_infos[i + 0] = malloc(sizeof(sc_pkcs15_algorithm_info_t)); + if (ti->algorithm_infos[i + 0] == NULL) goto error; + ti->algorithm_infos[i + 1] = NULL; + + /* copy current info to new item of list */ + *ti->algorithm_infos[i + 0] = ai; + } + free(buf_orig); } - if (ti->label == NULL) { - if (asn1_toki[3].flags & SC_ASN1_PRESENT || - asn1_toki[4].flags & SC_ASN1_PRESENT) - ti->label = strdup((char *) label); - else - ti->label = strdup("(unknown)"); - if (ti->label == NULL) - return SC_ERROR_OUT_OF_MEMORY; + + /* fix LastUpdate */ + if ((TokenInfo[10].flags & SC_ASN1_PRESENT) && (LastUpdate[0].flags & SC_ASN1_PRESENT)) { + ti->last_update = realloc(ti->last_update, l_update + 1); + if (ti->last_update == NULL) goto error; + ti->last_update[l_update] = '\0'; } - if (asn1_toki[11].flags & SC_ASN1_PRESENT) { - ti->last_update = strdup((char *)last_update); - if (ti->last_update == NULL) - return SC_ERROR_OUT_OF_MEMORY; - } - if (asn1_toki[12].flags & SC_ASN1_PRESENT) { - preferred_language[2] = 0; - ti->preferred_language = strdup((char *)preferred_language); - if (ti->preferred_language == NULL) - return SC_ERROR_OUT_OF_MEMORY; - } + return SC_SUCCESS; + +error: + /* TODO: free allocated memory */ + return SC_ERROR_INTERNAL; } int sc_pkcs15_encode_tokeninfo(sc_context_t *ctx, @@ -195,10 +227,9 @@ int r; size_t serial_len, mnfid_len, label_len, flags_len, last_upd_len; - struct sc_asn1_entry asn1_toki[14], asn1_tokeninfo[2]; + struct sc_asn1_entry asn1_toki[14]; - sc_copy_asn1_entry(c_asn1_toki, asn1_toki); - sc_copy_asn1_entry(c_asn1_tokeninfo, asn1_tokeninfo); + sc_copy_asn1_entry(DEF_TokenInfo, asn1_toki); sc_format_asn1_entry(asn1_toki + 0, &ti->version, NULL, 1); if (ti->serial_number != NULL) { u8 serial[128]; @@ -223,22 +254,30 @@ sc_format_asn1_entry(asn1_toki + 3, NULL, NULL, 0); if (ti->flags) { flags_len = sizeof(ti->flags); - sc_format_asn1_entry(asn1_toki + 5, &ti->flags, &flags_len, 1); + sc_format_asn1_entry(asn1_toki + 4, &ti->flags, &flags_len, 1); } else - sc_format_asn1_entry(asn1_toki + 5, NULL, NULL, 0); + sc_format_asn1_entry(asn1_toki + 4, NULL, NULL, 0); + sc_format_asn1_entry(asn1_toki + 5, NULL, NULL, 0); sc_format_asn1_entry(asn1_toki + 6, NULL, NULL, 0); sc_format_asn1_entry(asn1_toki + 7, NULL, NULL, 0); sc_format_asn1_entry(asn1_toki + 8, NULL, NULL, 0); sc_format_asn1_entry(asn1_toki + 9, NULL, NULL, 0); - sc_format_asn1_entry(asn1_toki + 10, NULL, NULL, 0); if (ti->last_update != NULL) { last_upd_len = strlen(ti->last_update); - sc_format_asn1_entry(asn1_toki + 11, ti->last_update, &last_upd_len, 1); - } else - sc_format_asn1_entry(asn1_toki + 11, NULL, NULL, 0); - sc_format_asn1_entry(asn1_toki + 12, NULL, NULL, 0); - sc_format_asn1_entry(asn1_tokeninfo, asn1_toki, NULL, 1); + struct sc_asn1_entry LastUpdate[3]; + sc_copy_asn1_entry(DEF_LastUpdate, LastUpdate); + sc_format_asn1_entry(LastUpdate + 0, ti->last_update, &last_upd_len, 1); + sc_format_asn1_entry(asn1_toki + 10, &LastUpdate, NULL, 1); + } else { + sc_format_asn1_entry(asn1_toki + 10, NULL, NULL, 0); + } + sc_format_asn1_entry(asn1_toki + 11, NULL, NULL, 0); + struct sc_asn1_entry asn1_tokeninfo[] = { + { "TokenInfo", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_TAG_SEQUENCE, SC_ASN1_PRESENT, asn1_toki, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; + r = sc_asn1_encode(ctx, asn1_tokeninfo, buf, buflen); if (r) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "sc_asn1_encode() failed: %s", sc_strerror(r)); @@ -462,7 +501,10 @@ return NULL; } + p15card->tokeninfo->se_infos = calloc(1, sizeof(void *)); + p15card->tokeninfo->algorithm_infos = calloc(1, sizeof(void *)); p15card->magic = SC_PKCS15_CARD_MAGIC; + return p15card; } @@ -507,17 +549,24 @@ free(p15card->tokeninfo->last_update); if (p15card->tokeninfo->preferred_language != NULL) free(p15card->tokeninfo->preferred_language); - if (p15card->tokeninfo->seInfo != NULL) { - for (i = 0; i < p15card->tokeninfo->num_seInfo; i++) - free(p15card->tokeninfo->seInfo[i]); - free(p15card->tokeninfo->seInfo); - } + i = -1; + while (p15card->tokeninfo->se_infos[++i]) + free(p15card->tokeninfo->se_infos[i]); + free(p15card->tokeninfo->se_infos); + if (p15card->tokeninfo->record_info != NULL) + free(p15card->tokeninfo->record_info); + i = -1; + while (p15card->tokeninfo->algorithm_infos[++i]) + free(p15card->tokeninfo->algorithm_infos[i]); + free(p15card->tokeninfo->algorithm_infos); free(p15card->tokeninfo); free(p15card); } void sc_pkcs15_card_clear(sc_pkcs15_card_t *p15card) { + size_t i; + if (p15card == NULL) return; @@ -573,14 +622,20 @@ free(p15card->tokeninfo->preferred_language); p15card->tokeninfo->preferred_language = NULL; } - if (p15card->tokeninfo->seInfo != NULL) { - size_t i; - for (i = 0; i < p15card->tokeninfo->num_seInfo; i++) - free(p15card->tokeninfo->seInfo[i]); - free(p15card->tokeninfo->seInfo); - p15card->tokeninfo->seInfo = NULL; - p15card->tokeninfo->num_seInfo = 0; + i = -1; + while (p15card->tokeninfo->se_infos[++i]) + free(p15card->tokeninfo->se_infos[i]); + free(p15card->tokeninfo->se_infos); + p15card->tokeninfo->se_infos = calloc(1, sizeof(void *)); + if (p15card->tokeninfo->record_info != NULL) { + free(p15card->tokeninfo->record_info); + p15card->tokeninfo->record_info = NULL; } + i = -1; + while (p15card->tokeninfo->algorithm_infos[++i]) + free(p15card->tokeninfo->algorithm_infos[i]); + free(p15card->tokeninfo->algorithm_infos); + p15card->tokeninfo->algorithm_infos = calloc(1, sizeof(void *)); } static int sc_pkcs15_bind_internal(sc_pkcs15_card_t *p15card) @@ -591,7 +646,6 @@ sc_path_t tmppath; sc_card_t *card = p15card->card; sc_context_t *ctx = card->ctx; - sc_pkcs15_tokeninfo_t tokeninfo; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "trying normal pkcs15 processing"); @@ -699,6 +753,8 @@ } } +p15card->df_list = NULL; + if (p15card->file_tokeninfo == NULL) { tmppath = p15card->file_app->path; sc_append_path_id(&tmppath, (const u8 *) "\x50\x32", 2); @@ -721,30 +777,11 @@ err = sc_read_binary(card, 0, buf, len, 0); if (err < 0) goto end; - if (err <= 2) { - err = SC_ERROR_PKCS15_APP_NOT_FOUND; - goto end; - } - - memset(&tokeninfo, 0, sizeof(tokeninfo)); - err = sc_pkcs15_parse_tokeninfo(ctx, &tokeninfo, buf, (size_t)err); + len = err; + err = sc_pkcs15_parse_tokeninfo(ctx, p15card->tokeninfo, buf, len); if (err != SC_SUCCESS) goto end; - *(p15card->tokeninfo) = tokeninfo; - - if (!p15card->tokeninfo->serial_number && card->serialnr.len) { - char *serial = calloc(1, card->serialnr.len*2 + 1); - size_t ii; - - for(ii=0;ii<card->serialnr.len;ii++) - sprintf(serial + ii*2, "%02X", *(card->serialnr.value + ii)); - - p15card->tokeninfo->serial_number = serial; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "p15card->tokeninfo->serial_number %s", - p15card->tokeninfo->serial_number); - } - ok = 1; end: if(buf != NULL) Index: libopensc/pkcs15.h =================================================================== --- libopensc/pkcs15.h (revision 4829) +++ libopensc/pkcs15.h (working copy) @@ -416,18 +416,38 @@ size_t aid_len; } sc_pkcs15_sec_env_info_t; +typedef struct sc_pkcs15_record_info { + int od_record_length; + int prkd_record_length; + int pukd_record_length; + int skd_record_length; + int cd_record_length; + int dcod_record_length; + int aod_record_length; +} sc_pkcs15_record_info_t; + +typedef struct sc_pkcs15_algorithm_info { + int reference; /* used to link with keys */ + int algorithm; /* possibly references a PKCS#11 mechanism (CKM_*) */ + sc_pkcs15_der_t parameters; /* parameters of algorithm */ + unsigned int operations; /* bitmap of supported operations */ + struct sc_object_id oid; /* OID of algorithm OPTIONAL */ + int alg_ref; /* algorithm reference according ISO 7816-4 OPTIONAL */ +} sc_pkcs15_algorithm_info_t; + typedef struct sc_pkcs15_tokeninfo { - unsigned int version; + int version; + char *serial_number; + char *manufacturer_id; + char *label; unsigned int flags; - char *label; - char *serial_number; - char *manufacturer_id; + sc_pkcs15_sec_env_info_t **se_infos; + sc_pkcs15_record_info_t *record_info; + sc_pkcs15_algorithm_info_t **algorithm_infos; + char *issuer_id; + char *holder_id; char *last_update; char *preferred_language; - sc_pkcs15_sec_env_info_t **seInfo; - size_t num_seInfo; - - struct sc_supported_algo_info supported_algos[SC_MAX_SUPPORTED_ALGORITHMS]; } sc_pkcs15_tokeninfo_t; struct sc_pkcs15_operations { Index: libopensc/types.h =================================================================== --- libopensc/types.h (revision 4829) +++ libopensc/types.h (working copy) @@ -40,11 +40,6 @@ #define SC_MAX_PATH_SIZE 16 #define SC_MAX_PATH_STRING_SIZE (SC_MAX_PATH_SIZE * 2 + 1) -/* When changing this value, pay attention to the initialization of the ASN1 - * static variables that use this macro, like, for example, - * 'c_asn1_supported_algorithms' in src/libopensc/pkcs15.c - */ -#define SC_MAX_SUPPORTED_ALGORITHMS 8 struct sc_object_id { Index: libopensc/asn1.c =================================================================== --- libopensc/asn1.c (revision 4829) +++ libopensc/asn1.c (working copy) @@ -1040,6 +1040,27 @@ sc_debug(ctx, SC_LOG_DEBUG_ASN1, "%*.*sdecoding '%s'\n", depth, depth, "", entry->name); switch (entry->type) { + case SC_ASN1_SEQUENCE: + if (parm != NULL) { + assert(len != NULL); + if (entry->flags & SC_ASN1_ALLOC) { + u8 **buf = (u8 **) parm; + *buf = malloc(objlen); + if (*buf == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + break; + } + *len = objlen; + parm = *buf; + } else if (*len >= objlen) { + *len = objlen; + } else { + r = SC_ERROR_BUFFER_TOO_SMALL; + break; + } + memcpy(parm, obj, objlen); + } + break; case SC_ASN1_STRUCT: if (parm != NULL) r = asn1_decode(ctx, (struct sc_asn1_entry *) parm, obj, @@ -1092,7 +1113,7 @@ break; case SC_ASN1_BIT_FIELD: if (parm != NULL) - r = decode_bit_field(obj, objlen, (u8 *) parm, *len); + r = decode_bit_field(obj, objlen, (u8 *) parm, sizeof(unsigned int)); break; case SC_ASN1_OCTET_STRING: if (parm != NULL) { @@ -1209,6 +1230,30 @@ return 0; } +/* Maps TYPE to TAG (if possible) */ + +static int asn1_foo(int type, int tag) +{ + if (tag == 0) { + if (type == SC_ASN1_BIT_FIELD || type == SC_ASN1_BIT_STRING_NI) { + tag = SC_ASN1_BIT_STRING; + } else if (type == SC_ASN1_SEQUENCE) { + tag = SC_ASN1_CONS | SC_ASN1_SEQUENCE; + } else if (type > 0 && type < 0x1F) { + tag = type; + } else if (type == SC_ASN1_CHOICE) { + tag = 0; + } else { + tag = -1; + } + } else if ((type == SC_ASN1_SEQUENCE || type == SC_ASN1_SET || type == SC_ASN1_CHOICE) && + ((tag & SC_ASN1_APP) || (tag & SC_ASN1_CTX))) { + tag |= SC_ASN1_CONS; + } + + return tag; +} + static int asn1_decode(sc_context_t *ctx, struct sc_asn1_entry *asn1, const u8 *in, size_t len, const u8 **newp, size_t *len_left, int choice, int depth) @@ -1218,6 +1263,8 @@ struct sc_asn1_entry *entry = asn1; size_t left = len, objlen; + assert(asn1 != NULL); + sc_debug(ctx, SC_LOG_DEBUG_ASN1, "%*.*scalled, left=%u, depth %d%s\n", depth, depth, "", left, depth, @@ -1247,13 +1294,24 @@ entry->name, entry->tag, choice? ", CHOICE" : "", (entry->flags & SC_ASN1_OPTIONAL)? ", OPTIONAL": ""); - /* Special case CHOICE has no tag */ - if (entry->type == SC_ASN1_CHOICE) { + /* complete tag */ + r = asn1_foo(entry->type, entry->tag); + if (r < 0) + goto decode_ok; + entry->tag = r; + r = 0; + + /* Special case CHOICE sometimes has no tag */ + if ((entry->type == SC_ASN1_CHOICE) && (entry->tag == 0)) { r = asn1_decode(ctx, (struct sc_asn1_entry *) entry->parm, p, left, &p, &left, 1, depth + 1); - if (r >= 0) + if (r >= 0) { + entry->flags |= SC_ASN1_PRESENT; r = 0; + } else if (entry->flags & SC_ASN1_OPTIONAL || choice) { + continue; + } goto decode_ok; } @@ -1278,8 +1336,22 @@ } SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_ASN1, SC_ERROR_ASN1_OBJECT_NOT_FOUND); } - r = asn1_decode_entry(ctx, entry, obj, objlen, depth); + if (entry->type == SC_ASN1_CHOICE) { + /* Case with explicit tagging (usualaly of context class). + Tag was present, verified and skipped. Current tag + belongs to choice item. */ + + r = asn1_decode(ctx, (struct sc_asn1_entry *) entry->parm, + obj, objlen, &obj, &objlen, 1, depth + 1); + if (r >= 0) { + entry->flags |= SC_ASN1_PRESENT; + r = 0; + } + } else { + r = asn1_decode_entry(ctx, entry, obj, objlen, depth); + } + decode_ok: if (r) return r; @@ -1322,6 +1394,13 @@ callback_func = parm; + /* complete tag */ + r = asn1_foo(entry->type, entry->tag); + if (r < 0) + return r; + ((struct sc_asn1_entry *) entry)->tag = r; + r = 0; + sc_debug(ctx, SC_LOG_DEBUG_ASN1, "%*.*sencoding '%s'%s\n", depth, depth, "", entry->name, (entry->flags & SC_ASN1_PRESENT)? "" : " (not present)"); @@ -1353,7 +1432,12 @@ } if (choice == NULL) goto no_object; - return asn1_encode_entry(ctx, choice, obj, objlen, depth + 1); + if (entry->tag == 0) + return asn1_encode_entry(ctx, choice, obj, objlen, depth + 1); + r = asn1_encode_entry(ctx, choice, &buf, &buflen, depth + 1); + if (r < 0) + return r; + goto no_object; } if (entry->type != SC_ASN1_NULL && parm == NULL) { Index: libopensc/opensc.h =================================================================== --- libopensc/opensc.h (revision 4829) +++ libopensc/opensc.h (working copy) @@ -115,14 +115,6 @@ #define SC_EVENT_READER_DETACHED 0x0008 #define SC_EVENT_READER_EVENTS SC_EVENT_READER_ATTACHED|SC_EVENT_READER_DETACHED -struct sc_supported_algo_info { - unsigned int reference; - unsigned int mechanism; - unsigned int operations; - struct sc_object_id algo_id; - unsigned int algo_ref; -}; - typedef struct sc_security_env { unsigned long flags; int operation; @@ -132,8 +124,6 @@ struct sc_path file_ref; u8 key_ref[8]; size_t key_ref_len; - - struct sc_supported_algo_info supported_algos[SC_MAX_SUPPORTED_ALGORITHMS]; } sc_security_env_t; struct sc_algorithm_id {
_______________________________________________ opensc-devel mailing list opensc-devel@lists.opensc-project.org http://www.opensc-project.org/mailman/listinfo/opensc-devel