* These modifiers nor format is not documented anywhere, but ActivClient expects them and cards happily answer them * This is a kind of more compressed form of other ACA buffers, but it is extended with some additional values of unknown meaning. * This is somehow consistent with the standard GET ACR parameters, but if P1 | 0x40 is set, the response should come in this new format. * This affects also GET PROPERTIES APDU, where we get also other bunch of TLVs in case of this bit is set. Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx> Reviewed-by: Robert Relyea <rrelyea@xxxxxxxxxx> --- src/cac-aca.c | 293 ++++++++++++++++++++++++++++++++++++++++++++-- src/cac-aca.h | 9 +- src/cac.c | 253 +++++++++++++++++++++++++++++++++------ src/cac.h | 6 +- tests/common.c | 72 +++++++++++- tests/libcacard.c | 56 +++++++++ 6 files changed, 637 insertions(+), 52 deletions(-) diff --git a/src/cac-aca.c b/src/cac-aca.c index aae06a1..a915689 100644 --- a/src/cac-aca.c +++ b/src/cac-aca.c @@ -370,6 +370,7 @@ cac_aca_get_service_table(size_t *r_len, unsigned int pki_applets) * Can be pulled from existing card using the OpenSC: * $ opensc-tool -s 00A4040007A0000000790300 -s 804C100000 */ + enum { ACR_INS_CONFIG_NONE = 0x00, ACR_INS_CONFIG_P1 = 0x01, @@ -950,6 +951,16 @@ struct amp_table amp_table = { } }; +static unsigned char amp_table_extended[] = { + 0x1F, 0x00, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x79, 0x03, 0x00, + 0x12, 0x00, 0x00, 0x00, + /* Sometimes it can be 1E 00 07 A0 00 00 00 79 03 00 10 00 00 00 */ + 0x1E, 0x00, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x79, 0x03, 0x00, + 0x10, 0x01, 0x00, 0x00, + 0x1D, 0x00, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x79, 0x03, 0x00, + 0x01, 0x00, 0x00, 0x00, +}; + static struct simpletlv_member * cac_aca_get_amp(size_t *amp_len) { @@ -1013,10 +1024,72 @@ cac_aca_get_properties(size_t *properties_len) return aca_properties; } +/* + * This is ACR table in undocumented compressed form + * + * |ACRID ACRType AMPid:keyID + * 06 00 00 00 00 00 00 | 00 00 + * 06 01 01 00 01 00 00 | 01 01 + * 06 02 00 1F 00 00 00 | 02 00 + * 06 06 06 00 1E 00 00 | 06 06 1E:00 + * 06 04 04 1F 1F 21 00 | 04 04 1F:21 + * 08 08 03 00 9D 01 1E 01 00 | 08 03 9D:01 1E:01 + * 06 09 02 00 1D 02 00 | 09 02 1D:02 + * 08 0A 03 00 9D 03 1E 01 00 | 0A 03 9D:03 1E:01 + * 06 0B 02 00 1D 04 00 | 0B 02 1D:04 + * 06 10 00 1E 00 00 00 | 10 00 + * 06 11 00 1D 00 00 00 | 11 00 + * len | | ? | | + * ACRID | ? | | + * ACRType | | + * AMPid | + * KeyIdOrReference + */ +static VCardResponse * +cac_aca_get_acr_response_extended(VCard *card, int Le, unsigned char *acrid) +{ + size_t buffer_len; + unsigned char *buffer = NULL, *p; + VCardResponse *r = NULL; + size_t i, j; + buffer_len = acr_table.num_entries * (7 + 2 * (MAX_ACCESS_METHODS - 1)); + buffer = g_malloc_n(buffer_len, sizeof(unsigned char)); + p = buffer; -VCardResponse * -cac_aca_get_acr_response(VCard *card, int Le, unsigned char *acrid) + for (i = 0; i < acr_table.num_entries; i++) { + struct acr_entry *a = &acr_table.entries[i]; + g_assert_cmpint(a->num_access_methods, <=, MAX_ACCESS_METHODS); + *p++ = a->num_access_methods == 2 ? 0x08 : 0x06; + *p++ = a->acrid; + *p++ = a->acrtype; + *p++ = a->applet_id; + + for (j = 0; j < a->num_access_methods; j++) { + *p++ = a->access_methods[j].provider_id; + *p++ = a->access_methods[j].keyIDOrReference; + } + if (a->num_access_methods == 0) { + *p++ = 0x00; + *p++ = 0x00; + } + *p++ = 0x00; + } + g_assert((unsigned long)(p - buffer) <= buffer_len); + buffer_len = (p - buffer); + + r = vcard_response_new(card, buffer, buffer_len, Le, + VCARD7816_STATUS_SUCCESS); + g_debug("%s: response bytes: %s", __func__, + hex_dump(buffer, buffer_len, NULL, 0)); + g_free(buffer); + return r; +} + + + +static VCardResponse * +cac_aca_get_acr_response_simpletlv(VCard *card, int Le, unsigned char *acrid) { size_t acr_buffer_len; unsigned char *acr_buffer = NULL; @@ -1061,9 +1134,21 @@ failure: } VCardResponse * -cac_aca_get_applet_acr_response(VCard *card, int Le, unsigned int pki_applets, - unsigned char *aid, unsigned int aid_len, - unsigned char *coid) +cac_aca_get_acr_response(VCard *card, int Le, unsigned char *acrid, int format) +{ + if (format == CAC_FORMAT_SIMPLETLV) { + return cac_aca_get_acr_response_simpletlv(card, Le, acrid); + } else { + return cac_aca_get_acr_response_extended(card, Le, acrid); + } +} + +static VCardResponse * +cac_aca_get_applet_acr_response_simpletlv(VCard *card, int Le, + unsigned int pki_applets, + unsigned char *aid, + unsigned int aid_len, + unsigned char *coid) { size_t acr_buffer_len; unsigned char *acr_buffer = NULL; @@ -1080,7 +1165,7 @@ cac_aca_get_applet_acr_response(VCard *card, int Le, unsigned int pki_applets, if (coid != NULL) { g_debug("%s: Called. COID = %s", __func__, hex_dump(coid, 2, NULL, 0)); - /* getting the table for COID (2B) */ + /* getting the table for Card Object ID (2B) */ acr_len = 1; // returns exactly one element if found acr = cac_aca_get_applet_acr_coid(pki_applets, coid); if (!acr) { @@ -1125,8 +1210,112 @@ failure: return r; } +/* + * This is Applet/Object ACR Table in undocumented compressed format. + * + * Len + * | Applet Id + * | | Num Objects + * | | | Len [OID] INS+Config+ACRID + * 1C 1F 01 + * 19 FF FF + * 26 80 00 + * 2C 80 04 + * 24 81 80 04 + * 24 81 01 00 + * 24 80 06 + * 20 00 00 + * D8 80 04 + * 24 4F 02 + * 08 DB 00 + * 58 00 04 + * 52 00 00 + * 18 FF FF + * 82 01 00 11 + * 50 80 02 + * 82 80 02 + * F0 80 04 + * 34 80 04 + * 84 00 11 + * 20 00 10 + * [...] + */ +static VCardResponse * +cac_aca_get_applet_acr_response_extended(VCard *card, int Le, + unsigned int pki_applets, + unsigned char *aid, + unsigned int aid_len, + unsigned char *coid) +{ + size_t buffer_len, i, j, plen; + unsigned char *buffer = NULL, *p; + VCardResponse *r = NULL; + unsigned int num_applets = applets_table.num_static_applets + pki_applets; + + buffer_len = num_applets * (3 + ACR_MAX_APPLET_OBJECTS + * ( 3 + ACR_MAX_INSTRUCTIONS * 6)); + buffer = g_malloc_n(buffer_len, sizeof(unsigned char)); + p = buffer; + plen = buffer_len; + + for (i = 0; i < applets_table.num_applets; i++) { + struct acr_applet *a; + unsigned char *len; + /* Skip unused PKI applets */ + if (i >= pki_applets && i < 10) + continue; + + a = &applets_table.applets[i]; + if (plen < 3) + goto failure; + len = p++; /* here we will store the length later */ + *p++ = a->id; + *p++ = a->num_objects; + plen -= 3; + for (j = 0; j < a->num_objects; j++) { + struct acr_object *o = &a->objects[j]; + unsigned char *len2; + unsigned int olen; + + len2 = p++; /* here we will store the length later */ + /* the encoding from here on is the same as in specification */ + p = acr_applet_object_encode(o, p, plen, &olen); + if (!p) + goto failure; + plen -= olen; + *len2 = olen; + } + *len = (p - len - 1); + } + g_assert((unsigned long)(p - buffer) <= buffer_len); + buffer_len = (p - buffer); + + r = vcard_response_new(card, buffer, buffer_len, Le, + VCARD7816_STATUS_SUCCESS); + g_debug("%s: response bytes: %s", __func__, + hex_dump(buffer, buffer_len, NULL, 0)); +failure: + g_free(buffer); + return r; +} + VCardResponse * -cac_aca_get_amp_response(VCard *card, int Le) +cac_aca_get_applet_acr_response(VCard *card, int Le, unsigned int pki_applets, + unsigned char *aid, unsigned int aid_len, + unsigned char *coid, int format) +{ + if (format == CAC_FORMAT_SIMPLETLV) { + return cac_aca_get_applet_acr_response_simpletlv(card, Le, + pki_applets, aid, aid_len, coid); + } else { + return cac_aca_get_applet_acr_response_extended(card, Le, + pki_applets, aid, aid_len, coid); + } +} + + +static VCardResponse * +cac_aca_get_amp_response_simpletlv(VCard *card, int Le) { size_t amp_buffer_len; unsigned char *amp_buffer = NULL; @@ -1168,7 +1357,84 @@ failure: } VCardResponse * -cac_aca_get_service_response(VCard *card, int Le, unsigned int pki_applets) +cac_aca_get_amp_response(VCard *card, int Le, int format) +{ + if (format == CAC_FORMAT_SIMPLETLV) { + return cac_aca_get_amp_response_simpletlv(card, Le); + } else { + /* 1F 00 07 A0 00 00 00 79 03 00 12 00 00 00 + * 1E 00 07 A0 00 00 00 79 03 00 10 01 00 00 + * 1D 00 07 A0 00 00 00 79 03 00 01 00 00 00 + * LEN [ AID ] [OID] + */ + return vcard_response_new(card, amp_table_extended, + sizeof(amp_table_extended), Le, VCARD7816_STATUS_SUCCESS); + } +} + +/* + * This is Service Applet table in undocumented compressed format + * + * Applet ID + * | Len [ AID ] + * 40 00 07 A0 00 00 01 16 30 00 + * 4F 00 07 A0 00 00 01 16 DB 00 + * 4B 00 07 A0 00 00 00 79 02 FB + * 41 00 07 A0 00 00 00 79 02 00 + * 42 00 07 A0 00 00 00 79 02 01 + * 4E 00 07 A0 00 00 00 79 02 FE + * 4D 00 07 A0 00 00 00 79 02 FD + * 50 00 07 A0 00 00 00 79 02 F2 + * 63 00 07 A0 00 00 00 79 01 02 + * 51 00 07 A0 00 00 00 79 02 F0 + * 61 00 07 A0 00 00 00 79 01 00 + * 52 00 07 A0 00 00 00 79 02 F1 + * 62 00 07 A0 00 00 00 79 01 01 + * 44 00 07 A0 00 00 00 79 12 01 + * 45 00 07 A0 00 00 00 79 12 02 + */ +static VCardResponse * +cac_aca_get_service_response_extended(VCard *card, int Le, + unsigned int pki_applets) +{ + size_t buffer_len; + unsigned char *buffer = NULL, *p; + VCardResponse *r = NULL; + size_t num_entries, i; + + num_entries = service_table.num_static_entries + pki_applets; + + buffer_len = num_entries * (3 + MAX_AID_LEN); + buffer = g_malloc(buffer_len); + p = buffer; + + for (i = 0; i < service_table.num_entries; i++) { + struct applet_entry *e; + /* Skip unused PKI applets */ + if (i >= pki_applets && i < 10) + continue; + + e = &service_table.entries[i]; + *p++ = e->applet_id; + *p++ = 0; + *p++ = e->applet_aid_len; + memcpy(p, e->applet_aid, e->applet_aid_len); + p += e->applet_aid_len; + } + g_assert((unsigned long)(p - buffer) <= buffer_len); + buffer_len = (p - buffer); + + r = vcard_response_new(card, buffer, buffer_len, Le, + VCARD7816_STATUS_SUCCESS); + g_debug("%s: response bytes: %s", __func__, + hex_dump(buffer, buffer_len, NULL, 0)); + g_free(buffer); + return r; +} + +static VCardResponse * +cac_aca_get_service_response_simpletlv(VCard *card, int Le, + unsigned int pki_applets) { size_t service_buffer_len; unsigned char *service_buffer = NULL; @@ -1209,4 +1475,15 @@ failure: return r; } +VCardResponse * +cac_aca_get_service_response(VCard *card, int Le, + unsigned int pki_applets, int format) +{ + if (format == CAC_FORMAT_SIMPLETLV) { + return cac_aca_get_service_response_simpletlv(card, Le, pki_applets); + } else { + return cac_aca_get_service_response_extended(card, Le, pki_applets); + } +} + /* vim: set ts=4 sw=4 tw=0 noet expandtab: */ diff --git a/src/cac-aca.h b/src/cac-aca.h index cb3a606..34dde2d 100644 --- a/src/cac-aca.h +++ b/src/cac-aca.h @@ -19,14 +19,15 @@ #include <string.h> VCardResponse * -cac_aca_get_acr_response(VCard *card, int Le, unsigned char *acrid); +cac_aca_get_acr_response(VCard *card, int Le, unsigned char *acrid, int format); VCardResponse * cac_aca_get_applet_acr_response(VCard *card, int Le, unsigned int pki_applets, unsigned char *aid, unsigned int aid_len, - unsigned char *coid); + unsigned char *coid, int format); VCardResponse * -cac_aca_get_amp_response(VCard *card, int Le); +cac_aca_get_amp_response(VCard *card, int Le, int format); VCardResponse * -cac_aca_get_service_response(VCard *card, int Le, unsigned int pki_applets); +cac_aca_get_service_response(VCard *card, int Le, unsigned int pki_applets, + int format); diff --git a/src/cac.c b/src/cac.c index d2a519b..f133a93 100644 --- a/src/cac.c +++ b/src/cac.c @@ -4,6 +4,15 @@ * Adaptation to GSC-IS 2.1: * https://nvlpubs.nist.gov/nistpubs/Legacy/IR/nistir6887e2003.pdf * + * and NIST SP 800-73-4 + * https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-73-4.pdf + * + * Copyright 2010 - 2018 Red Hat, Inc. + * + * Authors: Robert Relyea <rrelyea@xxxxxxxxxx> + * Alon Levy <alevy@xxxxxxxxxx> + * Jakub Jelen <jjelen@xxxxxxxxxx> + * * This code is licensed under the GNU LGPL, version 2.1 or later. * See the COPYING file in the top-level directory. */ @@ -86,6 +95,7 @@ struct VCardAppletPrivateStruct { int val_buffer_len; struct simpletlv_member *properties; unsigned int properties_len; + unsigned int long_properties_len; /* TODO we should also keep a state, which OID is selected, * but it does not matter now, because we do not have anything different * in either buffer @@ -263,6 +273,7 @@ cac_common_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response) ret = VCARD_DONE; break; } + switch (apdu->a_p1) { case 0x00: /* Get a GSC-IS v2.0 compatible properties response message. */ @@ -278,8 +289,11 @@ cac_common_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response) ret = VCARD_DONE; break; } + /* TODO the properties buffer should be shorter for P1 = 0x01 */ + *response = get_properties(card, applet_private->properties, applet_private->properties_len, NULL, 0, apdu->a_Le); + break; case 0x02: /* Get the properties of the tags provided in list of tags in @@ -293,12 +307,20 @@ cac_common_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response) *response = get_properties(card, applet_private->properties, applet_private->properties_len, apdu->a_body, apdu->a_Lc, apdu->a_Le); break; + case 0x40: + /* XXX This is undocumented P1 argument, which returns properties + * extended with some more values of unknown meaning. + */ + *response = get_properties(card, applet_private->properties, + applet_private->long_properties_len, NULL, 0, apdu->a_Le); + break; default: /* unknown params returns (SW1=0x6A, SW2=0x86) */ *response = vcard_make_response( VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); break; } + ret = VCARD_DONE; break; case VCARD7816_INS_SELECT_FILE: @@ -341,7 +363,7 @@ cac_common_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response) break; default: *response = vcard_make_response( - VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); + VCARD7816_STATUS_ERROR_INS_CODE_INVALID); ret = VCARD_DONE; break; } @@ -489,6 +511,7 @@ cac_applet_pki_process_apdu(VCard *card, VCardAPDU *apdu, if (apdu->a_p2 != 0) { *response = vcard_make_response( VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); + ret = VCARD_DONE; break; } size = apdu->a_Lc; @@ -548,6 +571,7 @@ cac_applet_aca_process_apdu(VCard *card, VCardAPDU *apdu, VCardStatus ret = VCARD_FAIL; CACACAAppletData *aca_applet; VCardAppletPrivate *applet_private; + int format; applet_private = vcard_get_current_applet_private(card, apdu->a_channel); assert(applet_private); @@ -565,15 +589,23 @@ cac_applet_aca_process_apdu(VCard *card, VCardAPDU *apdu, ret = VCARD_DONE; break; } + + format = ((apdu->a_p1 & 0x40) + ? CAC_FORMAT_EXTENDED + : CAC_FORMAT_SIMPLETLV); + switch (apdu->a_p1) { case 0x00: + case 0x40: + case 0x41: /* This one returns the same as 0x40 for some reason */ /* All ACR table entries are to be extracted */ if (apdu->a_Lc != 0) { *response = vcard_make_response( VCARD7816_STATUS_ERROR_DATA_INVALID); break; } - *response = cac_aca_get_acr_response(card, apdu->a_Le, NULL); + *response = cac_aca_get_acr_response(card, apdu->a_Le, NULL, + format); break; case 0x01: @@ -583,9 +615,14 @@ cac_applet_aca_process_apdu(VCard *card, VCardAPDU *apdu, VCARD7816_STATUS_ERROR_DATA_INVALID); break; } - *response = cac_aca_get_acr_response(card, apdu->a_Le, apdu->a_body); + *response = cac_aca_get_acr_response(card, apdu->a_Le, + apdu->a_body, format); break; + case 0x10: + case 0x50: + case 0x51: /* returns the same as 0x50 for some reason */ + case 0x52: /* returns the same as 0x50 for some reason */ /* All Applet/Object ACR table entries are to be extracted */ if (apdu->a_Lc != 0) { *response = vcard_make_response( @@ -593,7 +630,7 @@ cac_applet_aca_process_apdu(VCard *card, VCardAPDU *apdu, break; } *response = cac_aca_get_applet_acr_response(card, apdu->a_Le, - aca_applet->pki_applets, NULL, 0, NULL); + aca_applet->pki_applets, NULL, 0, NULL, format); break; case 0x11: @@ -605,7 +642,8 @@ cac_applet_aca_process_apdu(VCard *card, VCardAPDU *apdu, break; } *response = cac_aca_get_applet_acr_response(card, apdu->a_Le, - aca_applet->pki_applets, apdu->a_body, apdu->a_Lc, NULL); + aca_applet->pki_applets, apdu->a_body, apdu->a_Lc, NULL, + format); break; case 0x12: @@ -617,19 +655,22 @@ cac_applet_aca_process_apdu(VCard *card, VCardAPDU *apdu, break; } *response = cac_aca_get_applet_acr_response(card, apdu->a_Le, - aca_applet->pki_applets, NULL, 0, apdu->a_body); + aca_applet->pki_applets, NULL, 0, apdu->a_body, format); break; case 0x20: + case 0x60: /* The Access Method Provider table is extracted. */ if (apdu->a_Lc != 0) { *response = vcard_make_response( VCARD7816_STATUS_ERROR_DATA_INVALID); break; } - *response = cac_aca_get_amp_response(card, apdu->a_Le); + *response = cac_aca_get_amp_response(card, apdu->a_Le, format); break; + case 0x21: + case 0x61: /* The Service Applet table is extracted. */ if (apdu->a_Lc != 0) { *response = vcard_make_response( @@ -637,8 +678,9 @@ cac_applet_aca_process_apdu(VCard *card, VCardAPDU *apdu, break; } *response = cac_aca_get_service_response(card, apdu->a_Le, - aca_applet->pki_applets); + aca_applet->pki_applets, format); break; + default: *response = vcard_make_response( VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); @@ -646,6 +688,15 @@ cac_applet_aca_process_apdu(VCard *card, VCardAPDU *apdu, } ret = VCARD_DONE; break; + /* XXX unknown APDU from ActivClient after PIN verification */ + case 0x5A: + /* ??? + * 80 5A 00 00 00 + * HW response: 90 00 + */ + *response = vcard_make_response(VCARD7816_STATUS_SUCCESS); + ret = VCARD_DONE; + break; default: ret = cac_common_process_apdu(card, apdu, response); break; @@ -721,7 +772,7 @@ cac_delete_pki_applet_private(VCardAppletPrivate *applet_private) g_free(applet_private->val_buffer); g_free(applet_private->coids); /* this one is cloned so needs to be freed */ - simpletlv_free(applet_private->properties, applet_private->properties_len); + simpletlv_free(applet_private->properties, applet_private->long_properties_len); if (pki_applet_data->key != NULL) { vcard_emul_delete_key(pki_applet_data->key); } @@ -814,33 +865,64 @@ cac_new_pki_applet_private(int i, const unsigned char *cert, * 10 Key length bytes /8 * 01 Private key initialized * 01 Public key initialized + * + * Long Properties: + * PKI applet: + * 01 05 Applet Information + * 10 02 06 02 03 + * 40 01 Number of objects + * 01 + * 51 14 First PKI Object + * 41 02 Object ID + * 01 02 + * 42 05 Buffer properties + * 00 1E 00 57 05 + * 43 04 PKI Properties + * 06 10 01 00 + * 26 01 ??? + * 01 + * 39 01 + * 00 + * 3A 07 ACA ??? + * A0 00 00 00 79 03 00 */ unsigned char object_id[] = "\x01\x00"; unsigned char buffer_properties[] = "\x00\x00\x00\x00\x00"; unsigned char pki_properties[] = "\x06\x10\x01\x01"; - static struct simpletlv_member pki_object[3] = { + unsigned char buffer_26[] = "\x01"; + static struct simpletlv_member pki_object[] = { {CAC_PROPERTIES_OBJECT_ID, 2, {/*.value = object_id*/}, SIMPLETLV_TYPE_LEAF}, {CAC_PROPERTIES_BUFFER_PROPERTIES, 5, {/*.value = buffer_properties*/}, SIMPLETLV_TYPE_LEAF}, {CAC_PROPERTIES_PKI_PROPERTIES, 4, {/*.value = pki_properties*/}, SIMPLETLV_TYPE_LEAF}, + {0x26, 0x01, {/*.child = buffer_26*/}, SIMPLETLV_TYPE_LEAF}, }; unsigned char applet_information[] = "\x10\x02\x06\x02\x03"; unsigned char number_objects[] = "\x01"; - static struct simpletlv_member properties[3] = { + unsigned char buffer_39[] = "\x00"; + unsigned char aca_aid[] = "\xA0\x00\x00\x00\x79\x03\x00"; + static struct simpletlv_member properties[] = { {CAC_PROPERTIES_APPLET_INFORMATION, 5, {/*.value = applet_information*/}, SIMPLETLV_TYPE_LEAF}, {CAC_PROPERTIES_NUMBER_OBJECTS, 1, {/*.value = number_objects */}, SIMPLETLV_TYPE_LEAF}, - {CAC_PROPERTIES_PKI_OBJECT, 3, {/*.child = pki_object*/}, + {CAC_PROPERTIES_PKI_OBJECT, 4, {/*.child = pki_object*/}, SIMPLETLV_TYPE_COMPOUND}, + {0x39, 0x01, {/*.child = buffer_39*/}, SIMPLETLV_TYPE_LEAF}, + {0x3A, 0x07, {/*.child = aca_aid*/}, SIMPLETLV_TYPE_LEAF}, }; /* if this would be 1, the certificate would be compressed */ unsigned char certinfo[] = "\x00"; struct simpletlv_member buffer[] = { - {CAC_PKI_TAG_CERTINFO, 1, {/*.value = certinfo*/}, SIMPLETLV_TYPE_LEAF}, - {CAC_PKI_TAG_CERTIFICATE, cert_len, {/*.value = cert*/}, SIMPLETLV_TYPE_LEAF}, + {CAC_PKI_TAG_CERTINFO, 1, {/*.value = certinfo*/}, + SIMPLETLV_TYPE_LEAF}, + {CAC_PKI_TAG_CERTIFICATE, cert_len, {/*.value = cert*/}, + SIMPLETLV_TYPE_LEAF}, + {CAC_PKI_TAG_MSCUID, 0, {/*.value = NULL*/}, SIMPLETLV_TYPE_LEAF}, + {CAC_PKI_TAG_ERROR_DETECTION_CODE, 0, {/*.value = NULL*/}, + SIMPLETLV_TYPE_LEAF}, }; applet_private = g_new0(VCardAppletPrivate, 1); @@ -863,6 +945,8 @@ cac_new_pki_applet_private(int i, const unsigned char *cert, /* Tag+Len buffer */ buffer[0].value.value = certinfo; buffer[1].value.value = (unsigned char *)cert; + buffer[2].value.value = NULL; + buffer[3].value.value = NULL; /* Ex: * 0A 00 Length of whole buffer * 71 Tag: CertInfo @@ -915,15 +999,20 @@ cac_new_pki_applet_private(int i, const unsigned char *cert, if (bits > 0) pki_properties[1] = 0xff & (bits / 8 / 8); pki_object[2].value.value = pki_properties; + pki_object[3].value.value = buffer_26; /* Inject Applet Version */ properties[0].value.value = applet_information; properties[1].value.value = number_objects; properties[2].value.child = pki_object; + properties[3].value.value = buffer_39; + properties[4].value.value = aca_aid; /* Clone the properties */ applet_private->properties_len = 3; - applet_private->properties = simpletlv_clone(properties, 3); + applet_private->long_properties_len = 5; + applet_private->properties = simpletlv_clone(properties, + applet_private->long_properties_len); if (applet_private->properties == NULL) goto failure; @@ -1322,36 +1411,38 @@ failure: * 00 * 0E 00 4E 04 * - * OID buffers: - * :3000 + * OID buffers (from NIST SP 800-73-4) + * :3000: Card Holder Unique Identifier * Tag buffer: * 0C 00 - * 30 19 SEIWG data - * 34 10 ?? list of 0x30 - * 35 08 ?? 32 30 31 32 30 34 30 31 - * 3E FF 1A 06 ?? + * 30 19 FASC + * 34 10 GUID + * 35 08 Expiration Date + * 3E FF 1A 06 Issuer Asymmetric Signature * FE 00 Error detection code * Value buffer: * D4 F8 10 DA 08 26 6C 10 A2 04 E5 83 60 DA 01 0C * 11 CE 66 62 84 38 10 93 E1 <-- SEIWG data * - * :6010 + * :6010: Cardholder Fingerprints * Tag buffer: * 06 00 - * BC FF CF 04 ?? + * BC FF CF 04 Fingerprint I & II * FE 00 Error Detection Code * - * :6030 + * :6030: Cardholder Facial Image * Tag buffer: * 06 00 - * BC FF A9 29 ?? + * BC FF A9 29 Image for Visual Verification * FE 00 Error Detection Code * - * :9000 + * :9000: Security Object * 08 00 - * BB FF 38 02 Some PKCS#7 signed block - * BA 30 ??? + * BB FF 38 02 Security Object + * BA 30 Mapping of DG to ContainerID * FE 00 Error Detectionc Code + * + * we should do this as passthrough too */ @@ -1381,21 +1472,27 @@ cac_new_ccc_applet_private(int cert_count) */ static unsigned char object_id[] = "\xDB\x00"; static unsigned char buffer_properties[] = "\x00\x00\x00\x00\x00"; - static struct simpletlv_member tv_object[2] = { + static unsigned char buffer_26[] = "\x01"; + static struct simpletlv_member tv_object[3] = { {CAC_PROPERTIES_OBJECT_ID, 2, {/*.value = object_id*/}, SIMPLETLV_TYPE_LEAF}, {CAC_PROPERTIES_BUFFER_PROPERTIES, 5, {/*.value = buffer_properties*/}, SIMPLETLV_TYPE_LEAF}, + {0x26, 0x01, {/*.value = buffer_26*/}, SIMPLETLV_TYPE_LEAF}, }; static unsigned char applet_information[] = "\x10\x02\x06\x02\x03"; static unsigned char number_objects[] = "\x01"; - static struct simpletlv_member properties[4] = { + static unsigned char buffer_39[] = "\x00"; + static unsigned char aca_aid[] = "\xA0\x00\x00\x00\x79\x03\x00"; + static struct simpletlv_member properties[5] = { {CAC_PROPERTIES_APPLET_INFORMATION, 5, {/*.value = applet_information*/}, SIMPLETLV_TYPE_LEAF}, {CAC_PROPERTIES_NUMBER_OBJECTS, 1, {/*.value = number_objects */}, SIMPLETLV_TYPE_LEAF}, {CAC_PROPERTIES_TV_OBJECT, 2, {/*.child = tv_object*/}, SIMPLETLV_TYPE_COMPOUND}, + {0x39, 0x01, {/*.child = buffer_39*/}, SIMPLETLV_TYPE_LEAF}, + {0x3A, 0x07, {/*.child = aca_aid*/}, SIMPLETLV_TYPE_LEAF}, }; unsigned char card_identifier[] = "\xA0\x00\x00\x00\x79\x03\x02\x40\x70\x50" @@ -1440,6 +1537,8 @@ cac_new_ccc_applet_private(int cert_count) */ }; unsigned char pkcs15[] = "\x00"; + /* NIST SP 800-73-4: The data model of the PIV Card Application shall + * be identified by data model number 0x10 */ unsigned char reg_data_model[] = "\x10"; unsigned char acr_table[] = "\x07\xA0\x00\x00\x00\x79\x03\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00"; @@ -1632,15 +1731,19 @@ cac_new_ccc_applet_private(int cert_count) ushort2lebytes(&buffer_properties[1], applet_private->tag_buffer_len); ushort2lebytes(&buffer_properties[3], applet_private->val_buffer_len); tv_object[1].value.value = buffer_properties; + tv_object[2].value.value = buffer_26; /* Inject Applet Version */ properties[0].value.value = applet_information; properties[1].value.value = number_objects; properties[2].value.child = tv_object; + properties[3].value.value = buffer_39; + properties[4].value.value = aca_aid; /* Link the properties */ applet_private->properties = properties; - applet_private->properties_len = 4; + applet_private->properties_len = 3; + applet_private->long_properties_len = 5; return applet_private; @@ -1695,14 +1798,87 @@ cac_new_aca_applet_private(int cert_count) * 10 Applet family * 02 06 02 02 Applet version */ + + /* 0x40 Long properties (?): + * 01 05 Applet Information + * 10 02 06 02 02 + * 23 0C + * 20 11 00 00 00 00 00 00 00 00 00 58 + * 2A 02 + * 02 00 + * 2B 05 RID ??? + * A0 00 00 00 79 + * 22 10 + * 00 00 00 DA 01 B4 03 30 00 00 00 70 00 51 02 9D + * 2C 01 + * 00 + * 20 02 + * 08 01 + * 21 01 + * 0F + * 32 04 + * 03 03 03 03 + * 30 01 + * 08 + * 31 04 + * 06 08 00 00 + * 24 01 + * FF + * 25 02 + * 01 00 + * 26 01 + * 01 + */ + static unsigned char applet_information[] = "\x10\x02\x06\x02\x02"; - static struct simpletlv_member properties[1] = { + static unsigned char buffer_23[] = "\x20\x11\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x58"; + static unsigned char buffer_2A[] = "\x02\x00"; + static unsigned char rid[] = "\xA0\x00\x00\x00\x79"; + static unsigned char buffer_22[] = "\x00\x00\x00\xDA\x01\xB4\x03\x30\x00" + "\x00\x00\x70\x00\x51\x02\x9D"; + static unsigned char buffer_2C[] = "\x00"; + static unsigned char buffer_20[] = "\x08\x01"; + static unsigned char buffer_21[] = "\x0F"; + static unsigned char buffer_32[] = "\x03\x03\x03\x03"; + static unsigned char buffer_30[] = "\x08"; + static unsigned char buffer_31[] = "\x06\x08\x00\x00"; + static unsigned char buffer_24[] = "\xFF"; + static unsigned char buffer_25[] = "\x01\x00"; + static unsigned char buffer_26[] = "\x01"; + static struct simpletlv_member properties[] = { {CAC_PROPERTIES_APPLET_INFORMATION, 5, {/*.value = applet_information*/}, SIMPLETLV_TYPE_LEAF}, + {0x23, 0x0C, {/*.value = buffer_23*/}, SIMPLETLV_TYPE_LEAF}, + {0x2A, 0x02, {/*.value = buffer_2A*/}, SIMPLETLV_TYPE_LEAF}, + {0x2B, 0x05, {/*.value = rid*/}, SIMPLETLV_TYPE_LEAF}, + {0x22, 0x10, {/*.value = buffer_22*/}, SIMPLETLV_TYPE_LEAF}, + {0x2C, 0x01, {/*.value = buffer_2C*/}, SIMPLETLV_TYPE_LEAF}, + {0x20, 0x02, {/*.value = buffer_20*/}, SIMPLETLV_TYPE_LEAF}, + {0x21, 0x01, {/*.value = buffer_21*/}, SIMPLETLV_TYPE_LEAF}, + {0x32, 0x04, {/*.value = buffer_32*/}, SIMPLETLV_TYPE_LEAF}, + {0x30, 0x01, {/*.value = buffer_30*/}, SIMPLETLV_TYPE_LEAF}, + {0x31, 0x04, {/*.value = buffer_31*/}, SIMPLETLV_TYPE_LEAF}, + {0x24, 0x01, {/*.value = buffer_24*/}, SIMPLETLV_TYPE_LEAF}, + {0x25, 0x02, {/*.value = buffer_25*/}, SIMPLETLV_TYPE_LEAF}, + {0x26, 0x01, {/*.value = buffer_26*/}, SIMPLETLV_TYPE_LEAF}, }; /* Inject Applet Version into Applet information */ properties[0].value.value = applet_information; + properties[1].value.value = buffer_23; + properties[2].value.value = buffer_2A; + properties[3].value.value = rid; + properties[4].value.value = buffer_22; + properties[5].value.value = buffer_2C; + properties[6].value.value = buffer_20; + properties[7].value.value = buffer_21; + properties[8].value.value = buffer_32; + properties[9].value.value = buffer_30; + properties[10].value.value = buffer_31; + properties[11].value.value = buffer_24; + properties[12].value.value = buffer_25; + properties[13].value.value = buffer_26; /* Create the private data structure */ applet_private = g_new0(VCardAppletPrivate, 1); @@ -1719,6 +1895,7 @@ cac_new_aca_applet_private(int cert_count) /* Link the properties */ applet_private->properties = properties; applet_private->properties_len = 1; + applet_private->long_properties_len = 14; aca_applet_data->pki_applets = cert_count; @@ -1809,6 +1986,7 @@ cac_new_empty_applet_private(unsigned char objects[][2], unsigned int objects_le /* Clone the properties */ applet_private->properties_len = properties_len; + applet_private->long_properties_len = properties_len; /*TODO*/ applet_private->properties = simpletlv_clone(properties, properties_len); if (applet_private->properties == NULL) goto failure; @@ -1845,11 +2023,13 @@ cac_new_passthrough_applet_private(VCard *card, const char *label, unsigned char object_id[] = "\x00\x00"; unsigned char buffer_properties[] = "\x00\x00\x00\x00\x00"; - static struct simpletlv_member tv_buffer[2] = { + static unsigned char buffer_26[] = "\x01"; + static struct simpletlv_member tv_buffer[3] = { {CAC_PROPERTIES_OBJECT_ID, 2, {/*.value = object_id*/}, SIMPLETLV_TYPE_LEAF}, {CAC_PROPERTIES_BUFFER_PROPERTIES, 5, {/*.value = buffer_properties*/}, SIMPLETLV_TYPE_LEAF}, + {0x26, 0x01, {/*.value = buffer_26*/}, SIMPLETLV_TYPE_LEAF}, }; unsigned char applet_information[] = "\x10\x02\x06\x02\x03"; unsigned char number_objects[] = "\x01"; @@ -1858,7 +2038,7 @@ cac_new_passthrough_applet_private(VCard *card, const char *label, SIMPLETLV_TYPE_LEAF}, {CAC_PROPERTIES_NUMBER_OBJECTS, 1, {/*.value = number_objects*/}, SIMPLETLV_TYPE_LEAF}, - {CAC_PROPERTIES_TV_OBJECT, 2, {/*.child = tv_buffer*/}, + {CAC_PROPERTIES_TV_OBJECT, 3, {/*.child = tv_buffer*/}, SIMPLETLV_TYPE_COMPOUND}, }; @@ -1889,6 +2069,7 @@ cac_new_passthrough_applet_private(VCard *card, const char *label, /* Inject Object ID */ tv_buffer[0].value.value = object_id; tv_buffer[1].value.value = buffer_properties; + tv_buffer[2].value.value = buffer_26; /* Inject Applet Version */ properties[0].value.value = applet_information; @@ -1897,7 +2078,9 @@ cac_new_passthrough_applet_private(VCard *card, const char *label, /* Clone the properties */ applet_private->properties_len = 3; - applet_private->properties = simpletlv_clone(properties, 3); + applet_private->long_properties_len = 3; /*TODO*/ + applet_private->properties = simpletlv_clone(properties, + applet_private->long_properties_len); if (applet_private->properties == NULL) goto failure; diff --git a/src/cac.h b/src/cac.h index ff8fa0e..db8af2f 100644 --- a/src/cac.h +++ b/src/cac.h @@ -1,5 +1,5 @@ /* - * defines the entry point for the cac card. Only used by cac.c anc + * defines the entry point for the cac card. Only used by cac.c and * vcard_emul_type.c * * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. @@ -64,6 +64,10 @@ #define CAC_PROPERTIES_TV_OBJECT 0x50 #define CAC_PROPERTIES_PKI_OBJECT 0x51 +/* Buffer formats */ +#define CAC_FORMAT_SIMPLETLV 1 +#define CAC_FORMAT_EXTENDED 2 + /* * Initialize the cac card. This is the only public function in this file. All diff --git a/tests/common.c b/tests/common.c index 839c42b..a70ec89 100644 --- a/tests/common.c +++ b/tests/common.c @@ -83,8 +83,8 @@ int select_aid_response(VReader *reader, unsigned char *aid, g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_RESPONSE_BYTES); g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], >, 0); } else { - g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_SUCCESS); - g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x00); + g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_RESPONSE_BYTES); + g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x0d); } return pbRecvBuffer[dwRecvLength-2]; } @@ -101,17 +101,29 @@ void get_properties_coid(VReader *reader, const unsigned char coid[2], VReaderStatus status; uint8_t pbRecvBuffer[APDUBufSize], *p, *p_end, *p2, *p2_end; uint8_t get_properties[] = { - /* Get properties */ + /* Get properties [Le] */ 0x80, 0x56, 0x01, 0x00, 0x00 }; uint8_t get_properties_tag[] = { - /* Get properties [tag list] */ + /* Get properties [tag list] [Le] */ 0x80, 0x56, 0x02, 0x00, 0x02, 0x01, 0x01, 0x00 }; int verified_pki_properties = 0; int num_objects = 0, num_objects_expected = -1; int have_applet_information = 0; + status = vreader_xfr_bytes(reader, + get_properties, sizeof(get_properties), + pbRecvBuffer, &dwRecvLength); + g_assert_cmpint(status, ==, VREADER_OK); + /* for too long Le, the cards return LE_ERROR with correct length to ask */ + g_assert_cmpint(dwRecvLength, ==, 2); + g_assert_cmpint(pbRecvBuffer[0], ==, VCARD7816_SW1_LE_ERROR); + g_assert_cmpint(pbRecvBuffer[1], !=, 0x00); + + /* Update the APDU to match Le field from response and resend */ + get_properties[4] = pbRecvBuffer[1]; + dwRecvLength = APDUBufSize; status = vreader_xfr_bytes(reader, get_properties, sizeof(get_properties), pbRecvBuffer, &dwRecvLength); @@ -190,6 +202,11 @@ void get_properties_coid(VReader *reader, const unsigned char coid[2], verified_pki_properties = 1; break; + case 0x26: + g_assert_cmpint(vlen2, ==, 1); + g_assert_cmphex(p2[0], ==, 0x01); + break; + default: g_debug("Unknown tag in object: 0x%02x", tag2); g_assert_not_reached(); @@ -199,6 +216,16 @@ void get_properties_coid(VReader *reader, const unsigned char coid[2], /* one more object processed */ num_objects++; break; + + case 0x39: + g_assert_cmpint(vlen, ==, 1); + g_assert_cmphex(p[0], ==, 0x00); + break; + + case 0x3A: + g_assert_cmpint(vlen, ==, 7); + break; + default: g_debug("Unknown tag in properties buffer: 0x%02x", tag); g_assert_not_reached(); @@ -230,9 +257,46 @@ void get_properties_coid(VReader *reader, const unsigned char coid[2], get_properties_tag, sizeof(get_properties_tag), pbRecvBuffer, &dwRecvLength); g_assert_cmpint(status, ==, VREADER_OK); + g_assert_cmpint(dwRecvLength, ==, 2); + g_assert_cmpint(pbRecvBuffer[0], ==, VCARD7816_SW1_LE_ERROR); + g_assert_cmpint(pbRecvBuffer[1], ==, 0x0e); /* Two applet information buffers */ + + /* Update the APDU to match Le field from response and resend */ + get_properties_tag[7] = pbRecvBuffer[1]; + dwRecvLength = APDUBufSize; + status = vreader_xfr_bytes(reader, + get_properties_tag, sizeof(get_properties_tag), + pbRecvBuffer, &dwRecvLength); + g_assert_cmpint(status, ==, VREADER_OK); g_assert_cmpint(dwRecvLength, ==, 16); /* Two applet information buffers + status */ g_assert_cmpint(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_SUCCESS); g_assert_cmpint(pbRecvBuffer[dwRecvLength-1], ==, 0x00); + + + /* Test the undocumented P1 = 0x40 */ + dwRecvLength = APDUBufSize; + get_properties[2] = 0x40; + get_properties[4] = 0x00; + status = vreader_xfr_bytes(reader, + get_properties, sizeof(get_properties), + pbRecvBuffer, &dwRecvLength); + g_assert_cmpint(status, ==, VREADER_OK); + /* for too long Le, the cards return LE_ERROR with correct length to ask */ + g_assert_cmpint(dwRecvLength, ==, 2); + g_assert_cmpint(pbRecvBuffer[0], ==, VCARD7816_SW1_LE_ERROR); + g_assert_cmpint(pbRecvBuffer[1], !=, 0x00); + + /* Update the APDU to match Le field from response and resend */ + get_properties[4] = pbRecvBuffer[1]; + dwRecvLength = APDUBufSize; + status = vreader_xfr_bytes(reader, + get_properties, sizeof(get_properties), + pbRecvBuffer, &dwRecvLength); + g_assert_cmpint(status, ==, VREADER_OK); + g_assert_cmpint(dwRecvLength, >, 2); + g_assert_cmpint(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_SUCCESS); + g_assert_cmpint(pbRecvBuffer[dwRecvLength-1], ==, 0x00); + } void get_properties(VReader *reader, int object_type) diff --git a/tests/libcacard.c b/tests/libcacard.c index b340038..cd08a54 100644 --- a/tests/libcacard.c +++ b/tests/libcacard.c @@ -386,6 +386,62 @@ static void get_acr(VReader *reader) /* parse the response */ parse_acr(pbRecvBuffer, dwRecvLength); + + /* Undocumented 0x40 returns ACR in different encoding */ + get_acr[2] = 0x40; + dwRecvLength = APDUBufSize; + status = vreader_xfr_bytes(reader, + get_acr, sizeof(get_acr), + pbRecvBuffer, &dwRecvLength); + g_assert_cmpint(status, ==, VREADER_OK); + g_assert_cmpint(dwRecvLength, >, 2); + g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_SUCCESS); + g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x00); + + /* parse the response */ + //parse_acr(pbRecvBuffer, dwRecvLength); + + /* Undocumented 0x50 returns Applet/Object ACR in different encoding */ + get_acr[2] = 0x50; + dwRecvLength = APDUBufSize; + status = vreader_xfr_bytes(reader, + get_acr, sizeof(get_acr), + pbRecvBuffer, &dwRecvLength); + g_assert_cmpint(status, ==, VREADER_OK); + g_assert_cmpint(dwRecvLength, ==, 2); + g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_RESPONSE_BYTES); + g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x00); + + /* parse the response */ + //parse_acr(pbRecvBuffer, dwRecvLength); + + /* Undocumented 0x60 returns AMP in different encoding */ + get_acr[2] = 0x60; + dwRecvLength = APDUBufSize; + status = vreader_xfr_bytes(reader, + get_acr, sizeof(get_acr), + pbRecvBuffer, &dwRecvLength); + g_assert_cmpint(status, ==, VREADER_OK); + g_assert_cmpint(dwRecvLength, >, 2); + g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_SUCCESS); + g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x00); + + /* parse the response */ + //parse_acr(pbRecvBuffer, dwRecvLength); + + /* Undocumented 0x61 returns Service Applet in different encoding */ + get_acr[2] = 0x61; + dwRecvLength = APDUBufSize; + status = vreader_xfr_bytes(reader, + get_acr, sizeof(get_acr), + pbRecvBuffer, &dwRecvLength); + g_assert_cmpint(status, ==, VREADER_OK); + g_assert_cmpint(dwRecvLength, >, 2); + g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_SUCCESS); + g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x00); + + /* parse the response */ + //parse_acr(pbRecvBuffer, dwRecvLength); } static void do_login(VReader *reader) -- 2.17.1 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel