diff --git a/configure.in b/configure.in index ad6ea86c..a2327f72 100644 --- a/configure.in +++ b/configure.in @@ -2,7 +2,7 @@ AC_INIT(libopensc) AC_CONFIG_SRCDIR(src/sc.c) AM_CONFIG_HEADER(config.h:config.h.in) -AM_INIT_AUTOMAKE(libopensc, 0.3.5) +AM_INIT_AUTOMAKE(libopensc, 0.4.0) # Checks for programs. diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am index bd81d804..d7ec2f49 100644 --- a/src/libopensc/Makefile.am +++ b/src/libopensc/Makefile.am @@ -7,7 +7,7 @@ libopensc_la_SOURCES = sc-asn1.c sc-base64.c sc-defaults.c \ sc-pkcs15.c sc-pkcs15-cert.c \ sc-pkcs15-pin.c sc-pkcs15-prkey.c \ sc-pkcs15-defaults.c sc-pkcs15-sec.c -libopensc_la_LDFLAGS = -version-info 0:3:0 +libopensc_la_LDFLAGS = -version-info 0:4:0 libopensc_la_CFLAGS = $(AM_CFLAGS) -Werror include_HEADERS = opensc.h opensc-pkcs15.h -noinst_HEADERS = sc-asn1.h +noinst_HEADERS = sc-asn1.h sc-log.h diff --git a/src/libopensc/asn1.c b/src/libopensc/asn1.c index 49bb9faa..5d05525e 100644 --- a/src/libopensc/asn1.c +++ b/src/libopensc/asn1.c @@ -20,10 +20,12 @@ #include "opensc.h" #include "sc-asn1.h" +#include "sc-log.h" #include #include #include #include +#include const char *tag2str(int tag) { @@ -286,6 +288,50 @@ const u8 *sc_asn1_skip_tag(const u8 ** buf, int *buflen, int tag_in, int *taglen return p; } +static const u8 *sc_asn1_skip_tag2(const u8 ** buf, int *buflen, unsigned int tag_in, int *taglen_out) +{ + const u8 *p = *buf; + int len = *buflen, cla, tag, taglen; + + if (read_tag((const u8 **) &p, len, &cla, &tag, &taglen) != 1) + return NULL; + switch (cla & 0xC0) { + case ASN1_TAG_UNIVERSAL: + if ((tag_in & SC_ASN1_CLASS_MASK) != SC_ASN1_UNI) + return NULL; + break; + case ASN1_TAG_APPLICATION: + if ((tag_in & SC_ASN1_CLASS_MASK) != SC_ASN1_APP) + return NULL; + break; + case ASN1_TAG_CONTEXT: + if ((tag_in & SC_ASN1_CLASS_MASK) != SC_ASN1_CTX) + return NULL; + break; + case ASN1_TAG_PRIVATE: + if ((tag_in & SC_ASN1_CLASS_MASK) != SC_ASN1_PRV) + return NULL; + break; + } + if (cla & ASN1_TAG_CONSTRUCTED) { + if ((tag_in & SC_ASN1_CONS) == 0) + return NULL; + } else + if (tag_in & SC_ASN1_CONS) + return NULL; + if ((tag_in & SC_ASN1_TAG_MASK) != tag) + return NULL; + len -= (p - *buf); /* header size */ + if (taglen > len) { + fprintf(stderr, "skip_tag(): too long tag\n"); + return NULL; + } + *buflen -= (p - *buf) + taglen; + *buf = p + taglen; /* point to next tag */ + *taglen_out = taglen; + return p; +} + const u8 *sc_asn1_verify_tag(const u8 * buf, int buflen, int tag_in, int *taglen_out) { return sc_asn1_skip_tag(&buf, &buflen, tag_in, taglen_out); @@ -302,7 +348,7 @@ static int decode_bit_string(const u8 * inbuf, int inlen, void *outbuf, in++; if (outlen < octets_left) - return SC_ERROR_INVALID_ARGUMENTS; + return SC_ERROR_BUFFER_TOO_SMALL; while (octets_left) { /* 1st octet of input: ABCDEFGH, where A is the MSB */ /* 1st octet of output: HGFEDCBA, where A is the LSB */ @@ -326,7 +372,7 @@ static int decode_bit_string(const u8 * inbuf, int inlen, void *outbuf, octets_left--; count++; } - return (count * 8) - zero_bits; + return (count * 8) - zero_bits; } int sc_asn1_decode_bit_string(const u8 * inbuf, @@ -390,6 +436,17 @@ int sc_asn1_decode_object_id(const u8 * inbuf, int inlen, return 0; } +int sc_asn1_decode_utf8string(const u8 * inbuf, int inlen, + u8 *out, int *outlen) +{ + if (inlen+1 > *outlen) + return SC_ERROR_BUFFER_TOO_SMALL; + *outlen = inlen+1; + memcpy(out, inbuf, inlen); + out[inlen] = 0; + return 0; +} + int sc_asn1_put_tag(int tag, const u8 * data, int datalen, u8 * out, int outlen, u8 **ptr) { u8 *p = out; @@ -411,3 +468,148 @@ int sc_asn1_put_tag(int tag, const u8 * data, int datalen, u8 * out, int outlen, *ptr = p; return 0; } + +int sc_asn1_parse_path(struct sc_context *ctx, const u8 *in, int len, + struct sc_path *path) +{ + int idx, r; + struct sc_asn1_struct asn1_path[] = { + { "path", SC_ASN1_OCTET_STRING, ASN1_OCTET_STRING, 0, &path->value, &path->len }, + { "index", SC_ASN1_INTEGER, ASN1_INTEGER, SC_ASN1_OPTIONAL, &idx }, + { NULL } + }; + path->len = SC_MAX_PATH_SIZE; + r = sc_asn1_parse(ctx, asn1_path, in, len, NULL, NULL); + if (r) + return r; + + return 0; +} + +static int asn1_decode_entry(struct sc_context *ctx, struct sc_asn1_struct *entry, + const u8 *obj, int objlen) +{ + void *parm = entry->parm; + int *len = entry->len; + int r = 0; + + switch (entry->type) { + case SC_ASN1_STRUCT: + assert(parm != NULL); + r = sc_asn1_parse(ctx, (struct sc_asn1_struct *) parm, obj, objlen, NULL, NULL); + break; + case SC_ASN1_INTEGER: + if (parm != NULL) + r = sc_asn1_decode_integer(obj, objlen, (int *) entry->parm); + break; + case SC_ASN1_BIT_STRING: + if (parm != NULL && len != NULL) { + r = sc_asn1_decode_bit_string(obj, objlen, (u8 *) parm, *len); + if (r >= 0) { + *len = r; + r = 0; + } + } + break; + case SC_ASN1_OCTET_STRING: + if (parm != NULL && len != NULL) { + int c = objlen > *len ? *len : objlen; + + memcpy(parm, obj, c); + *len = c; + } + break; + case SC_ASN1_OBJECT: + if (parm != NULL) + r = sc_asn1_decode_object_id(obj, objlen, (struct sc_object_id *) parm); + break; + case SC_ASN1_UTF8STRING: + if (parm != NULL && len != NULL) + r = sc_asn1_decode_utf8string(obj, objlen, parm, len); + break; + case SC_ASN1_PATH: + if (entry->parm != NULL) + r = sc_asn1_parse_path(ctx, obj, objlen, (struct sc_path *) entry->parm); + break; + default: + error(ctx, "invalid ASN.1 type: %d\n", entry->type); + assert(0); + } + if (r) { + error(ctx, "decoding of ASN.1 object failed: %s\n", entry->name); + return r; + } + entry->flags |= SC_ASN1_PRESENT; + return 0; +} + +int sc_asn1_parse(struct sc_context *ctx, struct sc_asn1_struct *asn1, + const u8 *in, int len, const u8 **newp, int *len_left) +{ + int r; + const u8 *p = in, *obj; + struct sc_asn1_struct *entry = asn1; + int left = len, objlen; + + SC_FUNC_CALLED(ctx); + while (entry->name != NULL) { + r = 0; + obj = sc_asn1_skip_tag2(&p, &left, entry->tag, &objlen); + if (obj == NULL) { + if (entry->flags & SC_ASN1_OPTIONAL) { + entry++; + continue; + } + error(ctx, "mandatory ASN.1 object not found: %s\n", entry->name); + return SC_ERROR_ASN1_OBJECT_NOT_FOUND; + } + r = asn1_decode_entry(ctx, entry, obj, objlen); + if (r) + return r; + entry++; + } + if (newp != NULL) + *newp = p; + if (len_left != NULL) + *len_left = left; + return 0; +} + +int sc_asn1_parse_choice(struct sc_context *ctx, struct sc_asn1_struct *asn1, + const u8 *in, int len, const u8 **newp, int *len_left) +{ + int r, idx = 0; + const u8 *p = in, *obj; + struct sc_asn1_struct *entry; + int left = len, objlen; + + SC_FUNC_CALLED(ctx); + entry = asn1; + while (entry->name) { + entry->flags &= ~SC_ASN1_PRESENT; + entry++; + } + if (left < 2) + return SC_ERROR_ASN1_END_OF_CONTENTS; + if (p[0] == 0 && p[1] == 0) + return SC_ERROR_ASN1_END_OF_CONTENTS; + entry = asn1; + while (entry->name) { + r = 0; + obj = sc_asn1_skip_tag2(&p, &left, entry->tag, &objlen); + if (obj == NULL) { + idx++; + entry++; + continue; + } + r = asn1_decode_entry(ctx, entry, obj, objlen); + if (r) + return r; + if (newp != NULL) + *newp = p; + if (len_left != NULL) + *len_left = left; + return idx; + } + return SC_ERROR_ASN1_OBJECT_NOT_FOUND; +} diff --git a/src/libopensc/asn1.h b/src/libopensc/asn1.h index 8f5cb359..748c61ab 100644 --- a/src/libopensc/asn1.h +++ b/src/libopensc/asn1.h @@ -21,8 +21,24 @@ #ifndef _SC_ASN1_H #define _SC_ASN1_H +#include "opensc.h" + +struct sc_asn1_struct { + const char *name; + unsigned int type; + unsigned int tag; + unsigned int flags; + void *parm; + int *len; +}; + /* DER tag and length parsing */ +int sc_asn1_parse(struct sc_context *ctx, struct sc_asn1_struct *asn1, + const u8 *in, int len, const u8 **newp, int *left); +int sc_asn1_parse_choice(struct sc_context *ctx, struct sc_asn1_struct *asn1, + const u8 *in, int len, const u8 **newp, int *left); + const u8 *sc_asn1_find_tag(const u8 * buf, int buflen, int tag, int *taglen); const u8 *sc_asn1_verify_tag(const u8 * buf, int buflen, int tag, int *taglen); const u8 *sc_asn1_skip_tag(const u8 ** buf, int *buflen, int tag, int *taglen); @@ -47,6 +63,38 @@ int sc_asn1_decode_bit_string_ni(const u8 * inbuf, int inlen, int sc_asn1_decode_integer(const u8 * inbuf, int inlen, int *out); int sc_asn1_decode_object_id(const u8 * inbuf, int inlen, struct sc_object_id *id); +#define SC_ASN1_CLASS_MASK 0x30000000 +#define SC_ASN1_UNI 0x00000000 /* Universal */ +#define SC_ASN1_APP 0x10000000 /* Application */ +#define SC_ASN1_CTX 0x20000000 /* Context */ +#define SC_ASN1_PRV 0x30000000 /* Private */ +#define SC_ASN1_CONS 0x01000000 + +#define SC_ASN1_TAG_MASK 0x00FFFFFF + +#define SC_ASN1_PRESENT 0x00000001 +#define SC_ASN1_OPTIONAL 0x00000002 + +#define SC_ASN1_BOOLEAN 1 +#define SC_ASN1_INTEGER 2 +#define SC_ASN1_BIT_STRING 3 +#define SC_ASN1_OCTET_STRING 4 +#define SC_ASN1_NULL 5 +#define SC_ASN1_OBJECT 6 +#define SC_ASN1_ENUMERATED 10 +#define SC_ASN1_UTF8STRING 12 +#define SC_ASN1_SEQUENCE 16 +#define SC_ASN1_SET 17 +#define SC_ASN1_PRINTABLESTRING 19 +#define SC_ASN1_UTCTIME 23 +#define SC_ASN1_GENERALIZEDTIME 24 + +/* internal structures */ +#define SC_ASN1_STRUCT 128 +#define SC_ASN1_CHOICE 129 + +/* PKCS#15 structures */ +#define SC_ASN1_PATH 256 #define ASN1_TAG_CLASS 0xC0 #define ASN1_TAG_UNIVERSAL 0x00 diff --git a/src/libopensc/opensc-pkcs15.h b/src/libopensc/opensc-pkcs15.h index ee33c208..e54d0822 100644 --- a/src/libopensc/opensc-pkcs15.h +++ b/src/libopensc/opensc-pkcs15.h @@ -31,10 +31,14 @@ extern "C" { #define SC_PKCS15_PIN_MAGIC 0x31415926 #define SC_PKCS15_MAX_PINS 2 -#define SC_PKCS15_MAX_CERTS 3 #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_CERTS 4 /* Total certificates */ struct sc_pkcs15_id { u8 value[SC_PKCS15_MAX_ID_SIZE]; @@ -147,12 +151,14 @@ struct sc_pkcs15_card { struct sc_pkcs15_pin_info pin_info[SC_PKCS15_MAX_PINS]; int pin_count; - struct sc_file file_dir, file_ao1, file_app; + 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_aodf, file_ao2; - struct sc_file file_cdf1, file_cdf2, file_cdf3; + 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; }; diff --git a/src/libopensc/opensc.h b/src/libopensc/opensc.h index 263cbd69..99f3d5a5 100644 --- a/src/libopensc/opensc.h +++ b/src/libopensc/opensc.h @@ -56,6 +56,9 @@ extern "C" { #define SC_ERROR_UNKNOWN_REPLY -1023 #define SC_ERROR_OBJECT_NOT_FOUND -1024 #define SC_ERROR_CARD_RESET -1025 +#define SC_ERROR_ASN1_OBJECT_NOT_FOUND -1026 +#define SC_ERROR_ASN1_END_OF_CONTENTS -1027 +#define SC_ERROR_TOO_MANY_OBJECTS -1028 #define SC_APDU_CASE_NONE 0 #define SC_APDU_CASE_1 1 @@ -111,6 +114,7 @@ struct sc_object_id { struct sc_path { u8 value[SC_MAX_PATH_SIZE]; int len; + int index; }; struct sc_file { diff --git a/src/libopensc/pkcs15-cert.c b/src/libopensc/pkcs15-cert.c index d284b522..8361ec69 100644 --- a/src/libopensc/pkcs15-cert.c +++ b/src/libopensc/pkcs15-cert.c @@ -329,25 +329,26 @@ static int get_certs_from_file(struct sc_pkcs15_card *card, int r, taglen, left; u8 buf[MAX_BUFFER_SIZE]; const u8 *tag, *p; - int count = 0; r = sc_select_file(card->card, file, &file->path, SC_SELECT_FILE_BY_PATH); if (r) return r; + if (file->size > sizeof(buf)) + return SC_ERROR_BUFFER_TOO_SMALL; r = sc_read_binary(card->card, 0, buf, file->size); if (r < 0) return r; left = r; p = buf; - count = 0; - while (card->cert_count < SC_PKCS15_MAX_CERTS) { - tag = sc_asn1_skip_tag(&p, &left, 0x30, &taglen); /* SEQUENCE */ - if (tag == NULL) - break; - if (parse_x509_cert_info(&card->cert_info[card->cert_count], tag, taglen)) - break; + while ((tag = sc_asn1_skip_tag(&p, &left, 0x30, &taglen)) != NULL) { + if (card->cert_count >= SC_PKCS15_MAX_CERTS) + return SC_ERROR_TOO_MANY_OBJECTS; + r = parse_x509_cert_info(&card->cert_info[card->cert_count], + tag, taglen); + if (r) + return r; card->cert_count++; } return 0; @@ -355,24 +356,16 @@ static int get_certs_from_file(struct sc_pkcs15_card *card, int sc_pkcs15_enum_certificates(struct sc_pkcs15_card *card) { - int r; + int r, i; assert(card != NULL); if (card->cert_count) return card->cert_count; /* already enumerated */ - - card->cert_count = 0; - r = get_certs_from_file(card, &card->file_cdf1); - if (r != 0) - return r; - if (card->file_cdf2.path.len) { - r = get_certs_from_file(card, &card->file_cdf2); + for (i = 0; i < card->cdf_count; i++) { + r = get_certs_from_file(card, &card->file_cdf[i]); if (r != 0) return r; } - r = get_certs_from_file(card, &card->file_cdf3); - if (r != 0) - return r; return card->cert_count; } diff --git a/src/libopensc/pkcs15-defaults.c b/src/libopensc/pkcs15-defaults.c index 8221751a..57a2b5c5 100644 --- a/src/libopensc/pkcs15-defaults.c +++ b/src/libopensc/pkcs15-defaults.c @@ -99,11 +99,13 @@ static int fineid_pkcs15_defaults(struct sc_pkcs15_card *arg, card->alg_info[0].supported_operations = 0xa2; format_file_struct(&card->file_app, "5015", 7); /* 7 = DF, 0 = EF */ - format_file_struct(&card->file_aodf, "50154401", 0); + card->aodf_count = 1; + format_file_struct(&card->file_aodf[0], "50154401", 0); format_file_struct(&card->file_prkdf, "50154402", 0); - format_file_struct(&card->file_cdf1, "50154403", 0); - format_file_struct(&card->file_cdf2, "50154404", 0); - format_file_struct(&card->file_cdf3, "50154405", 0); + card->cdf_count = 3; + format_file_struct(&card->file_cdf[0], "50154403", 0); + format_file_struct(&card->file_cdf[1], "50154404", 0); + format_file_struct(&card->file_cdf[2], "50154405", 0); format_file_struct(&card->file_dodf, "50154406", 0); format_file_struct(&card->file_odf, "50155031", 0); format_file_struct(&card->file_tokeninfo, "50155032", 0); diff --git a/src/libopensc/pkcs15-pin.c b/src/libopensc/pkcs15-pin.c index 42d7a60c..e2ac5e88 100644 --- a/src/libopensc/pkcs15-pin.c +++ b/src/libopensc/pkcs15-pin.c @@ -21,6 +21,7 @@ #include "opensc.h" #include "opensc-pkcs15.h" #include "sc-asn1.h" +#include "sc-log.h" #include #include #include @@ -119,15 +120,45 @@ void sc_pkcs15_print_pin_info(const struct sc_pkcs15_pin_info *pin) printf("\n"); } +static int get_pins_from_file(struct sc_pkcs15_card *p15card, + struct sc_file *file) +{ + int r, taglen, left; + const u8 *p, *tag; + u8 buf[MAX_BUFFER_SIZE]; + + r = sc_select_file(p15card->card, file, &file->path, + SC_SELECT_FILE_BY_PATH); + if (r) + return r; + if (file->size > sizeof(buf)) + return SC_ERROR_BUFFER_TOO_SMALL; + r = sc_read_binary(p15card->card, 0, buf, file->size); + if (r < 0) + return r; + + left = r; + p = buf; + while ((tag = sc_asn1_skip_tag(&p, &left, 0x30, &taglen)) != NULL) { + if (p15card->pin_count >= SC_PKCS15_MAX_PINS) + return SC_ERROR_TOO_MANY_OBJECTS; + r = decode_pin_info(tag, taglen, + &p15card->pin_info[p15card->pin_count]); + if (r) + return r; + + p15card->pin_count++; + } + return 0; +} + int sc_pkcs15_enum_pins(struct sc_pkcs15_card *p15card) { int r, i; - u8 buf[MAX_BUFFER_SIZE]; - const u8 *tag, *p; - int taglen, buflen; + struct sc_context *ctx = p15card->card->ctx; assert(p15card != NULL); - + SC_FUNC_CALLED(ctx); if (p15card->pin_count) { for (i = 0; i < p15card->pin_count; i++) { if (p15card->pin_info[i].magic != SC_PKCS15_PIN_MAGIC) @@ -136,29 +167,9 @@ int sc_pkcs15_enum_pins(struct sc_pkcs15_card *p15card) if (i == p15card->pin_count) return i; /* Already enumerated */ } - - r = sc_select_file(p15card->card, &p15card->file_aodf, - &p15card->file_aodf.path, - SC_SELECT_FILE_BY_PATH); - if (r) - return r; - r = sc_read_binary(p15card->card, 0, buf, p15card->file_aodf.size); - if (r < 0) - return r; - buflen = r; - p = buf; - i = 0; - p15card->pin_count = 0; - while ((tag = sc_asn1_skip_tag(&p, &buflen, 0x30, &taglen)) != NULL) { - - r = decode_pin_info(tag, taglen, - &p15card->pin_info[p15card-> - pin_count]); - if (r) - break; - p15card->pin_count++; - if (p15card->pin_count >= SC_PKCS15_MAX_PINS) - break; + for (i = 0; i < p15card->aodf_count; i++) { + r = get_pins_from_file(p15card, &p15card->file_aodf[i]); + SC_TEST_RET(ctx, r, "Failed to read PINs from AODF"); } return p15card->pin_count; } diff --git a/src/libopensc/pkcs15.c b/src/libopensc/pkcs15.c index c86dea2c..97c171ba 100644 --- a/src/libopensc/pkcs15.c +++ b/src/libopensc/pkcs15.c @@ -55,84 +55,51 @@ void sc_pkcs15_print_card(const struct sc_pkcs15_card *card) printf("\n"); } -static int extract_path(const u8 * buf, int buflen, struct sc_path *path) -{ - const u8 *tag; - int taglen; - - tag = sc_asn1_verify_tag(buf, buflen, 0x30, &taglen); - if (tag == NULL) - return -1; - tag = sc_asn1_verify_tag(tag, taglen, 0x04, &taglen); /* OCTET STRING */ - if (tag == NULL) - return -1; - memcpy(path->value, tag, taglen); - path->len = taglen; - - return 0; -} - void parse_tokeninfo(struct sc_pkcs15_card *card, const u8 * buf, int buflen) { - const u8 *tag, *p = buf; - int i, taglen, left = buflen; + int i, r; + u8 serial[128]; + int serial_len = sizeof(serial); + u8 mnfid[128]; + int mnfid_len = sizeof(mnfid); + int flags_len = sizeof(card->flags); - p = sc_asn1_verify_tag(buf, buflen, 0x30, &left); /* SEQUENCE */ - if (p == NULL) + struct sc_asn1_struct 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_SEQUENCE, SC_ASN1_CTX | 1, SC_ASN1_OPTIONAL, NULL }, + { "supportedAlgorithms", SC_ASN1_SEQUENCE,SC_ASN1_CTX | 2, SC_ASN1_OPTIONAL, NULL }, + { NULL } }; + + buf = sc_asn1_verify_tag(buf, buflen, 0x30, &buflen); /* SEQUENCE */ + if (buf == NULL) { + error(card->card->ctx, "invalid EF(TokenInfo)\n"); goto err; - tag = sc_asn1_skip_tag(&p, &left, 0x02, &taglen); /* INTEGER */ - if (tag == NULL) + } + r = sc_asn1_parse(card->card->ctx, asn1_tokeninfo, buf, buflen, NULL, NULL); + if (r) { + error(card->card->ctx, "ASN.1 parsing failed: %s\n", sc_strerror(r)); goto err; - card->version = tag[0] + 1; - tag = sc_asn1_skip_tag(&p, &left, 0x04, &taglen); /* OCTET STRING */ - if (tag == NULL) - goto err; - card->serial_number = malloc(taglen * 2 + 1); + } + card->version += 1; + card->serial_number = malloc(serial_len * 2 + 1); card->serial_number[0] = 0; - for (i = 0; i < taglen; i++) { + for (i = 0; i < serial_len; i++) { char byte[3]; - sprintf(byte, "%02X", tag[i]); + sprintf(byte, "%02X", serial[i]); strcat(card->serial_number, byte); } - tag = sc_asn1_skip_tag(&p, &left, 0x0C, &taglen); /* UTF8 STRING */ - if (tag == NULL) - goto err; - card->manufacturer_id = malloc(taglen + 1); - memcpy(card->manufacturer_id, tag, taglen); - card->manufacturer_id[taglen] = 0; - tag = sc_asn1_skip_tag(&p, &left, 0x80, &taglen); /* Label */ - if (tag != NULL) { /* skip this tag */ - } - tag = sc_asn1_skip_tag(&p, &left, 0x03, &taglen); /* BIT STRING */ - if (tag == NULL) - goto err; - sc_asn1_decode_bit_string(tag, taglen, &card->flags, - sizeof(card->flags)); - tag = sc_asn1_find_tag(p, left, 0xA2, &taglen); /* supportedAlgo */ - if (tag == NULL) - goto err; - p = sc_asn1_skip_tag(&tag, &taglen, 0x30, &left); /* SEQUENCE */ - if (p == NULL) - goto err; - tag = sc_asn1_skip_tag(&p, &left, 0x02, &taglen); /* INTEGER */ - if (tag == NULL) - goto err; - card->alg_info[0].reference = tag[0]; - tag = sc_asn1_skip_tag(&p, &left, 0x02, &taglen); /* INTEGER */ - if (tag == NULL) - goto err; - card->alg_info[0].algorithm = tag[0]; - tag = sc_asn1_find_tag(p, left, 0x03, &taglen); /* BIT STRING */ ; - if (tag == NULL) - goto err; - sc_asn1_decode_bit_string(tag, taglen, - &card->alg_info[0].supported_operations, - sizeof(card->alg_info[0]. - supported_operations)); - + if (asn1_tokeninfo[2].flags & SC_ASN1_PRESENT) + card->manufacturer_id = strdup(mnfid); + else + card->manufacturer_id = strdup("(unknown)"); return; - err: +err: if (card->serial_number == NULL) card->serial_number = strdup("(unknown)"); if (card->manufacturer_id == NULL) @@ -142,77 +109,101 @@ void parse_tokeninfo(struct sc_pkcs15_card *card, const u8 * buf, int buflen) static int parse_dir(const u8 * buf, int buflen, struct sc_pkcs15_card *card) { - const u8 *tag; - int taglen; - const char *aid = "\xA0\x00\x00\x00\x63PKCS-15"; - const int aidlen = 12; + const u8 *aidref = "\xA0\x00\x00\x00\x63PKCS-15"; + const int aidref_len = 12; + 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_struct 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_struct 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(buf, buflen, 0x61, &buflen); - if (buf == NULL) + if (buf == NULL) { + error(card->card->ctx, "No [APPLICATION 1] tag in EF(DIR)\n"); return -1; - - tag = sc_asn1_skip_tag(&buf, &buflen, 0x4F, &taglen); - if (taglen != aidlen || memcmp(aid, tag, aidlen) != 0) - return -1; - - tag = sc_asn1_skip_tag(&buf, &buflen, 0x50, &taglen); - if (taglen > 0) { - card->label = malloc(taglen + 1); - if (card->label != NULL) { - memcpy(card->label, tag, taglen); - card->label[taglen] = 0; - } - } else - card->label = strdup("(unknown)"); - tag = sc_asn1_skip_tag(&buf, &buflen, 0x51, &taglen); - if (tag == NULL) - return -1; - memcpy(card->file_app.path.value, tag, taglen); - card->file_app.path.len = taglen; - tag = sc_asn1_skip_tag(&buf, &buflen, 0x73, &taglen); - if (taglen > 2) { - /* FIXME: process DDO */ } + r = sc_asn1_parse(card->card->ctx, asn1_dir, buf, buflen, NULL, NULL); + if (r) { + error(card->card->ctx, "EF(DIR) parsing failed: %s\n", + sc_strerror(r)); + return r; + } + if (aid_len != aidref_len || memcmp(aidref, aid, aid_len) != 0) { + error(card->card->ctx, "AID in EF(DIR) is invalid\n"); + return -1; + } + if (asn1_dir[1].flags & SC_ASN1_PRESENT) + card->label = strdup(label); + else + card->label = strdup("(unknown)"); + memcpy(card->file_app.path.value, path, path_len); + card->file_app.path.len = path_len; + return 0; } static int parse_odf(const u8 * buf, int buflen, struct sc_pkcs15_card *card) { - const u8 *tag; - int taglen; - - /* authObjects */ - if ((tag = sc_asn1_find_tag(buf, buflen, 0xA8, &taglen)) == NULL) - return -1; - if (extract_path(tag, taglen, &card->file_aodf.path)) - return -1; - /* CDF #1 -- Card holder certificates */ - if ((tag = sc_asn1_find_tag(buf, buflen, 0xA4, &taglen)) == NULL) - return -1; - if (extract_path(tag, taglen, &card->file_cdf1.path)) - return -1; - /* CDF #2 -- New certificates */ - tag += taglen; - taglen += 2; - if ((tag = sc_asn1_verify_tag(tag, taglen, 0xA4, &taglen)) == NULL) - return -1; - if (extract_path(tag, taglen, &card->file_cdf2.path)) - return -1; - /* CDF #3 -- Trusted CA certificates */ - if ((tag = sc_asn1_find_tag(buf, buflen, 0xA5, &taglen)) == NULL) - return -1; - if (extract_path(tag, taglen, &card->file_cdf3.path)) - return -1; - /* PrKDF */ - if ((tag = sc_asn1_find_tag(buf, buflen, 0xA0, &taglen)) == NULL) - return -1; - if (extract_path(tag, taglen, &card->file_prkdf.path)) - return -1; - /* DODF */ - if ((tag = sc_asn1_find_tag(buf, buflen, 0xA7, &taglen)) == NULL) - return -1; - if (extract_path(tag, taglen, &card->file_dodf.path)) - return -1; + const u8 *p = buf; + int r, left = buflen; + struct sc_path path; + struct sc_asn1_struct asn1_obj_or_path[] = { + { "path", SC_ASN1_PATH, SC_ASN1_CONS | SC_ASN1_SEQUENCE, 0, &path }, + { NULL } }; + struct sc_asn1_struct 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 }, + }; + + while (left > 0) { + r = sc_asn1_parse_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: + 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"); + break; + } + card->file_cdf[card->cdf_count].path = path; + card->cdf_count++; + break; + case 3: + 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; + } + } return 0; } @@ -274,11 +265,15 @@ int sc_pkcs15_init(struct sc_card *card, err = SC_ERROR_PKCS15_CARD_NOT_FOUND; goto error; } - defaults = find_defaults(buf, err); + if (p15card->card->ctx->use_cache) + defaults = find_defaults(buf, err); if (defaults == NULL) { - memcpy(&tmppath, &p15card->file_app.path, sizeof(struct sc_path)); - memcpy(tmppath.value + tmppath.len, "\x50\x31", 2); - tmppath.len += 2; + if (p15card->file_odf.path.len == 0) { + tmppath = p15card->file_app.path; + memcpy(tmppath.value + tmppath.len, "\x50\x31", 2); + tmppath.len += 2; + } else + tmppath = p15card->file_odf.path; err = sc_select_file(card, &p15card->file_odf, &tmppath, SC_SELECT_FILE_BY_PATH); if (err) @@ -295,9 +290,12 @@ int sc_pkcs15_init(struct sc_card *card, err = SC_ERROR_PKCS15_CARD_NOT_FOUND; goto error; } - tmppath.len -= 2; - memcpy(tmppath.value + tmppath.len, "\x50\x32", 2); - tmppath.len += 2; + if (p15card->file_tokeninfo.path.len == 0) { + tmppath.len -= 2; + memcpy(tmppath.value + tmppath.len, "\x50\x32", 2); + tmppath.len += 2; + } else + tmppath = p15card->file_tokeninfo.path; } else { defaults->defaults_func(p15card, defaults->arg); tmppath = p15card->file_tokeninfo.path; @@ -316,7 +314,7 @@ int sc_pkcs15_init(struct sc_card *card, parse_tokeninfo(p15card, buf, err); *p15card_out = p15card; return 0; - error: +error: free(p15card); return err; } @@ -355,7 +353,6 @@ int sc_pkcs15_parse_common_object_attr(struct sc_pkcs15_common_obj_attr *attr, attr->auth_id.len = 0; /* FIXME: parse rest */ - attr->auth_id.len = 0; attr->user_consent = 0; return 0; diff --git a/src/libopensc/pkcs15.h b/src/libopensc/pkcs15.h index ee33c208..e54d0822 100644 --- a/src/libopensc/pkcs15.h +++ b/src/libopensc/pkcs15.h @@ -31,10 +31,14 @@ extern "C" { #define SC_PKCS15_PIN_MAGIC 0x31415926 #define SC_PKCS15_MAX_PINS 2 -#define SC_PKCS15_MAX_CERTS 3 #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_CERTS 4 /* Total certificates */ struct sc_pkcs15_id { u8 value[SC_PKCS15_MAX_ID_SIZE]; @@ -147,12 +151,14 @@ struct sc_pkcs15_card { struct sc_pkcs15_pin_info pin_info[SC_PKCS15_MAX_PINS]; int pin_count; - struct sc_file file_dir, file_ao1, file_app; + 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_aodf, file_ao2; - struct sc_file file_cdf1, file_cdf2, file_cdf3; + 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; }; diff --git a/src/libopensc/sc-asn1.h b/src/libopensc/sc-asn1.h index 8f5cb359..748c61ab 100644 --- a/src/libopensc/sc-asn1.h +++ b/src/libopensc/sc-asn1.h @@ -21,8 +21,24 @@ #ifndef _SC_ASN1_H #define _SC_ASN1_H +#include "opensc.h" + +struct sc_asn1_struct { + const char *name; + unsigned int type; + unsigned int tag; + unsigned int flags; + void *parm; + int *len; +}; + /* DER tag and length parsing */ +int sc_asn1_parse(struct sc_context *ctx, struct sc_asn1_struct *asn1, + const u8 *in, int len, const u8 **newp, int *left); +int sc_asn1_parse_choice(struct sc_context *ctx, struct sc_asn1_struct *asn1, + const u8 *in, int len, const u8 **newp, int *left); + const u8 *sc_asn1_find_tag(const u8 * buf, int buflen, int tag, int *taglen); const u8 *sc_asn1_verify_tag(const u8 * buf, int buflen, int tag, int *taglen); const u8 *sc_asn1_skip_tag(const u8 ** buf, int *buflen, int tag, int *taglen); @@ -47,6 +63,38 @@ int sc_asn1_decode_bit_string_ni(const u8 * inbuf, int inlen, int sc_asn1_decode_integer(const u8 * inbuf, int inlen, int *out); int sc_asn1_decode_object_id(const u8 * inbuf, int inlen, struct sc_object_id *id); +#define SC_ASN1_CLASS_MASK 0x30000000 +#define SC_ASN1_UNI 0x00000000 /* Universal */ +#define SC_ASN1_APP 0x10000000 /* Application */ +#define SC_ASN1_CTX 0x20000000 /* Context */ +#define SC_ASN1_PRV 0x30000000 /* Private */ +#define SC_ASN1_CONS 0x01000000 + +#define SC_ASN1_TAG_MASK 0x00FFFFFF + +#define SC_ASN1_PRESENT 0x00000001 +#define SC_ASN1_OPTIONAL 0x00000002 + +#define SC_ASN1_BOOLEAN 1 +#define SC_ASN1_INTEGER 2 +#define SC_ASN1_BIT_STRING 3 +#define SC_ASN1_OCTET_STRING 4 +#define SC_ASN1_NULL 5 +#define SC_ASN1_OBJECT 6 +#define SC_ASN1_ENUMERATED 10 +#define SC_ASN1_UTF8STRING 12 +#define SC_ASN1_SEQUENCE 16 +#define SC_ASN1_SET 17 +#define SC_ASN1_PRINTABLESTRING 19 +#define SC_ASN1_UTCTIME 23 +#define SC_ASN1_GENERALIZEDTIME 24 + +/* internal structures */ +#define SC_ASN1_STRUCT 128 +#define SC_ASN1_CHOICE 129 + +/* PKCS#15 structures */ +#define SC_ASN1_PATH 256 #define ASN1_TAG_CLASS 0xC0 #define ASN1_TAG_UNIVERSAL 0x00 diff --git a/src/libopensc/sc.c b/src/libopensc/sc.c index b4f33fa3..ad27e392 100644 --- a/src/libopensc/sc.c +++ b/src/libopensc/sc.c @@ -114,33 +114,33 @@ int sc_hex_to_bin(const char *in, u8 *out, int *outlen) return err; } -int sc_check_apdu(const struct sc_apdu *apdu) +int sc_check_apdu(struct sc_context *ctx, const struct sc_apdu *apdu) { switch (apdu->cse) { case SC_APDU_CASE_1: if (apdu->datalen) - return SC_ERROR_INVALID_ARGUMENTS; + SC_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); break; case SC_APDU_CASE_2_SHORT: if (apdu->datalen) - return SC_ERROR_INVALID_ARGUMENTS; + SC_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); if (apdu->resplen < apdu->le) - return SC_ERROR_INVALID_ARGUMENTS; + SC_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); break; case SC_APDU_CASE_3_SHORT: if (apdu->datalen == 0 || apdu->data == NULL) - return SC_ERROR_INVALID_ARGUMENTS; + SC_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); break; case SC_APDU_CASE_4_SHORT: if (apdu->datalen == 0 || apdu->data == NULL) - return SC_ERROR_INVALID_ARGUMENTS; + SC_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); if (apdu->resplen < apdu->le) - return SC_ERROR_INVALID_ARGUMENTS; + SC_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); break; case SC_APDU_CASE_2_EXT: case SC_APDU_CASE_3_EXT: case SC_APDU_CASE_4_EXT: - return SC_ERROR_NOT_SUPPORTED; + SC_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); } return 0; } @@ -195,6 +195,8 @@ static int sc_transceive_t0(struct sc_card *card, struct sc_apdu *apdu) dwSendLength = data - s; dwRecvLength = apdu->resplen + 2; + if (dwRecvLength > 255) /* FIXME: PC/SC Lite quirk */ + dwRecvLength = 255; if (sc_debug > 3) { char buf[2048]; @@ -239,7 +241,7 @@ int sc_transmit_apdu(struct sc_card *card, struct sc_apdu *apdu) int r; SC_FUNC_CALLED(card->ctx); - r = sc_check_apdu(apdu); + r = sc_check_apdu(card->ctx, apdu); SC_TEST_RET(card->ctx, r, "APDU sanity check failed"); r = sc_transceive_t0(card, apdu); SC_TEST_RET(card->ctx, r, "transceive_t0() failed"); @@ -396,7 +398,9 @@ int sc_select_file(struct sc_card *card, ctx = card->ctx; if (in_path->len > SC_MAX_PATH_SIZE) - return SC_ERROR_INVALID_ARGUMENTS; + SC_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + if (in_path->len == 0) + SC_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); memcpy(path, in_path->value, in_path->len); pathlen = in_path->len; @@ -760,6 +764,9 @@ const char *sc_strerror(int error) "Unknown reply from SmartCard", "Requested object not found", "Card reset" + "Required ASN.1 object not found", + "Premature end of ASN.1 stream", + "Too many objects", }; int nr_errors = sizeof(errors) / sizeof(errors[0]);