* The Access Control Applet is used to discover other applets in the card, discover Access Control Rules for various opperations and provides information about authentication mechanisms. * The ACA provides many structures that are quite independent from the rest of CAC so it is implemented in separate file. * All the structures are annotated with the references to specifications * Implements structures and access functions in ACA Applet * Access Control Rules table * Applet/Object Access Control Rules table * Access Method Provider table * Service Applet Table (from "5.3.3.5 Get ACR APDU" of GSC-IS 2.1) Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx> Reviewed-by: Robert Relyea <rrelyea@xxxxxxxxxx> --- Makefile.am | 2 + docs/libcacard.txt | 1 + src/cac-aca.c | 1106 ++++++++++++++++++++++++++++++++++++++++++++ src/cac-aca.h | 32 ++ src/cac.c | 204 ++++++++ src/cac.h | 12 + 6 files changed, 1357 insertions(+) create mode 100644 src/cac-aca.c create mode 100644 src/cac-aca.h diff --git a/Makefile.am b/Makefile.am index 4dae691..af99a33 100644 --- a/Makefile.am +++ b/Makefile.am @@ -5,6 +5,7 @@ lib_LTLIBRARIES = libcacard.la libcacard_la_SOURCES = \ src/cac.c \ + src/cac-aca.c \ src/gp.c \ src/capcsc.h \ src/card_7816.c \ @@ -27,6 +28,7 @@ endif libcacard_includedir = $(includedir)/cacard libcacard_include_HEADERS = \ src/cac.h \ + src/cac-aca.h \ src/gp.h \ src/card_7816.h \ src/card_7816t.h \ diff --git a/docs/libcacard.txt b/docs/libcacard.txt index a6c9307..b6ae392 100644 --- a/docs/libcacard.txt +++ b/docs/libcacard.txt @@ -475,6 +475,7 @@ src/vreadert.h - comon virtual reader types. src/vcard_emul_type.c - manage the card type emulators. src/vcard_emul_type.h - definitions for card type emulators. src/cac.c - card type emulator for CAC cards +src/cac-aca.c - implementation of CAC's ACA applet related buffers src/gp.c - basic Global Platform card manager emulation src/vcard_emul.h - virtual card emulator service definitions. src/vcard_emul_nss.c - virtual card emulator implementation for nss. diff --git a/src/cac-aca.c b/src/cac-aca.c new file mode 100644 index 0000000..e303ffa --- /dev/null +++ b/src/cac-aca.c @@ -0,0 +1,1106 @@ +/* + * implement the ACA applet for the CAC card. + * + * Adaptation to GSC-IS 2.1: + * https://nvlpubs.nist.gov/nistpubs/Legacy/IR/nistir6887e2003.pdf + * + * Copyright 2018 Red Hat, Inc. + * + * Author: 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. + */ + +#include "glib-compat.h" +#include "card_7816t.h" +#include "card_7816.h" +#include "common.h" +#include "cac-aca.h" +#include "simpletlv.h" + +#include <string.h> + +#define MAX_ACCESS_METHODS 2 + +/* From Table 3-2 */ +enum ACRType { + ACR_ALWAYS = 0x00, + ACR_NEVER = 0x01, + ACR_XAUTH = 0x02, + ACR_XAUTH_OR_PIN = 0x03, + ACR_SECURE_CHANNEL_GP = 0x04, + ACR_ACR_PIN_ALWAYS = 0x05, + ACR_PIN = 0x06, + ACR_XAUTH_THEN_PIN = 0x07, + ACR_UPDATE_ONCE = 0x08, + ACR_PIN_THEN_XAUTH = 0x09, + /* RFU = 0x0A,*/ + ACR_SECURE_CHANNEL_ISO = 0x0B, + ACR_XAUTH_AND_PIN = 0x0C +}; + +/* + * ACR table: + * This table maps the Access Control Rule Type (ACRType) and Access Method + * information to the Access Control Rule Identifier (ACRID) for each + * Access Control Rule. + * (from 5.3.3.5 Get ACR APDU, Table 5-15) + */ +struct acr_access_method { + unsigned char provider_id; + unsigned char keyIDOrReference; +}; +struct acr_entry { + unsigned char acrid; + unsigned char acrtype; + unsigned int num_access_methods; + struct acr_access_method access_methods[MAX_ACCESS_METHODS]; +}; +struct acr_table { + unsigned int num_entries; + struct acr_entry entries[]; +}; + +/* + * Example: + * 01 05 TL: Applet family + Applet Version + * 10 02 06 02 02 (1B) (4B) + * A1 01 TL: Number of ACR entries (unique ACRID) + * 0B V: 11 + * A0 03 TL: First ACR entry + * 00 V: ACRID of ACR entry + * 00 V: ACRType: BSI_ACR_ALWAYS + * 00 V: Number of AccessMethods in this ACR + * A0 03 + * 01 V: ACRID of ACR entry + * 01 V: ACRType: BSI_ACR_NEVER + * 00 V: Number of AccessMethods in this ACR + * A0 03 + * 02 V: ACRID of ACR entry + * 00 V: ACRType: BSI_ACR_ALWAYS + * 00 V: Number of AccessMethods in this ACR + * A0 05 TL: Next ACR entry + * 06 V: ACRID of ACR entry + * 06 V: ACRType: BSI_ACR_PIN + * 01 V: Number of AccessMethods in this ACR + * 1E V: First AccessMethodProviderID -- PIN ??? + * 00 V: First keyIDOrReference -- id 00 ? + * A0 05 + * 04 V: ACRID of ACR entry + * 04 V: ACRType: BSI_SECURE_CHANNEL_GP + * 01 V: Number of AccessMethods in this ACR + * 1F V: First AccessMethodProviderID -- SC ??? + * 21 V: First keyIDOrReference -- ref 21 ?? + * A0 07 + * 08 V: ACRID of ACR entry + * 03 V: ACRType: BSI_ACR_XAUTH_OR_PIN + * 02 V: Number of AccessMethods in this ACR + * 1D V: First AccessMethodProviderID -- XAUTH ??? + * 01 V: First keyIDOrReference -- ref 01 ?? + * 1E V: Last AccessMethodProviderID -- PIN ??? + * 01 V: Last keyIDOrReference -- id 01 ? + * A0 05 + * 09 V: ACRID of ACR entry + * 02 V: ACRType: BSI_ACR_XAUTH + * 01 V: Number of AccessMethods in this ACR + * 1D V: First AccessMethodProviderID -- XAUTH ??? + * 02 V: First keyIDOrReference -- ref 02 ?? + * A0 07 + * 0A V: ACRID of ACR entry + * 03 V: ACRType: BSI_ACR_XAUTH_OR_PIN + * 02 V: Number of AccessMethods in this ACR + * 1D V: First AccessMethodProviderID -- XAUTH ??? + * 03 V: First keyIDOrReference -- ref 03 ?? + * 1E V: Last AccessMethodProviderID -- PIN ??? + * 01 V: Last keyIDOrReference -- id 01 ? + * A0 05 + * 0B V: ACRID of ACR entry + * 02 V: ACRType: BSI_ACR_XAUTH + * 01 V: Number of AccessMethods in this ACR + * 1D V: First AccessMethodProviderID -- XAUTH ??? + * 04 V: First keyIDOrReference -- ref 04 ?? + * A0 03 + * 10 V: ACRID of ACR entry + * 00 V: ACRType: BSI_ACR_ALWAYS + * 00 V: Number of AccessMethods in this ACR + * A0 03 + * 11 V: ACRID of ACR entry + * 00 V: ACRType: BSI_ACR_ALWAYS + * 00 V: Number of AccessMethods in this ACR + */ +struct acr_table acr_table = { + 11, { + {0x00, ACR_ALWAYS, 0}, + {0x01, ACR_NEVER, 0}, + {0x02, ACR_ALWAYS, 0}, + {0x06, ACR_PIN, 1, { + {0x1E, 0x00} + }}, + {0x04, ACR_SECURE_CHANNEL_GP, 1, { + {0x1F, 0x21} + }}, + {0x08, ACR_XAUTH_OR_PIN, 2, { + {0x1D, 0x01}, + {0x1E, 0x01} + }}, + {0x09, ACR_XAUTH, 1, { + {0x1D, 0x01} + }}, + {0x0A, ACR_XAUTH_OR_PIN, 2, { + {0x1D, 0x03}, + {0x1E, 0x01} + }}, + {0x0B, ACR_XAUTH, 1, { + {0x1D, 0x0} + }}, + {0x10, ACR_ALWAYS, 0}, + {0x11, ACR_ALWAYS, 0}, + } +}; + +static struct simpletlv_member * +cac_aca_get_acr(size_t *acr_len, unsigned char *acrid) +{ + struct simpletlv_member *r; + size_t i; + int j = 0; + + g_assert_nonnull(acr_len); + + if (acrid != NULL) { + r = g_malloc(sizeof(struct simpletlv_member)); + } else { + r = g_malloc_n(acr_table.num_entries + 1, sizeof(struct simpletlv_member)); + + r[j].tag = CAC_ACR_NUM_ENTRIES; + r[j].length = 1; + r[j].value.value = (unsigned char *) &acr_table.num_entries; + r[j].type = SIMPLETLV_TYPE_LEAF; + j++; + } + for (i = 0; i < acr_table.num_entries; i++) { + if (acrid != NULL && *acrid != acr_table.entries[i].acrid) + continue; + + r[j].tag = CAC_ACR_ENTRY; + r[j].length = 2*acr_table.entries[i].num_access_methods+3; + r[j].value.value = (unsigned char *) &acr_table.entries[i]; + r[j].type = SIMPLETLV_TYPE_LEAF; + j++; + } + if (j <= 0) { + /* we did not find the requested ACRID */ + g_free(r); + r = NULL; + } + *acr_len = j; + return r; +} + +/* + * Service Applet Table: + * This table maps the Service Applet ID to the full AID for each + * Service Applet. + * (from 5.3.3.5 Get ACR APDU, Table 5-21) + */ + +#define MAX_AID_LEN 7 + +struct applet_entry { + unsigned char applet_id; + unsigned int applet_aid_len; + unsigned char applet_aid[MAX_AID_LEN]; +}; +struct service_applet_table { + unsigned num_entries; + struct applet_entry entries[]; +}; + +/* Example: + * 01 05 TL: Applet Information + * 10 02 06 02 02 + * 94 01 TL: Number of Applet Entries + * 0F + * 93 0A TL: Applet entry + * 40 Applet ID + * 92 07 TL: Applet AID + * A0 00 00 01 16 30 00 + * 93 0A + * 4F + * 92 07 + * A0 00 00 01 16 DB 00 + * 93 0A + * 4B + * 92 07 + * A0 00 00 00 79 02 FB + * 93 0A + * 41 + * 92 07 + * A0 00 00 00 79 02 00 + * 93 0A + * 42 + * 92 07 + * A0 00 00 00 79 02 01 + * 93 0A + * 4E + * 92 07 + * A0 00 00 00 79 02 FE + * 93 0A + * 4D + * 92 07 + * A0 00 00 00 79 02 FD + * 93 0A + * 50 + * 92 07 + * A0 00 00 00 79 02 F2 + * 93 0A + * 63 + * 92 07 + * A0 00 00 00 79 01 02 + * 93 0A + * 51 + * 92 07 + * A0 00 00 00 79 02 F0 + * 93 0A + * 61 + * 92 07 + * A0 00 00 00 79 01 00 + * 93 0A + * 52 + * 92 07 + * A0 00 00 00 79 02 F1 + * 93 0A + * 62 + * 92 07 + * A0 00 00 00 79 01 01 + * 93 0A + * 44 + * 92 07 + * A0 00 00 00 79 12 01 + * 93 0A + * 45 + * 92 07 + * A0 00 00 00 79 12 02 + */ + +struct service_applet_table service_table = { + 15, { + {0x40, 7, "\xA0\x00\x00\x01\x16\x30\x00"}, + {0x4F, 7, "\xA0\x00\x00\x01\x16\xDB\x00"}, + {0x4B, 7, "\xA0\x00\x00\x00\x79\x02\xFB"}, + {0x41, 7, "\xA0\x00\x00\x00\x79\x02\x00"}, + {0x42, 7, "\xA0\x00\x00\x00\x79\x02\x01"}, + {0x4E, 7, "\xA0\x00\x00\x00\x79\x02\xFE"}, + {0x4D, 7, "\xA0\x00\x00\x00\x79\x02\xFD"}, + {0x50, 7, "\xA0\x00\x00\x00\x79\x02\xF2"}, + {0x63, 7, "\xA0\x00\x00\x00\x79\x01\x02"}, + {0x51, 7, "\xA0\x00\x00\x00\x79\x02\xF0"}, + {0x61, 7, "\xA0\x00\x00\x00\x79\x01\x00"}, + {0x52, 7, "\xA0\x00\x00\x00\x79\x02\xF1"}, + {0x62, 7, "\xA0\x00\x00\x00\x79\x01\x01"}, + {0x44, 7, "\xA0\x00\x00\x00\x79\x12\x01"}, + {0x45, 7, "\xA0\x00\x00\x00\x79\x12\x02"}, + } +}; + +static struct simpletlv_member * +cac_aca_get_service_table(size_t *r_len) +{ + struct simpletlv_member *r = NULL; + unsigned char *num_entries = NULL; + unsigned char *entry = NULL; + size_t i = 0; + + g_assert_nonnull(r_len); + + r = g_malloc(sizeof(struct simpletlv_member)*(service_table.num_entries+1)); + + num_entries = g_malloc(1); + *num_entries = service_table.num_entries; + + r[0].type = SIMPLETLV_TYPE_LEAF; + r[0].tag = CAC_ACR_SERVICE_NUM_ENTRIES; + r[0].length = 1; + r[0].value.value = num_entries; + for (i = 1; i <= service_table.num_entries; i++) { + r[i].type = SIMPLETLV_TYPE_LEAF; + r[i].tag = CAC_ACR_SERVICE_ENTRY; + r[i].length = service_table.entries[i].applet_aid_len + 3; + entry = g_malloc(r[i].length); + entry[0] = service_table.entries[i].applet_id; + entry[1] = CAC_ACR_AID; + entry[2] = service_table.entries[i].applet_aid_len; + memcpy(&entry[3], (unsigned char *) &service_table.entries[i], + service_table.entries[i].applet_aid_len); + r[i].value.value = entry; + } + *r_len = service_table.num_entries + 1; + return r; +} + +/* + * Object/Applet ACR Table: + * This table maps the service (INS code/P1 byte/P2 byte/1 st data byte) + * to the ACRID for each container. + * (from 5.3.3.5 Get ACR APDU, Table 5-16) + * + * 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, + ACR_INS_CONFIG_P2 = 0x02, + ACR_INS_CONFIG_DATA1 = 0x04, +}; + +#define ACR_MAX_INSTRUCTIONS 5 +#define ACR_MAX_APPLET_OBJECTS 5 +#define ACR_MAX_APPLETS 20 + +struct cac_ins { + unsigned char code; + unsigned char acrid; + unsigned char config; + unsigned char p1; + unsigned char p2; + unsigned char data1; +}; +struct acr_object { + unsigned char id[2]; + unsigned int num_ins; + struct cac_ins ins[ACR_MAX_INSTRUCTIONS]; +}; +struct acr_applet { + unsigned char id; + unsigned int num_objects; + struct acr_object objects[ACR_MAX_APPLET_OBJECTS]; +}; +struct acr_applets { + unsigned int num_applets; + struct acr_applet applets[ACR_MAX_APPLETS]; +}; + +/* Example: + * 01 05 TL: Applet Information + * 10 02 06 02 02 + * 81 01 TL: Number of applets managed by this ACA + * 10 + * 80 09 TL: Card Applet ACR + * 1F Applet ID + * 01 Number of objects managed by this applet + * 82 05 TL: Card Object ACR + * FF FF Card Object ID + * 20 INS1 code + * 00 INS1 configuration definition + * 00 ACRID + * 80 1A TL: Card Applet ACR + * 4F Applet ID + * 02 Number of objects managed by this applet + * 82 08 TL: Card Object ACR + * DB 00 Card Object ID: CCC + * 58 INS1: Update Buffer + * 00 config: none + * 04 ACRID: (ID from ACR Table) + * 52 INS2: Read Buffer + * 00 config: none + * 00 ACRID: (ID from ACR Table) + * 82 0C TL: Card Object ACR + * FF FF Card Object ID + * 82 INS1 code + * 01 config + * 00 P1 Value + * 11 ACRID + * 84 INS2 code + * 00 INS2 config + * 11 ACRID + * 20 INS3 code + * 00 INS3 config + * 10 ACRID + * [...] + */ + +struct acr_applets applets_table = { + 16, { + {0x1F, 1, { + {"\xFF\xFF", 1, { + {VCARD7816_INS_VERIFY, 0x00, ACR_INS_CONFIG_NONE} + }} + }}, + {0xF4, 2, { + {"\xDB\x00", 2, { + {CAC_UPDATE_BUFFER, 0x04, ACR_INS_CONFIG_NONE}, + {CAC_READ_BUFFER, 0x00, ACR_INS_CONFIG_NONE} + }}, + {"\xFF\xFF", 3, { + {VCARD7816_INS_EXTERNAL_AUTHENTICATE, 0x11, ACR_INS_CONFIG_P1, .p1=0x00}, + {VCARD7816_INS_GET_CHALLENGE, 0x11, ACR_INS_CONFIG_NONE}, + {VCARD7816_INS_VERIFY, 0x10, ACR_INS_CONFIG_NONE}, + }} + }}, + {0x4B, 2, { + {"\x02\xFB", 2, { + {CAC_UPDATE_BUFFER, 0x06, ACR_INS_CONFIG_NONE}, + {CAC_READ_BUFFER, 0x00, ACR_INS_CONFIG_NONE} + }}, + {"\xFF\xFF", 3, { + {VCARD7816_INS_EXTERNAL_AUTHENTICATE, 0x11, ACR_INS_CONFIG_P1, .p1=0x00}, + {VCARD7816_INS_GET_CHALLENGE, 0x11, ACR_INS_CONFIG_NONE}, + {VCARD7816_INS_VERIFY, 0x10, ACR_INS_CONFIG_NONE}, + }} + }}, + {0x41, 2, { + {"\x02\x00", 3, { + {CAC_UPDATE_BUFFER, 0x09, ACR_INS_CONFIG_NONE}, + {CAC_READ_BUFFER, 0x00, ACR_INS_CONFIG_DATA1, .data1=0x01}, + {CAC_READ_BUFFER, 0x08, ACR_INS_CONFIG_NONE} + }}, + {"\xFF\xFF", 3, { + {VCARD7816_INS_EXTERNAL_AUTHENTICATE, 0x11, ACR_INS_CONFIG_P1, .p1=0x00}, + {VCARD7816_INS_GET_CHALLENGE, 0x11, ACR_INS_CONFIG_NONE}, + {VCARD7816_INS_VERIFY, 0x10, ACR_INS_CONFIG_NONE}, + }} + }}, + {0x42, 2, { + {"\x02\x01", 3, { + {CAC_UPDATE_BUFFER, 0x0B, ACR_INS_CONFIG_NONE}, + {CAC_READ_BUFFER, 0x00, ACR_INS_CONFIG_DATA1, .data1=0x01}, + {CAC_READ_BUFFER, 0x0A, ACR_INS_CONFIG_NONE} + }}, + {"\xFF\xFF", 3, { + {VCARD7816_INS_EXTERNAL_AUTHENTICATE, 0x11, ACR_INS_CONFIG_P1, .p1=0x00}, + {VCARD7816_INS_GET_CHALLENGE, 0x11, ACR_INS_CONFIG_NONE}, + {VCARD7816_INS_VERIFY, 0x10, ACR_INS_CONFIG_NONE}, + }} + }}, + {0x40, 5, { + {"\x30\x00", 3, { + {CAC_UPDATE_BUFFER, 0x04, ACR_INS_CONFIG_NONE}, + {CAC_READ_BUFFER, 0x00, ACR_INS_CONFIG_NONE}, + {VCARD7816_INS_READ_BINARY, 0x00, ACR_INS_CONFIG_NONE} + }}, + {"\x60\x10", 3, { + {CAC_UPDATE_BUFFER, 0x04, ACR_INS_CONFIG_NONE}, + {CAC_READ_BUFFER, 0x00, ACR_INS_CONFIG_DATA1, .data1=0x01}, + {CAC_READ_BUFFER, 0x06, ACR_INS_CONFIG_NONE} + }}, + {"\x60\x30", 3, { + {CAC_UPDATE_BUFFER, 0x04, ACR_INS_CONFIG_NONE}, + {CAC_READ_BUFFER, 0x00, ACR_INS_CONFIG_DATA1, .data1=0x01}, + {CAC_READ_BUFFER, 0x06, ACR_INS_CONFIG_NONE} + }}, + {"\x90\x00", 2, { + {CAC_UPDATE_BUFFER, 0x04, ACR_INS_CONFIG_NONE}, + {CAC_READ_BUFFER, 0x00, ACR_INS_CONFIG_NONE} + }}, + {"\xFF\xFF", 3, { + {VCARD7816_INS_EXTERNAL_AUTHENTICATE, 0x11, ACR_INS_CONFIG_P1, .p1=0x00}, + {VCARD7816_INS_GET_CHALLENGE, 0x11, ACR_INS_CONFIG_NONE}, + {VCARD7816_INS_VERIFY, 0x10, ACR_INS_CONFIG_NONE}, + }} + }}, + {0x4e, 2, { + {"\x02\xFE", 2, { + {CAC_UPDATE_BUFFER, 0x04, ACR_INS_CONFIG_NONE}, + {CAC_READ_BUFFER, 0x00, ACR_INS_CONFIG_NONE} + }}, + {"\xFF\xFF", 3, { + {VCARD7816_INS_EXTERNAL_AUTHENTICATE, 0x11, ACR_INS_CONFIG_P1, .p1=0x00}, + {VCARD7816_INS_GET_CHALLENGE, 0x11, ACR_INS_CONFIG_NONE}, + {VCARD7816_INS_VERIFY, 0x10, ACR_INS_CONFIG_NONE}, + }} + }}, + {0x4d, 2, { + {"\x02\xFD", 3, { + {CAC_UPDATE_BUFFER, 0x06, ACR_INS_CONFIG_NONE}, + {CAC_READ_BUFFER, 0x00, ACR_INS_CONFIG_DATA1, .data1=0x01}, + {CAC_READ_BUFFER, 0x06, ACR_INS_CONFIG_NONE} + }}, + {"\xFF\xFF", 3, { + {VCARD7816_INS_EXTERNAL_AUTHENTICATE, 0x11, ACR_INS_CONFIG_P1, .p1=0x00}, + {VCARD7816_INS_GET_CHALLENGE, 0x11, ACR_INS_CONFIG_NONE}, + {VCARD7816_INS_VERIFY, 0x10, ACR_INS_CONFIG_NONE}, + }} + }}, + {0x50, 1, { + {"\xFF\xFF", 5, { + {CAC_UPDATE_BUFFER, 0x04, ACR_INS_CONFIG_NONE}, + {CAC_READ_BUFFER, 0x00, ACR_INS_CONFIG_NONE}, + {VCARD7816_INS_EXTERNAL_AUTHENTICATE, 0x11, ACR_INS_CONFIG_P1, .p1=0x00}, + {VCARD7816_INS_GET_CHALLENGE, 0x11, ACR_INS_CONFIG_NONE}, + {VCARD7816_INS_VERIFY, 0x10, ACR_INS_CONFIG_NONE}, + }} + }}, + {0x63, 2, { + {"\x01\x02", 3, { + {CAC_UPDATE_BUFFER, 0x04, ACR_INS_CONFIG_NONE}, + {CAC_READ_BUFFER, 0x00, ACR_INS_CONFIG_NONE}, + {CAC_SIGN_DECRYPT, 0x06, ACR_INS_CONFIG_NONE} + }}, + {"\xFF\xFF", 1, { + {VCARD7816_INS_VERIFY, 0x10, ACR_INS_CONFIG_NONE}, + }} + }}, + {0x51, 1, { + {"\xFF\xFF", 5, { + {CAC_UPDATE_BUFFER, 0x04, ACR_INS_CONFIG_NONE}, + {CAC_READ_BUFFER, 0x00, ACR_INS_CONFIG_NONE}, + {VCARD7816_INS_EXTERNAL_AUTHENTICATE, 0x11, ACR_INS_CONFIG_P1, .p1=0x00}, + {VCARD7816_INS_GET_CHALLENGE, 0x11, ACR_INS_CONFIG_NONE}, + {VCARD7816_INS_VERIFY, 0x10, ACR_INS_CONFIG_NONE}, + }} + }}, + {0x61, 2, { + {"\x01\x00", 3, { + {CAC_UPDATE_BUFFER, 0x04, ACR_INS_CONFIG_NONE}, + {CAC_READ_BUFFER, 0x00, ACR_INS_CONFIG_NONE}, + {CAC_SIGN_DECRYPT, 0x06, ACR_INS_CONFIG_NONE} + }}, + {"\xFF\xFF", 1, { + {VCARD7816_INS_VERIFY, 0x10, ACR_INS_CONFIG_NONE}, + }} + }}, + {0x52, 1, { + {"\xFF\xFF", 5, { + {CAC_UPDATE_BUFFER, 0x04, ACR_INS_CONFIG_NONE}, + {CAC_READ_BUFFER, 0x00, ACR_INS_CONFIG_NONE}, + {VCARD7816_INS_EXTERNAL_AUTHENTICATE, 0x11, ACR_INS_CONFIG_P1, .p1=0x00}, + {VCARD7816_INS_GET_CHALLENGE, 0x11, ACR_INS_CONFIG_NONE}, + {VCARD7816_INS_VERIFY, 0x10, ACR_INS_CONFIG_NONE}, + }} + }}, + {0x62, 2, { + {"\x01\x01", 3, { + {CAC_UPDATE_BUFFER, 0x04, ACR_INS_CONFIG_NONE}, + {CAC_READ_BUFFER, 0x00, ACR_INS_CONFIG_NONE}, + {CAC_SIGN_DECRYPT, 0x06, ACR_INS_CONFIG_NONE} + }}, + {"\xFF\xFF", 1, { + {VCARD7816_INS_VERIFY, 0x10, ACR_INS_CONFIG_NONE}, + }} + }}, + {0x44, 2, { + {"\x12\x01", 3, { + {CAC_UPDATE_BUFFER, 0x06, ACR_INS_CONFIG_NONE}, + {CAC_READ_BUFFER, 0x00, ACR_INS_CONFIG_DATA1, .data1=0x01}, + {CAC_READ_BUFFER, 0x06, ACR_INS_CONFIG_NONE} + }}, + {"\xFF\xFF", 3, { + {VCARD7816_INS_EXTERNAL_AUTHENTICATE, 0x11, ACR_INS_CONFIG_P1, .p1=0x00}, + {VCARD7816_INS_GET_CHALLENGE, 0x11, ACR_INS_CONFIG_NONE}, + {VCARD7816_INS_VERIFY, 0x10, ACR_INS_CONFIG_NONE}, + }} + }}, + {0x45, 2, { + {"\x12\x02", 3, { + {CAC_UPDATE_BUFFER, 0x06, ACR_INS_CONFIG_NONE}, + {CAC_READ_BUFFER, 0x00, ACR_INS_CONFIG_DATA1, .data1=0x01}, + {CAC_READ_BUFFER, 0x06, ACR_INS_CONFIG_NONE} + }}, + {"\xFF\xFF", 3, { + {VCARD7816_INS_EXTERNAL_AUTHENTICATE, 0x11, ACR_INS_CONFIG_P1, .p1=0x00}, + {VCARD7816_INS_GET_CHALLENGE, 0x11, ACR_INS_CONFIG_NONE}, + {VCARD7816_INS_VERIFY, 0x10, ACR_INS_CONFIG_NONE}, + }} + }}, + } +}; + +static unsigned char * +acr_applet_object_encode(struct acr_object *object, unsigned char *out, + unsigned int outlen, unsigned int *lenp) +{ + unsigned int j; + unsigned char *p = NULL; + + p = out; + if (outlen < 2) + return NULL; + *p++ = object->id[0]; + *p++ = object->id[1]; + outlen -= 2; + for (j = 0; j < object->num_ins; j++) { + if (outlen < 3) + return NULL; + *p++ = object->ins[j].code; + *p++ = object->ins[j].config; + outlen -= 2; + if (object->ins[j].config & ACR_INS_CONFIG_P1) { + if (outlen < 1) + return NULL; + *p++ = object->ins[j].p1; + outlen--; + } + if (object->ins[j].config & ACR_INS_CONFIG_P2) { + if (outlen < 1) + return NULL; + *p++ = object->ins[j].p2; + outlen--; + } + if (object->ins[j].config & ACR_INS_CONFIG_DATA1) { + if (outlen < 1) + return NULL; + *p++ = object->ins[j].data1; + outlen--; + } + + if (outlen < 1) + return NULL; + *p++ = object->ins[j].acrid; + outlen--; + } + *lenp = (p - out); + return p; +} + +static unsigned char * +acr_applet_encode(struct acr_applet *applet, unsigned int *outlen) +{ + unsigned char *buffer = NULL, *p, *lenp; + unsigned int i, j, plen, objlen, buffer_len; + + if (outlen == NULL) + return NULL; + + plen = 2; + for (i = 0; i < applet->num_objects; i++) { + plen += 4; + for (j = 0; j < applet->objects[i].num_ins; j++) { + plen += 3; + if (applet->objects[i].ins[j].config & ACR_INS_CONFIG_P1) + plen++; + if (applet->objects[i].ins[j].config & ACR_INS_CONFIG_P2) + plen++; + if (applet->objects[i].ins[j].config & ACR_INS_CONFIG_DATA1) + plen++; + } + } + buffer_len = plen; + buffer = g_malloc(plen); + + p = buffer; + *p++ = applet->id; + *p++ = applet->num_objects; + plen -= 2; + for (i = 0; i < applet->num_objects; i++) { + *p++ = CAC_ACR_OBJECT_ACR; + lenp = p++; + plen -= 2; + p = acr_applet_object_encode(&applet->objects[i], p, plen, &objlen); + if (!p) + goto failure; + *lenp = objlen & 0xff; + plen -= objlen; + } + + g_assert_cmpint(p - buffer, ==, buffer_len); + *outlen = buffer_len; + return buffer; + +failure: + g_free(buffer); + return NULL; +} + +static struct simpletlv_member * +cac_aca_get_applet_acr_coid(unsigned char *coid) +{ + struct simpletlv_member *r = NULL; + size_t i, j; + + r = g_malloc(sizeof(struct simpletlv_member)); + + for (i = 0; i <= applets_table.num_applets; i++) { + for (j = 0; j < applets_table.applets[i].num_objects; j++) { + if (memcmp(&applets_table.applets[i].objects[j].id, coid, 2) == 0) { + unsigned int buffer_len = ACR_MAX_INSTRUCTIONS * 6 + 2; + unsigned char *p = NULL; + + r->type = SIMPLETLV_TYPE_LEAF; + r->tag = CAC_ACR_OBJECT_ACR; + r->value.value = g_malloc_n(buffer_len, sizeof(unsigned char)); + p = acr_applet_object_encode( + &applets_table.applets[i].objects[j], + r->value.value, buffer_len, &r->length); + /* return the record on success */ + if (p) + return r; + + /* clean up on failure */ + g_free(r->value.value); + } + } + } + /* Failure */ + g_free(r); + return NULL; +} + +static unsigned char +aid_to_applet_id(unsigned char *aid, unsigned int aid_len) +{ + unsigned int i; + for (i = 0; i < service_table.num_entries; i++) { + if (aid_len == service_table.entries[i].applet_aid_len + && memcmp(aid, service_table.entries[i].applet_aid, aid_len) == 0) + return service_table.entries[i].applet_id; + } + return 0x00; +} + +static struct simpletlv_member * +cac_aca_get_applet_acr(size_t *acr_len, unsigned char *aid, + unsigned int aid_len) +{ + struct simpletlv_member *r = NULL; + unsigned char *num_applets = NULL; + size_t i, j = 0; + unsigned char applet_id = 0; + + g_assert_nonnull(acr_len); + + if (aid != NULL && aid_len != 0) { + /* We are selecting only one applet*/ + applet_id = aid_to_applet_id(aid, aid_len); + if (applet_id == 0) + return NULL; + r = g_malloc(sizeof(struct simpletlv_member)); + } else { + r = g_malloc(sizeof(struct simpletlv_member)*(applets_table.num_applets+1)); + } + + if (!applet_id) { + num_applets = g_malloc(1); + *num_applets = applets_table.num_applets; + + r[j].tag = CAC_ACR_NUM_APPLETS; + r[j].length = 1; + r[j].value.value = num_applets; + r[j].type = SIMPLETLV_TYPE_LEAF; + j++; + } + for (i = 0; i < applets_table.num_applets; i++) { + if (applet_id && applet_id != applets_table.applets[i].id) + continue; + + r[j].type = SIMPLETLV_TYPE_LEAF; + r[j].tag = CAC_ACR_APPLET_ACR; + r[j].value.value = acr_applet_encode(&applets_table.applets[i], &r[j].length); + if (r[j].value.value == NULL) + goto failure; + j++; + } + *acr_len = j; + return r; + +failure: + simpletlv_free(r, j); + g_free(num_applets); + return NULL; +} + +/* + * Access Method Provider (AMP) table: + * This table maps the Access Method Provider ID to the full AID + * for each Access Method Provider. + * (from 5.3.3.5 Get ACR APDU, Table 5-20) + */ + +struct amp_entry { + unsigned char amp_id; + unsigned int amp_aid_len; + unsigned char amp_aid[7]; +}; +struct amp_table { + unsigned int num_entries; + struct amp_entry entries[5]; +}; + +/* Example: + * 01 05 TL: Applet information + * 10 02 06 02 02 + * 91 01 TL: Number of AMP entries + * 03 + * 90 0A AMP Entry + * 1F Access Method Provider ID + * 92 07 TL: Access Method Provider AID + * A0 00 00 00 79 03 00 + * 90 0A AMP Entry + * 1E Access Method Provider ID + * 92 07 TL: Access Method Provider AID + * A0 00 00 00 79 03 00 + * 90 0A AMP Entry + * 1D Access Method Provider ID + * 92 07 TL: Access Method Provider AID + * A0 00 00 00 79 03 00 + */ + +struct amp_table amp_table = { + 3, { + {0x1F, 7, "\xA0\x00\x00\x00\x79\x03\x00"}, + {0x1E, 7, "\xA0\x00\x00\x00\x79\x03\x00"}, + {0x1D, 7, "\xA0\x00\x00\x00\x79\x03\x00"}, + } +}; + +static struct simpletlv_member * +cac_aca_get_amp(size_t *amp_len) +{ + struct simpletlv_member *r = NULL; + unsigned char *num_entries = NULL; + unsigned char *entry = NULL; + size_t i = 0; + + g_assert_nonnull(amp_len); + + r = g_malloc_n(amp_table.num_entries + 1, sizeof(struct simpletlv_member)); + + num_entries = g_malloc(1); + *num_entries = amp_table.num_entries; + + r[0].tag = CAC_ACR_AMP_NUM_ENTRIES; + r[0].length = 1; + r[0].value.value = num_entries; + r[0].type = SIMPLETLV_TYPE_LEAF; + for (i = 1; i <= amp_table.num_entries; i++) { + r[i].type = SIMPLETLV_TYPE_LEAF; + r[i].tag = CAC_ACR_AMP_ENTRY; + r[i].length = amp_table.entries[i].amp_aid_len + 3; + entry = g_malloc(r[i].length); + entry[0] = amp_table.entries[i].amp_id; + entry[1] = CAC_ACR_AID; + entry[2] = amp_table.entries[i].amp_aid_len; + memcpy(&entry[3], (unsigned char *) &_table.entries[i], + amp_table.entries[i].amp_aid_len); + r[i].value.value = entry; + } + *amp_len = amp_table.num_entries + 1; + return r; +} + +/* ACA Applet Information + * The following entry is always returned and precedes any ACR table, + * Applet/Object ACR table or Authentication Method Provider table. + * + * 01 Tag: Applet Information + * 05 Length + * 10 Applet family + * 02 06 02 02 Applet version + */ +unsigned char applet_information[] = "\x10\x02\x06\x02\x02"; +static struct simpletlv_member aca_properties[1] = { + {CAC_PROPERTIES_APPLET_INFORMATION, 5, {/*.value = applet_information*/}, + SIMPLETLV_TYPE_LEAF}, +}; + +static struct simpletlv_member * +cac_aca_get_properties(size_t *properties_len) +{ + g_assert_nonnull(properties_len); + + /* Inject Applet Version into Applet information */ + aca_properties[0].value.value = applet_information; + + *properties_len = 1; + + return aca_properties; +} + + + +VCardResponse * +cac_aca_get_acr_response(VCard *card, int Le, unsigned char *acrid) +{ + size_t acr_buffer_len; + unsigned char *acr_buffer = NULL; + size_t properties_len; + const struct simpletlv_member *properties; + size_t acr_len; + struct simpletlv_member *acr = NULL; + size_t list_len; + struct simpletlv_member *list = NULL; + VCardResponse *r = NULL; + + /* Prepare the SimpleTLV structures */ + properties = cac_aca_get_properties(&properties_len); + acr = cac_aca_get_acr(&acr_len, acrid); + if (acr == NULL) { + /* The requested ACR was not found */ + r = vcard_make_response(VCARD7816_STATUS_ERROR_DATA_NOT_FOUND); + return r; + } + + /* Merge them together */ + list_len = properties_len + acr_len; + list = simpletlv_merge(properties, properties_len, acr, acr_len); + if (list == NULL) + goto failure; + + /* encode the data */ + acr_buffer_len = simpletlv_encode(list, list_len, &acr_buffer, 0, NULL); + if (acr_buffer == NULL) + goto failure; + + r = vcard_response_new(card, acr_buffer, acr_buffer_len, Le, + VCARD7816_STATUS_SUCCESS); + +failure: + g_free(list); + g_free(acr); + g_free(acr_buffer); + if (r == NULL) + r = vcard_make_response(VCARD7816_STATUS_ERROR_GENERAL); + return r; +} + +VCardResponse * +cac_aca_get_applet_acr_response(VCard *card, int Le, + unsigned char *aid, unsigned int aid_len, + unsigned char *coid) +{ + size_t acr_buffer_len; + unsigned char *acr_buffer = NULL; + size_t properties_len; + const struct simpletlv_member *properties; + size_t acr_len; + struct simpletlv_member *acr = NULL; + size_t list_len; + struct simpletlv_member *list = NULL; + VCardResponse *r = NULL; + + /* Prepare the SimpleTLV structures */ + properties = cac_aca_get_properties(&properties_len); + if (coid != NULL) { + g_debug("%s: Called. COID = %s", __func__, hex_dump(coid, 2, NULL, 0)); + + /* getting the table for COID (2B) */ + acr_len = 1; // returns exactly one element if found + acr = cac_aca_get_applet_acr_coid(coid); + if (!acr) { + /* did not find the COID */ + r = vcard_make_response(VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA); + return r; + } + } else { + /* getting the table for AID or the whole */ + acr = cac_aca_get_applet_acr(&acr_len, aid, aid_len); + if (!acr && aid_len > 0) { + /* did not find the AID */ + r = vcard_make_response(VCARD7816_STATUS_ERROR_DATA_NOT_FOUND); + return r; + } + if (acr == NULL) + goto failure; + } + + /* Merge them together */ + list_len = properties_len + acr_len; + list = simpletlv_merge(properties, properties_len, acr, acr_len); + if (list == NULL) + goto failure; + + /* encode the data */ + acr_buffer_len = simpletlv_encode(list, list_len, &acr_buffer, 0, NULL); + if (acr_buffer == NULL) + goto failure; + + r = vcard_response_new(card, acr_buffer, acr_buffer_len, Le, + VCARD7816_STATUS_SUCCESS); + g_debug("%s: response bytes: %s", __func__, + hex_dump(acr_buffer, acr_buffer_len, NULL, 0)); + +failure: + g_free(list); + simpletlv_free(acr, acr_len); + g_free(acr_buffer); + if (r == NULL) + r = vcard_make_response(VCARD7816_STATUS_ERROR_GENERAL); + return r; +} + +VCardResponse * +cac_aca_get_amp_response(VCard *card, int Le) +{ + size_t amp_buffer_len; + unsigned char *amp_buffer = NULL; + size_t properties_len; + const struct simpletlv_member *properties; + size_t amp_len; + struct simpletlv_member *amp = NULL; + size_t list_len; + struct simpletlv_member *list = NULL; + VCardResponse *r = NULL; + + /* Prepare the SimpleTLV structures */ + properties = cac_aca_get_properties(&properties_len); + amp = cac_aca_get_amp(&_len); + if (amp == NULL) + goto failure; + + /* Merge them together */ + list_len = properties_len + amp_len; + list = simpletlv_merge(properties, properties_len, amp, amp_len); + if (list == NULL) + goto failure; + + /* encode the data */ + amp_buffer_len = simpletlv_encode(list, list_len, &_buffer, 0, NULL); + if (amp_buffer == NULL) + goto failure; + + r = vcard_response_new(card, amp_buffer, amp_buffer_len, Le, + VCARD7816_STATUS_SUCCESS); + +failure: + g_free(list); + simpletlv_free(amp, amp_len); + g_free(amp_buffer); + if (r == NULL) + r = vcard_make_response(VCARD7816_STATUS_ERROR_GENERAL); + return r; +} + +VCardResponse * +cac_aca_get_service_response(VCard *card, int Le) +{ + size_t service_buffer_len; + unsigned char *service_buffer = NULL; + size_t properties_len; + const struct simpletlv_member *properties; + size_t service_len; + struct simpletlv_member *service = NULL; + size_t list_len; + struct simpletlv_member *list = NULL; + VCardResponse *r = NULL; + + /* Prepare the SimpleTLV structures */ + properties = cac_aca_get_properties(&properties_len); + service = cac_aca_get_service_table(&service_len); + if (service == NULL) + goto failure; + + /* Merge them together */ + list_len = properties_len + service_len; + list = simpletlv_merge(properties, properties_len, service, service_len); + if (list == NULL) + goto failure; + + /* encode the data */ + service_buffer_len = simpletlv_encode(list, list_len, &service_buffer, 0, NULL); + if (service_buffer == NULL) + goto failure; + + r = vcard_response_new(card, service_buffer, service_buffer_len, Le, + VCARD7816_STATUS_SUCCESS); + +failure: + g_free(list); + simpletlv_free(service, service_len); + g_free(service_buffer); + if (r == NULL) + r = vcard_make_response(VCARD7816_STATUS_ERROR_GENERAL); + return r; +} + +/* vim: set ts=4 sw=4 tw=0 noet expandtab: */ diff --git a/src/cac-aca.h b/src/cac-aca.h new file mode 100644 index 0000000..bbe738f --- /dev/null +++ b/src/cac-aca.h @@ -0,0 +1,32 @@ +/* + * implement the ACA applet for the CAC card. + * + * Adaptation to GSC-IS 2.1: + * https://nvlpubs.nist.gov/nistpubs/Legacy/IR/nistir6887e2003.pdf + * + * Copyright 2018 Red Hat, Inc. + * + * Author: 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. + */ + +#include "glib-compat.h" +#include "card_7816t.h" +#include "cac.h" + +#include <string.h> + +VCardResponse * +cac_aca_get_acr_response(VCard *card, int Le, unsigned char *acrid); + +VCardResponse * +cac_aca_get_applet_acr_response(VCard *card, int Le, + unsigned char *aid, unsigned int aid_len, + unsigned char *coid); +VCardResponse * +cac_aca_get_amp_response(VCard *card, int Le); + +VCardResponse * +cac_aca_get_service_response(VCard *card, int Le); diff --git a/src/cac.c b/src/cac.c index 8f4fbae..4b54e91 100644 --- a/src/cac.c +++ b/src/cac.c @@ -14,12 +14,15 @@ #include <stdbool.h> #include "cac.h" +#include "cac-aca.h" #include "vcard.h" #include "vcard_emul.h" #include "card_7816.h" #include "simpletlv.h" #include "common.h" +static unsigned char cac_aca_aid[] = { + 0xa0, 0x00, 0x00, 0x00, 0x79, 0x03, 0x00 }; static unsigned char cac_ccc_aid[] = { 0xa0, 0x00, 0x00, 0x01, 0x16, 0xDB, 0x00 }; @@ -35,6 +38,11 @@ typedef struct CACPKIAppletDataStruct { typedef struct CACCCCAppletDataStruct { } CACCCCAppletData; +/* private data for ACA container */ +typedef struct CACACAAppletDataStruct { + /* At the moment mostly in cac-aca.c */ +} CACACAAppletData; + /* * CAC applet private data */ @@ -50,6 +58,7 @@ struct VCardAppletPrivateStruct { union { CACPKIAppletData pki_data; CACCCCAppletData ccc_data; + CACACAAppletData aca_data; void *reserved; } u; }; @@ -460,6 +469,115 @@ cac_applet_pki_process_apdu(VCard *card, VCardAPDU *apdu, return ret; } +static VCardStatus +cac_applet_aca_process_apdu(VCard *card, VCardAPDU *apdu, + VCardResponse **response) +{ + VCardStatus ret = VCARD_FAIL; + VCardAppletPrivate *applet_private; + + applet_private = vcard_get_current_applet_private(card, apdu->a_channel); + assert(applet_private); + + switch (apdu->a_ins) { + case CAC_GET_ACR: + /* generate some ACRs Chapter 5.3.3.5 + * Works only on the ACA container, not the others! + */ + if (apdu->a_p2 != 0x00) { + /* P2 needs to be 0x00 */ + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); + ret = VCARD_DONE; + break; + } + switch (apdu->a_p1) { + case 0x00: + /* 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); + break; + + case 0x01: + /* Only one entry of the ACR table is extracted based on ACRID */ + if (apdu->a_Lc != 1) { /* ACRID is one byte */ + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_DATA_INVALID); + break; + } + *response = cac_aca_get_acr_response(card, apdu->a_Le, apdu->a_body); + break; + case 0x10: + /* All Applet/Object 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_applet_acr_response(card, apdu->a_Le, + NULL, 0, NULL); + break; + + case 0x11: + /* Only the entries of the Applet/Object ACR table for + * one applet are extracted based on applet AID */ + if (apdu->a_Lc != 7) { + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_DATA_INVALID); + break; + } + *response = cac_aca_get_applet_acr_response(card, apdu->a_Le, + apdu->a_body, apdu->a_Lc, NULL); + break; + + case 0x12: + /* Only one entry of the Applet/Object ACR table for + * an object is extracted based on object ID */ + if (apdu->a_Lc != 2) { + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_DATA_INVALID); + break; + } + *response = cac_aca_get_applet_acr_response(card, apdu->a_Le, + NULL, 0, apdu->a_body); + break; + + case 0x20: + /* 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); + break; + case 0x21: + /* The Service Applet table is extracted. */ + if (apdu->a_Lc != 0) { + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_DATA_INVALID); + break; + } + *response = cac_aca_get_service_response(card, apdu->a_Le); + break; + default: + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); + break; + } + ret = VCARD_DONE; + break; + default: + ret = cac_common_process_apdu(card, apdu, response); + break; + } + return ret; +} + /* * utilities for creating and destroying the private applet data */ @@ -494,6 +612,15 @@ cac_delete_ccc_applet_private(VCardAppletPrivate *applet_private) g_free(applet_private); } +static void +cac_delete_aca_applet_private(VCardAppletPrivate *applet_private) +{ + if (applet_private == NULL) { + return; + } + g_free(applet_private); +} + static VCardAppletPrivate * cac_new_pki_applet_private(int i, const unsigned char *cert, int cert_len, VCardKey *key) @@ -944,6 +1071,76 @@ failure: return NULL; } +static VCardAppletPrivate * +cac_new_aca_applet_private(void) +{ + VCardAppletPrivate *applet_private; + + /* ACA applet Properties ex.: + * 01 Tag: Applet Information + * 05 Length + * 10 Applet family + * 02 06 02 02 Applet version + */ + static unsigned char applet_information[] = "\x10\x02\x06\x02\x02"; + static struct simpletlv_member properties[1] = { + {CAC_PROPERTIES_APPLET_INFORMATION, 5, {/*.value = applet_information*/}, + SIMPLETLV_TYPE_LEAF}, + }; + + /* Inject Applet Version into Applet information */ + properties[0].value.value = applet_information; + + /* Create the private data structure */ + applet_private = g_new0(VCardAppletPrivate, 1); + if (applet_private == NULL) + goto failure; + + /* Link the properties */ + applet_private->properties = properties; + applet_private->properties_len = 1; + + return applet_private; + +failure: + if (applet_private != NULL) { + cac_delete_aca_applet_private(applet_private); + } + return NULL; +} + + +/* + * create a new ACA applet + */ +static VCardApplet * +cac_new_aca_applet(void) +{ + VCardAppletPrivate *applet_private; + VCardApplet *applet; + + applet_private = cac_new_aca_applet_private(); + if (applet_private == NULL) { + goto failure; + } + applet = vcard_new_applet(cac_applet_aca_process_apdu, NULL, + cac_aca_aid, sizeof(cac_aca_aid)); + if (applet == NULL) { + goto failure; + } + vcard_set_applet_private(applet, applet_private, + cac_delete_aca_applet_private); + applet_private = NULL; + + return applet; + +failure: + if (applet_private != NULL) { + cac_delete_aca_applet_private(applet_private); + } + return NULL; +} + /* * create a new cac applet which links to a given cert @@ -1009,6 +1206,13 @@ cac_card_init(VReader *reader, VCard *card, vcard_add_applet(card, applet); } + /* create a ACA applet, to list access rules */ + applet = cac_new_aca_applet(); + if (applet == NULL) { + goto failure; + } + vcard_add_applet(card, applet); + /* create a CCC container, which is need for CAC recognition, * which should be default */ diff --git a/src/cac.h b/src/cac.h index 58e302d..f21b119 100644 --- a/src/cac.h +++ b/src/cac.h @@ -26,6 +26,18 @@ #define CAC_PKI_TAG_CERTIFICATE 0x70 #define CAC_PKI_TAG_CERTINFO 0x71 +/* ACA applet tags */ +#define CAC_ACR_NUM_ENTRIES 0xA1 +#define CAC_ACR_ENTRY 0xA0 +#define CAC_ACR_NUM_APPLETS 0x81 +#define CAC_ACR_APPLET_ACR 0x80 +#define CAC_ACR_OBJECT_ACR 0x82 +#define CAC_ACR_AMP_NUM_ENTRIES 0x91 +#define CAC_ACR_AMP_ENTRY 0x90 +#define CAC_ACR_AID 0x92 +#define CAC_ACR_SERVICE_NUM_ENTRIES 0x94 +#define CAC_ACR_SERVICE_ENTRY 0x93 + /* CCC applet tags */ #define CAC_CCC_CARD_IDENTIFIER 0xF0 #define CAC_CCC_CAPABILITY_CONTAINER_VERSION 0xF1 -- 2.17.1 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel