[Spice-devel] [PATCH libcacard v2 18/35] cac: Emulate also other empty applets; handling multiple COIDs

Jakub Jelen jjelen at redhat.com
Thu Aug 2 09:43:50 UTC 2018


 * These are probably not mandatory, but they are present in real card

 * There are two types od them, one of them presents buffers in
   properties, but they are empty. The other does not even present
   the buffers in properties.

 * They do not have any known purpose, but they are on existing cards

 * ACF applet has more valid Card Object IDs it answers to on SELECT OID
   APDU

 * This requires some internal changes of SELECT OID handling, but
   currently, we do not have any "useful" data in different OIDs
   so we just need to keep the protocol.

 * Actually, the data in ACF (Access Control File) are one of the
   mandatory parts of CAC, but they are not exposed in PKCS#11
   and impossible to emulate (signatures of the internal structures),
   but ActivClient does not really need them.

Signed-off-by: Jakub Jelen <jjelen at redhat.com>
Reviewed-by: Robert Relyea <rrelyea at redhat.com>
---
 src/cac.c         | 376 +++++++++++++++++++++++++++++++++++++++++++++-
 src/cac.h         |   2 +
 tests/libcacard.c | 156 +++++++++++++------
 3 files changed, 482 insertions(+), 52 deletions(-)

diff --git a/src/cac.c b/src/cac.c
index 0bc0833..782de1e 100644
--- a/src/cac.c
+++ b/src/cac.c
@@ -25,6 +25,20 @@ 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 };
+static unsigned char cac_02fb_aid[] = {
+    0xa0, 0x00, 0x00, 0x00, 0x79, 0x02, 0xFB };
+static unsigned char cac_1201_aid[] = {
+    0xa0, 0x00, 0x00, 0x00, 0x79, 0x12, 0x01 };
+static unsigned char cac_1202_aid[] = {
+    0xa0, 0x00, 0x00, 0x00, 0x79, 0x12, 0x02 };
+static unsigned char cac_02f0_aid[] = {
+    0xa0, 0x00, 0x00, 0x00, 0x79, 0x02, 0xF0 };
+static unsigned char cac_02f1_aid[] = {
+    0xa0, 0x00, 0x00, 0x00, 0x79, 0x02, 0xF1 };
+static unsigned char cac_02f2_aid[] = {
+    0xa0, 0x00, 0x00, 0x00, 0x79, 0x02, 0xF2 };
+static unsigned char cac_access_control_aid[] = {
+    0xa0, 0x00, 0x00, 0x01, 0x16, 0x30, 0x00 };
 
 
 /* private data for PKI applets */
@@ -44,6 +58,10 @@ typedef struct CACACAAppletDataStruct {
     /* At the moment mostly in cac-aca.c */
 } CACACAAppletData;
 
+struct coid {
+    unsigned char v[2];
+};
+
 /*
  * CAC applet private data
  */
@@ -55,6 +73,12 @@ struct VCardAppletPrivateStruct {
     int val_buffer_len;
     struct simpletlv_member *properties;
     unsigned int 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
+     */
+    struct coid *coids;
+    unsigned int coids_len;
     /* applet-specific */
     union {
         CACPKIAppletData pki_data;
@@ -196,9 +220,10 @@ cleanup:
 static VCardStatus
 cac_common_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response)
 {
-    int ef;
     VCardAppletPrivate *applet_private;
     VCardStatus ret = VCARD_FAIL;
+    int found = 0;
+    unsigned int i;
 
     applet_private = vcard_get_current_applet_private(card, apdu->a_channel);
 
@@ -268,9 +293,15 @@ cac_common_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response)
             ret = VCARD_DONE;
             break;
         }
-        /* CAC 1.0 only supports ef = 0 */
-        ef = apdu->a_body[0] | (apdu->a_body[1] << 8);
-        if (ef != 0) {
+        /* CAC 2 Card Object ID needs to match one of the COID defined
+         * in the applet
+         */
+        for (i = 0; i < applet_private->coids_len; i++) {
+            if (memcmp(apdu->a_body, applet_private->coids[i].v, 2) == 0) {
+                found = 1;
+            }
+        }
+        if (!found) {
             *response = vcard_make_response(
                 VCARD7816_STATUS_ERROR_FILE_NOT_FOUND);
             ret = VCARD_DONE;
@@ -597,6 +628,7 @@ cac_delete_pki_applet_private(VCardAppletPrivate *applet_private)
     g_free(pki_applet_data->sign_buffer);
     g_free(applet_private->tag_buffer);
     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);
     if (pki_applet_data->key != NULL) {
@@ -613,6 +645,7 @@ cac_delete_ccc_applet_private(VCardAppletPrivate *applet_private)
     }
     g_free(applet_private->tag_buffer);
     g_free(applet_private->val_buffer);
+    g_free(applet_private->coids);
     g_free(applet_private);
 }
 
@@ -622,6 +655,21 @@ cac_delete_aca_applet_private(VCardAppletPrivate *applet_private)
     if (applet_private == NULL) {
         return;
     }
+    g_free(applet_private->coids);
+    g_free(applet_private);
+}
+
+static void
+cac_delete_empty_applet_private(VCardAppletPrivate *applet_private)
+{
+    if (applet_private == NULL) {
+        return;
+    }
+    g_free(applet_private->coids);
+    g_free(applet_private->tag_buffer);
+    g_free(applet_private->val_buffer);
+    /* this one is cloned so needs to be freed */
+    simpletlv_free(applet_private->properties, applet_private->properties_len);
     g_free(applet_private);
 }
 
@@ -746,6 +794,11 @@ cac_new_pki_applet_private(int i, const unsigned char *cert,
     object_id[1] = i;
     pki_object[0].value.value = object_id;
 
+    /* Create Object ID list */
+    applet_private->coids = g_malloc(sizeof(struct coid));
+    memcpy(applet_private->coids[0].v, object_id, 2);
+    applet_private->coids_len = 1;
+
     /* Inject T-Buffer and V-Buffer lengths in the properties buffer */
     ushort2lebytes(&buffer_properties[1], applet_private->tag_buffer_len);
     ushort2lebytes(&buffer_properties[3], applet_private->val_buffer_len);
@@ -1050,12 +1103,27 @@ failure:
  * 41           Personnel Category Code
  * 4D 57        Pay Plan Code
  * 30 30        Personnel Entitlement Condition Code
- * TODO For real cards, we could try to proxy this from original card,
- * OpenSC exposes this as a data object as SimpleLTV merged in one buffer
+ *
+ * pkcs11-tool --pin 77777777 --read-object --application-label 'Personnel' --type data
+ * OpenSC exposes this as a data object as SimpleLTV merged in one buffer:
+ * 19 00
+ * 20 00
+ * 35 00
+ * 24 01
+ *  4e
+ * 25 02
+ *  30 31
+ * 26 04
+ *  57 4f 2d 31
+ * 34 01
+ *  41
+ * 36 02
+ *  4d 57
+ * d3 02
+ *  30 30
+ * 00 00 fc bf <<< OpenSC bug (junk in the end instead of last encoded elements)
  *
  *
- * A0000001166010: Not actually an applet
- * A0000001166030: Not actually an applet
  *
  *
  * A0000000791201: Empty
@@ -1091,6 +1159,94 @@ failure:
  * $ opensc-tool -s 00A4040007A0000000791202 -s 8052000002020202
  * TAG, VALUE BUFFERS:
  * empty
+ *
+ * A00000007902F0: Empty (no buffers)
+ * $ opensc-tool -s 00A4040007A00000007902F0 -s 8056010000
+ * PROPERTIES:
+ * 01 05
+ *  10 02 06 02 03
+ * 40 01
+ *  00
+ *
+ * A00000007902F1: Empty (no buffers)
+ * $ opensc-tool -s 00A4040007A00000007902F1 -s 8056010000
+ * PROPERTIES:
+ * 01 05
+ *  10 02 06 02 03
+ * 40 01
+ *  00
+ *
+ * A00000007902F2: Empty (no buffers)
+ * $ opensc-tool -s 00A4040007A00000007902F2 -s 8056010000
+ * PROPERTIES:
+ * 01 05
+ *  10 02 06 02 03
+ * 40 01
+ *  00
+ *
+ *
+ * Access Control File
+ * A0000001163000
+ * PROPERTIES: shared among the OIDs
+ * 01 05
+ * 10 02 06 02 03
+ * 40 01        Number of objects
+ *  04
+ * 50 0B        TV Buffer
+ *  41 02       Object ID
+ *   30 00
+ *  42 05
+ *   00         <- These are SimpleTLV
+ *   1A 00 D2 07
+ * 50 0B        TV Buffer
+ *  41 02       Object ID
+ *   60 10
+ *  42 05
+ *   00
+ *   0E 00 BA 0B
+ * 50 0B        TV buffer
+ *  41 02       Object ID
+ *   60 30
+ *  42 05
+ *   00
+ *   0A 00 EE 2C
+ * 50 0B        TV Buffer
+ *  41 02       Object ID
+ *   90 00
+ *  42 05
+ *   00
+ *   0E 00 4E 04
+ *
+ * OID buffers:
+ * :3000
+ * 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  ??
+ * 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
+ * Tag buffer:
+ * 06 00
+ * BC FF CF 04  ??
+ * FE 00        Error Detection Code
+ *
+ * :6030
+ * Tag buffer:
+ * 06 00
+ * BC FF A9 29  ??
+ * FE 00        Error Detection Code
+ *
+ * :9000
+ * 08 00
+ * BB FF 38 02  Some PKCS#7 signed block
+ * BA 30        ???
+ * FE 00        Error Detectionc Code
  */
 
 
@@ -1364,6 +1520,11 @@ cac_new_ccc_applet_private(int cert_count)
     /* Inject Object ID */
     tv_object[0].value.value = object_id;
 
+    /* Create Object ID list */
+    applet_private->coids = g_malloc(sizeof(struct coid));
+    memcpy(applet_private->coids[0].v, object_id, 2);
+    applet_private->coids_len = 1;
+
     /* Inject T-Buffer and V-Buffer lengths in the properties buffer */
     ushort2lebytes(&buffer_properties[1], applet_private->tag_buffer_len);
     ushort2lebytes(&buffer_properties[3], applet_private->val_buffer_len);
@@ -1446,6 +1607,12 @@ cac_new_aca_applet_private(int cert_count)
     if (applet_private == NULL)
         goto failure;
 
+    /* store the applet OID */
+    applet_private->coids = g_malloc(sizeof(struct coid));
+    applet_private->coids[0].v[0] = 0x03;
+    applet_private->coids[0].v[1] = 0x00;
+    applet_private->coids_len = 1;
+
     /* Link the properties */
     applet_private->properties = properties;
     applet_private->properties_len = 1;
@@ -1461,6 +1628,107 @@ failure:
     return NULL;
 }
 
+static VCardAppletPrivate *
+cac_new_empty_applet_private(unsigned char objects[][2], unsigned int objects_len)
+{
+    VCardAppletPrivate *applet_private = NULL;
+
+    unsigned char object_id[] = "\x00\x00";
+    unsigned char buffer_properties[] = "\x00\x00\x00\x00\x00";
+    static struct simpletlv_member tv_buffer[] = {
+      {CAC_PROPERTIES_OBJECT_ID, 2, {/*.value = object_id*/},
+          SIMPLETLV_TYPE_LEAF},
+      {CAC_PROPERTIES_BUFFER_PROPERTIES, 5, {/*.value = buffer_properties*/},
+          SIMPLETLV_TYPE_LEAF},
+    };
+    unsigned char applet_information[] = "\x10\x02\x06\x02\x03";
+    unsigned char number_objects = 0;
+    static struct simpletlv_member properties[7] = {
+      {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 = NULL},
+          SIMPLETLV_TYPE_COMPOUND},
+      {CAC_PROPERTIES_TV_OBJECT, 2, {.child = NULL},
+          SIMPLETLV_TYPE_COMPOUND},
+      {CAC_PROPERTIES_TV_OBJECT, 2, {.child = NULL},
+          SIMPLETLV_TYPE_COMPOUND},
+      {CAC_PROPERTIES_TV_OBJECT, 2, {.child = NULL},
+          SIMPLETLV_TYPE_COMPOUND},
+      {CAC_PROPERTIES_TV_OBJECT, 2, {.child = NULL},
+          SIMPLETLV_TYPE_COMPOUND},
+    };
+    unsigned properties_len = 2;
+    unsigned int i;
+
+    for (i = 0; i < objects_len; i++) {
+        /* Adjust Object ID based on the AID */
+        object_id[0] = objects[i][0];
+        object_id[1] = objects[i][1];
+
+        /* Create arbitrary sized buffers */
+        buffer_properties[0] = 0x01; // not a SimpleTLV
+        buffer_properties[1] = 0x60;
+        buffer_properties[2] = 0x00;
+        buffer_properties[3] = 0x60;
+        buffer_properties[4] = 0x00;
+
+        /* Inject Object ID */
+        tv_buffer[0].value.value = object_id;
+        tv_buffer[1].value.value = buffer_properties;
+
+        /* clone the object to the structure */
+        properties[2+i].value.child = simpletlv_clone(tv_buffer, 2);
+        if (properties[2+i].value.child == NULL)
+            goto failure;
+
+        properties_len++;
+        number_objects++;
+    }
+
+    /* Inject Applet Version */
+    properties[0].value.value = applet_information;
+    properties[1].value.value = &number_objects;
+
+    /* Create the private data structure */
+    applet_private = g_new0(VCardAppletPrivate, 1);
+    if (applet_private == NULL)
+        goto failure;
+
+    /* Create Object ID list */
+    applet_private->coids = g_malloc_n(objects_len, sizeof(struct coid));
+    memcpy(applet_private->coids, objects, 2*objects_len);
+    applet_private->coids_len = objects_len;
+
+    /* Clone the properties */
+    applet_private->properties_len = properties_len;
+    applet_private->properties = simpletlv_clone(properties, properties_len);
+    if (applet_private->properties == NULL)
+        goto failure;
+
+    /* clean up the allocated properties */
+    for (i = 0; i < number_objects; i++) {
+        simpletlv_free(properties[2+i].value.child, 2);
+    }
+
+    /* tag/value buffers */
+    applet_private->tag_buffer = g_malloc0(2);
+    applet_private->tag_buffer_len = 2;
+    applet_private->val_buffer = g_malloc0(2);
+    applet_private->val_buffer_len = 2;
+
+    return applet_private;
+
+failure:
+    for (i = 0; i < number_objects; i++) {
+        simpletlv_free(properties[2+i].value.child, 3);
+    }
+    if (applet_private != NULL) {
+       cac_delete_aca_applet_private(applet_private);
+    }
+    return NULL;
+}
 
 /*
  * create a new ACA applet
@@ -1530,6 +1798,36 @@ failure:
     return NULL;
 }
 
+static VCardApplet *
+cac_new_empty_applet(unsigned char *aid, unsigned int aid_len,
+                     unsigned char coids[][2], unsigned int coids_len)
+{
+    VCardAppletPrivate *applet_private;
+    VCardApplet *applet;
+
+    applet_private = cac_new_empty_applet_private(coids, coids_len);
+    if (applet_private == NULL) {
+        goto failure;
+    }
+
+    applet = vcard_new_applet(cac_common_process_apdu_read,
+        NULL, aid, aid_len);
+    if (applet == NULL) {
+        goto failure;
+    }
+
+    vcard_set_applet_private(applet, applet_private,
+                             cac_delete_empty_applet_private);
+    applet_private = NULL;
+
+    return applet;
+
+failure:
+    if (applet_private != NULL) {
+        cac_delete_empty_applet_private(applet_private);
+    }
+    return NULL;
+}
 
 /*
  * Initialize the cac card. This is the only public function in this file. All
@@ -1545,6 +1843,13 @@ cac_card_init(VReader *reader, VCard *card,
 {
     int i;
     VCardApplet *applet;
+    unsigned char coids[][2] = {{0x02, 0xfb}};
+    unsigned char acf_coids[][2] = {
+        {0x30, 0x00},
+        {0x60, 0x10},
+        {0x60, 0x30},
+        {0x90, 0x00},
+    };
 
     /* CAC Cards are VM Cards */
     vcard_set_type(card, VCARD_VM);
@@ -1579,6 +1884,61 @@ cac_card_init(VReader *reader, VCard *card,
     }
     vcard_add_applet(card, applet);
 
+    /* Three more empty applets without buffer */
+    /* 02 F0 */
+    applet = cac_new_empty_applet(cac_02f0_aid, sizeof(cac_02f0_aid), NULL, 0);
+    if (applet == NULL) {
+        goto failure;
+    }
+    vcard_add_applet(card, applet);
+
+    /* 02 F1 */
+    applet = cac_new_empty_applet(cac_02f1_aid, sizeof(cac_02f1_aid), NULL, 0);
+    if (applet == NULL) {
+        goto failure;
+    }
+    vcard_add_applet(card, applet);
+
+    /* 02 F2 */
+    applet = cac_new_empty_applet(cac_02f2_aid, sizeof(cac_02f2_aid), NULL, 0);
+    if (applet == NULL) {
+        goto failure;
+    }
+    vcard_add_applet(card, applet);
+
+    /* Empty generic applet (0x02FB) */
+    applet = cac_new_empty_applet(cac_02fb_aid, sizeof(cac_02fb_aid),
+        coids, 1);
+    if (applet == NULL) {
+        goto failure;
+    }
+    vcard_add_applet(card, applet);
+
+    /* Empty generic applet (0x1201) */
+    coids[0][0] = 0x12;
+    coids[0][1] = 0x01;
+    applet = cac_new_empty_applet(cac_1201_aid, sizeof(cac_1201_aid), coids, 1);
+    if (applet == NULL) {
+        goto failure;
+    }
+    vcard_add_applet(card, applet);
+
+    /* Empty generic applet (0x1202) */
+    coids[0][1] = 0x02;
+    applet = cac_new_empty_applet(cac_1202_aid, sizeof(cac_1202_aid), coids, 1);
+    if (applet == NULL) {
+        goto failure;
+    }
+    vcard_add_applet(card, applet);
+
+    /* Access Control File */
+    applet = cac_new_empty_applet(cac_access_control_aid,
+        sizeof(cac_access_control_aid), acf_coids, 4);
+    if (applet == NULL) {
+        goto failure;
+    }
+    vcard_add_applet(card, applet);
+
     /* GP applet is created from vcard_emul_type() */
 
     return VCARD_DONE;
diff --git a/src/cac.h b/src/cac.h
index f21b119..ff8fa0e 100644
--- a/src/cac.h
+++ b/src/cac.h
@@ -25,6 +25,8 @@
 /* PKI applet tags */
 #define CAC_PKI_TAG_CERTIFICATE               0x70
 #define CAC_PKI_TAG_CERTINFO                  0x71
+#define CAC_PKI_TAG_MSCUID                    0x72
+#define CAC_PKI_TAG_ERROR_DETECTION_CODE      0xFE
 
 /* ACA applet tags */
 #define CAC_ACR_NUM_ENTRIES                   0xA1
diff --git a/tests/libcacard.c b/tests/libcacard.c
index 5323a2c..b1c5604 100644
--- a/tests/libcacard.c
+++ b/tests/libcacard.c
@@ -12,9 +12,10 @@ static GMutex mutex;
 static GCond cond;
 
 enum {
-    TEST_PKI,
-    TEST_CCC,
-    TEST_ACA
+    TEST_PKI = 1,
+    TEST_CCC = 2,
+    TEST_ACA = 3,
+    TEST_GENERIC = 4
 };
 
 static gpointer
@@ -150,7 +151,8 @@ static void test_xfer(void)
     vreader_free(reader); /* get by id ref */
 }
 
-static void get_properties(VReader *reader, int object_type)
+static void get_properties_coid(VReader *reader, const unsigned char coid[2],
+                                int object_type)
 {
     int dwRecvLength = APDUBufSize;
     VReaderStatus status;
@@ -197,8 +199,8 @@ static void get_properties(VReader *reader, int object_type)
             g_debug("The generated SimpleTLV can not be parsed");
             g_assert_not_reached();
         }
-        g_assert_cmpint(vlen, <=, p_end - p);
         g_debug("Tag: 0x%02x, Len: %lu", tag, vlen);
+        g_assert_cmpint(vlen, <=, p_end - p);
 
         switch (tag) {
         case 0x01: /* Applet Information */
@@ -233,20 +235,13 @@ static void get_properties(VReader *reader, int object_type)
 
                 switch (tag2) {
                 case 0x41: /* Object ID */
-                    if (object_type == TEST_PKI) {
-                        // XXX only the first PKI for now
-                        g_assert_cmpmem(p2, vlen2, "\x01\x00", 2);
-                    } else if (object_type == TEST_CCC) {
-                        g_assert_cmpmem(p2, vlen2, "\xDB\x00", 2);
-                    } else {
-                        g_debug("Got unknown object type");
-                        g_assert_not_reached();
-                    }
+                    g_assert_cmpmem(p2, vlen2, coid, 2);
                     break;
 
                 case 0x42: /* Buffer properties */
                     g_assert_cmpint(vlen2, ==, 5);
-                    g_assert_cmpint(p2[0], ==, 0x00);
+                    if (object_type != TEST_GENERIC)
+                        g_assert_cmpint(p2[0], ==, 0x00);
                     break;
 
                 case 0x43: /* PKI properties */
@@ -284,6 +279,7 @@ static void get_properties(VReader *reader, int object_type)
 
     g_assert_cmpint(have_applet_information, ==, 1);
 
+    /* Try to list only some properties */
     dwRecvLength = APDUBufSize;
     status = vreader_xfr_bytes(reader,
                                get_properties_tag, sizeof(get_properties_tag),
@@ -305,6 +301,35 @@ static void get_properties(VReader *reader, int object_type)
     g_assert_cmpint(pbRecvBuffer[dwRecvLength-1], ==, 0x00);
 }
 
+static void get_properties(VReader *reader, int object_type)
+{
+    unsigned char coid[2];
+    switch (object_type) {
+    case TEST_PKI:
+        // XXX only the first PKI for now
+        coid[0] = 0x01;
+        coid[1] = 0x00;
+        get_properties_coid(reader, coid, object_type);
+        break;
+
+    case TEST_CCC:
+        coid[0] = 0xDB;
+        coid[1] = 0x00;
+        get_properties_coid(reader, coid, object_type);
+        break;
+
+    case TEST_ACA:
+        coid[0] = 0x03;
+        coid[1] = 0x00;
+        get_properties_coid(reader, coid, object_type);
+        break;
+
+    default:
+        g_debug("Got unknown object type");
+        g_assert_not_reached();
+    }
+}
+
 static void parse_acr(uint8_t *buf, int buflen)
 {
     uint8_t *p, *p_end;
@@ -559,10 +584,13 @@ static void read_buffer(VReader *reader, uint8_t type, int object_type)
                                pbRecvBuffer, &dwRecvLength);
     g_assert_cmpint(status, ==, VREADER_OK);
     g_assert_cmpint(dwRecvLength, ==, 4);
-    g_assert_cmphex(pbRecvBuffer[2], ==, VCARD7816_SW1_SUCCESS);
-    g_assert_cmphex(pbRecvBuffer[3], ==, 0x00);
+    g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_SUCCESS);
+    g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x00);
 
     dwLength = (pbRecvBuffer[0] & 0xff) | ((pbRecvBuffer[1] << 8) & 0xff);
+    if (dwLength == 0)
+        return;
+
     data = g_malloc(dwLength);
     offset = 0x02;
     do {
@@ -617,54 +645,69 @@ static void read_buffer(VReader *reader, uint8_t type, int object_type)
     g_free(data);
 }
 
-static void select_aid(VReader *reader, int type)
+static void select_aid(VReader *reader, unsigned char *aid, unsigned int aid_len)
 {
     VReaderStatus status;
     int dwRecvLength = APDUBufSize;
     uint8_t pbRecvBuffer[APDUBufSize];
+    uint8_t selfile[] = {
+        0x00, 0xa4, 0x04, 0x00, 0x07, 0xa0, 0x00, 0x00, 0x00, 0x79, 0x01, 0x00
+    };
+    size_t selfile_len = sizeof(selfile);
+
+    g_assert_cmpint(aid_len, ==, 7);
+    memcpy(&selfile[5], aid, aid_len);
+
+    g_debug("%s: Add applet with AID 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x",
+        __func__, aid[0], aid[1], aid[2], aid[3], aid[4], aid[5], aid[6]);
+    g_assert_nonnull(reader);
+    status = vreader_xfr_bytes(reader,
+                               selfile, selfile_len,
+                               pbRecvBuffer, &dwRecvLength);
+    g_assert_cmpint(status, ==, VREADER_OK);
+    g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_RESPONSE_BYTES);
+    g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], >, 0);
+}
+
+static void select_applet(VReader *reader, int type)
+{
     uint8_t selfile_ccc[] = {
         /* Select CCC Applet */
-        0x00, 0xa4, 0x04, 0x00, 0x07, 0xa0, 0x00, 0x00, 0x01, 0x16, 0xDB, 0x00
+        0xa0, 0x00, 0x00, 0x01, 0x16, 0xDB, 0x00
     };
     uint8_t selfile_aca[] = {
         /* Select ACA Applet */
-        0x00, 0xa4, 0x04, 0x00, 0x07, 0xa0, 0x00, 0x00, 0x00, 0x79, 0x03, 0x00
+        0xa0, 0x00, 0x00, 0x00, 0x79, 0x03, 0x00
     };
     uint8_t selfile_pki[] = {
         /* Select first PKI Applet */
-        0x00, 0xa4, 0x04, 0x00, 0x07, 0xa0, 0x00, 0x00, 0x00, 0x79, 0x01, 0x00
+        0xa0, 0x00, 0x00, 0x00, 0x79, 0x01, 0x00
     };
-    uint8_t *selfile = NULL;
-    size_t selfile_len = 0;
+    uint8_t *aid = NULL;
+    size_t aid_len = 0;
 
     switch (type) {
     case TEST_PKI:
-        selfile = selfile_pki;
-        selfile_len = sizeof(selfile_pki);
+        aid = selfile_pki;
+        aid_len = sizeof(selfile_pki);
         break;
 
     case TEST_CCC:
-        selfile = selfile_ccc;
-        selfile_len = sizeof(selfile_ccc);
+        aid = selfile_ccc;
+        aid_len = sizeof(selfile_ccc);
         break;
 
     case TEST_ACA:
-        selfile = selfile_aca;
-        selfile_len = sizeof(selfile_aca);
+        aid = selfile_aca;
+        aid_len = sizeof(selfile_aca);
         break;
 
     default:
         g_assert_not_reached();
     }
-    g_assert_nonnull(selfile);
+    g_assert_nonnull(aid);
 
-    g_assert_nonnull(reader);
-    status = vreader_xfr_bytes(reader,
-                               selfile, selfile_len,
-                               pbRecvBuffer, &dwRecvLength);
-    g_assert_cmpint(status, ==, VREADER_OK);
-    g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_RESPONSE_BYTES);
-    g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], >, 0);
+    select_aid(reader, aid, aid_len);
 }
 
 static void do_login(VReader *reader)
@@ -740,7 +783,7 @@ static void test_cac_pki(void)
     VReader *reader = vreader_get_reader_by_id(0);
 
     /* select the first PKI applet */
-    select_aid(reader, TEST_PKI);
+    select_applet(reader, TEST_PKI);
 
     /* get properties */
     get_properties(reader, TEST_PKI);
@@ -759,7 +802,7 @@ static void test_cac_ccc(void)
     VReader *reader = vreader_get_reader_by_id(0);
 
     /* select the CCC */
-    select_aid(reader, TEST_CCC);
+    select_applet(reader, TEST_CCC);
 
     /* get properties */
     get_properties(reader, TEST_CCC);
@@ -778,7 +821,7 @@ static void test_cac_aca(void)
     VReader *reader = vreader_get_reader_by_id(0);
 
     /* select the ACA */
-    select_aid(reader, TEST_ACA);
+    select_applet(reader, TEST_ACA);
 
     /* get properties */
     get_properties(reader, TEST_ACA);
@@ -794,7 +837,7 @@ static void test_login(void)
     VReader *reader = vreader_get_reader_by_id(0);
 
     /* select the ACA */
-    select_aid(reader, TEST_ACA);
+    select_applet(reader, TEST_ACA);
 
     do_login(reader);
 
@@ -806,12 +849,12 @@ static void test_sign(void)
     VReader *reader = vreader_get_reader_by_id(0);
 
     /* select the ACA */
-    select_aid(reader, TEST_ACA);
+    select_applet(reader, TEST_ACA);
 
     do_login(reader);
 
     /* select the PKI */
-    select_aid(reader, TEST_PKI);
+    select_applet(reader, TEST_PKI);
 
     do_sign(reader);
 
@@ -854,7 +897,7 @@ static void test_get_response(void)
     };
 
     /* select CCC */
-    select_aid(reader, TEST_CCC);
+    select_applet(reader, TEST_CCC);
 
     /* read buffer without response buffer */
     dwRecvLength = 2;
@@ -880,6 +923,30 @@ static void test_get_response(void)
     vreader_free(reader); /* get by id ref */
 }
 
+static void test_other_applets(void)
+{
+    uint8_t applet_02fb[] = {
+        /*Read Buffer  OFFSET         TYPE LENGTH */
+        0xA0, 0x00, 0x00, 0x00, 0x79, 0x02, 0xFB
+    };
+    uint8_t coid[2] = {0x02, 0xFB};
+    VReader *reader = vreader_get_reader_by_id(0);
+
+    /* select the empty applet A00000007902FB, which should be empty*/
+    select_aid(reader, applet_02fb, sizeof(applet_02fb));
+
+    /* get properties */
+    get_properties_coid(reader, coid, TEST_GENERIC);
+
+    /* get the TAG buffer length */
+    read_buffer(reader, CAC_FILE_TAG, TEST_GENERIC);
+
+    /* get the VALUE buffer length */
+    read_buffer(reader, CAC_FILE_VALUE, TEST_GENERIC);
+
+    vreader_free(reader); /* get by id ref */
+}
+
 static void libcacard_finalize(void)
 {
     VReader *reader = vreader_get_reader_by_id(0);
@@ -914,6 +981,7 @@ int main(int argc, char *argv[])
     g_test_add_func("/libcacard/get-response", test_get_response);
     g_test_add_func("/libcacard/login", test_login);
     g_test_add_func("/libcacard/sign", test_sign);
+    g_test_add_func("/libcacard/other-applets", test_other_applets);
     g_test_add_func("/libcacard/remove", test_remove);
 
     ret = g_test_run();
-- 
2.17.1



More information about the Spice-devel mailing list