From ca92ec661f21482bd8f93448d5d8db893e27e55b Mon Sep 17 00:00:00 2001 From: "ludovic.rousseau" Date: Fri, 9 Nov 2007 08:35:23 +0000 Subject: [PATCH] patch from Douglas E. Engert for bug #165 git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@3292 c6295689-39f2-0310-b995-f0e70906c6a9 --- src/libopensc/card-piv.c | 172 +++++++++++++++++++++++++++---------- src/libopensc/pkcs15-piv.c | 29 +++++-- 2 files changed, 149 insertions(+), 52 deletions(-) diff --git a/src/libopensc/card-piv.c b/src/libopensc/card-piv.c index e2c09c1e..006b4a28 100644 --- a/src/libopensc/card-piv.c +++ b/src/libopensc/card-piv.c @@ -47,6 +47,7 @@ typedef struct piv_private_data { sc_file_t *aid_file; int enumtag; int selected_obj; /* The index into the piv_objects last selected */ + int return_only_cert; /* return the cert from the object */ int eof; size_t max_recv_size; /* saved size, need to lie to pkcs15_read_file */ size_t max_send_size; @@ -118,19 +119,23 @@ struct piv_aid { /* The Generic entry should be the "A0 00 00 03 08 00 00 01 00 " * NIST published this on 10/6/2005 + * 800-73-2 is due for release 11/2007. + * 800-73-2 Part 1 now refers to version "02 00" + * i.e. "A0 00 00 03 08 00 00 01 00 02 00". + * but we dont need the version number. but could get it from the PIX. */ static struct piv_aid piv_aids[] = { {SC_CARD_TYPE_PIV_II_GENERIC, - 9, 11, (u8 *) "\xA0\x00\x00\x03\x08\x00\x00\x10\x00\x01\x00" }, + 9, 9, (u8 *) "\xA0\x00\x00\x03\x08\x00\x00\x10\x00" }, {0, 9, 0, NULL } }; enum { PIV_OBJ_CCC = 1, PIV_OBJ_CHUI, + PIV_OBJ_UCHUI, /* new with 800-73-2 */ PIV_OBJ_X509_PIV_AUTH, - PIV_OBJ_CHF1, - PIV_OBJ_CHF2, + PIV_OBJ_CHF, PIV_OBJ_PI, PIV_OBJ_CHFI, PIV_OBJ_X509_DS, @@ -155,28 +160,31 @@ struct piv_object { size_t maxlen; /* advisory, used with select_file, but we can read larger */ }; +/* maxlen values are advisory only, we can read larger if needed. */ static struct piv_object piv_objects[] = { { PIV_OBJ_CCC, "Card Capability Container", - "2.16.840.1.101.3.7.1.219.0", 3, "\x5F\xC1\x07", "\xDB\x00", 266}, + "2.16.840.1.101.3.7.1.219.0", 3, "\x5F\xC1\x07", "\xDB\x00", 266+30+3}, { PIV_OBJ_CHUI, "Card Holder Unique Identifier", - "2.16.840.1.101.3.7.2.48.0", 3, "\x5F\xC1\x02", "\x30\x00", 3379}, /* Updated per SP800-73-1 Errata */ + "2.16.840.1.101.3.7.2.48.0", 3, "\x5F\xC1\x02", "\x30\x00", 3392+20+4}, + { PIV_OBJ_CHUI, "Unsigned Card Holder Unique Identifier", + "2.16.840.1.101.3.7.2.48.1", 3, "\x5F\xC1\x04", "\x30\x10", 67+14+4}, { PIV_OBJ_X509_PIV_AUTH, "X.509 Certificate for PIV Authentication", - "2.16.840.1.101.3.7.2.1.1", 3, "\x5F\xC1\x05", "\x01\x01", 1856+4+400} , + "2.16.840.1.101.3.7.2.1.1", 3, "\x5F\xC1\x05", "\x01\x01", 1895+9+4+400} , /* extra 400 is hack for MultOS card which returns 2200 bytes */ - { PIV_OBJ_CHF1, "Card Holder Fingerprints", - "2.16.840.1.101.3.7.2.96.16", 3, "\x5F\xC1\x03", "\x60\x10", 7768}, + { PIV_OBJ_CHF, "Card Holder Fingerprints", + "2.16.840.1.101.3.7.2.96.16", 3, "\x5F\xC1\x03", "\x60\x10", 4000+4+4}, { PIV_OBJ_PI, "Printed Information", - "2.16.840.1.101.3.7.2.48.1", 3, "\x5F\xC1\x09", "\x30\x01", 106}, + "2.16.840.1.101.3.7.2.48.1", 3, "\x5F\xC1\x09", "\x30\x01", 146+18+3}, { PIV_OBJ_CHFI, "Card Holder Facial Image", - "2.16.840.1.101.3.7.2.96.48", 3, "\x5F\xC1\x08", "\x60\x30", 12704}, + "2.16.840.1.101.3.7.2.96.48", 3, "\x5F\xC1\x08", "\x60\x30", 12704+5+4}, { PIV_OBJ_X509_DS, "X.509 Certificate for Digital Signature", - "2.16.840.1.101.3.7.2.1.0", 3, "\x5F\xC1\x0A", "\x01\x00", 1856+4}, + "2.16.840.1.101.3.7.2.1.0", 3, "\x5F\xC1\x0A", "\x01\x00", 1895+9+4+400}, { PIV_OBJ_X509_KM, "X.509 Certificate for Key Management", - "2.16.840.1.101.3.7.2.1.2", 3, "\x5F\xC1\x0B", "\x01\x02", 1856+4}, + "2.16.840.1.101.3.7.2.1.2", 3, "\x5F\xC1\x0B", "\x01\x02", 1895+9+4+400}, { PIV_OBJ_X509_CARD_AUTH, "X.509 Certificate for Card Authentication", - "2.16.840.1.101.3.7.2.5.0", 3, "\x5F\xC1\x01", "\x05\x00", 1856+4}, + "2.16.840.1.101.3.7.2.5.0", 3, "\x5F\xC1\x01", "\x05\x00", 1895+9+4+400}, { PIV_OBJ_SEC_OBJ, "Security Object", - "2.16.840.1.101.3.7.2.144.0", 3, "\x5F\xC1\x06", "\x90\x00", 1000}, + "2.16.840.1.101.3.7.2.144.0", 3, "\x5F\xC1\x06", "\x90\x00", 1000+30+4+4}, /* following not standard , to be used by piv-tool only for testing */ { PIV_OBJ_9B03, "3DES-ECB ADM", "2.16.840.1.101.3.7.2.9999.3", 2, "\x9B\x03", "\x9B\x03", 24}, @@ -186,13 +194,13 @@ static struct piv_object piv_objects[] = { * but still use the "9x06" name. */ { PIV_OBJ_9A06, "RSA 9A Pub key from last genkey", - "2.16.840.1.101.3.7.2.9999.20", 2, "\x9A\x06", "\x9A\x06", 512}, + "2.16.840.1.101.3.7.2.9999.20", 2, "\x9A\x06", "\x9A\x06", 2048}, { PIV_OBJ_9C06, "Pub 9C key from last genkey", - "2.16.840.1.101.3.7.2.9999.21", 2, "\x9C\x06", "\x9C\x06", 512}, + "2.16.840.1.101.3.7.2.9999.21", 2, "\x9C\x06", "\x9C\x06", 2048}, { PIV_OBJ_9D06, "Pub 9D key from last genkey", - "2.16.840.1.101.3.7.2.9999.22", 2, "\x9D\x06", "\x9D\x06", 512}, + "2.16.840.1.101.3.7.2.9999.22", 2, "\x9D\x06", "\x9D\x06", 2048}, { PIV_OBJ_9E06, "Pub 9E key from last genkey", - "2.16.840.1.101.3.7.2.9999.23", 2, "\x9E\x06", "\x9E\x06", 512}, + "2.16.840.1.101.3.7.2.9999.23", 2, "\x9E\x06", "\x9E\x06", 2048}, { 0, "", "", 0, "", "", 0} }; @@ -525,8 +533,9 @@ static int piv_find_aid(sc_card_t * card, sc_file_t *aid_file) sc_debug(card->ctx,"found PIX"); /* early cards returned full AID, rather then just the pix */ - for (i = 0; piv_aids[i].len_short != 0; i++) { - if ((pixlen >= 6 && memcmp(pix, piv_aids[i].value + 5, 6) == 0) + for (i = 0; piv_aids[i].len_long != 0; i++) { + if ((pixlen >= 6 && memcmp(pix, piv_aids[i].value + 5, + piv_aids[i].len_long - 5 ) == 0) || ((pixlen >= piv_aids[i].len_short && memcmp(pix, piv_aids[i].value, piv_aids[i].len_short) == 0))) { @@ -619,10 +628,10 @@ static int piv_get_data(sc_card_t * card, unsigned int enumtag, * the PIV card will only recover the public key during a generate * key operation. If the piv-tool was used it would save this * as an OpenSSL EVP_KEY PEM using the -o parameter - * we will look to see there as a file and load it + * we will look to see if there is a file then load it * this is ugly, and maybe the pkcs15 cache would work * but we only need it to get the OpenSSL req with engine to work. - * Each of the 3 keys with certs has its own file. + * Each of the 4 keys with certs has its own file. */ switch (piv_objects[enumtag].enumtag) { @@ -723,6 +732,7 @@ static int piv_handle_certificate_data(sc_card_t *card, piv_cache_item* item; /* get the certificate out */ tag = (u8 *) sc_asn1_find_tag(card->ctx, data, length, 0x71, &taglen); + /* 800-72-1 not clear if this is 80 or 01 Sent comment to NIST for 800-72-2 */ if (tag && (((*tag) & 0x80) || ((*tag) & 0x01))) { compressed = 1; } @@ -880,7 +890,10 @@ static int piv_read_binary(sc_card_t *card, unsigned int idx, case PIV_OBJ_X509_DS: case PIV_OBJ_X509_KM: case PIV_OBJ_X509_CARD_AUTH: - r = piv_handle_certificate_data(card, enumtag, idx, buf, count, body, bodylen); + if (priv->return_only_cert) + r = piv_handle_certificate_data(card, enumtag, idx, buf, count, body, bodylen); + else + r = piv_handle_data(card, enumtag, idx, buf, count, rbuf, rbuflen); break; case PIV_OBJ_9A06: case PIV_OBJ_9C06: @@ -1075,25 +1088,37 @@ err: /* * will only deal with 3des for now + * assumptions include: + * size of encrypted data is same as unencrypted + * challenges, nonces etc from card are less then 114 (keeps tags simple) */ static int piv_general_mutual_authenticate(sc_card_t *card, unsigned int key_ref, unsigned int alg_id) { int r; - size_t N; - int locked = 0, outl; + int N; + int locked = 0, outl, outl2; u8 *rbuf = NULL; size_t rbuflen; u8 nonce[8] = {0xDE, 0xE0, 0xDE, 0xE1, 0xDE, 0xE2, 0xDE, 0xE3}; u8 sbuf[255], key[24]; u8 *p, *q; EVP_CIPHER_CTX ctx; + const EVP_CIPHER *cipher; SC_FUNC_CALLED(card->ctx,1); EVP_CIPHER_CTX_init(&ctx); + switch (alg_id) { + case 1: cipher=EVP_des_ede3_ecb(); break; + case 2: cipher=EVP_des_ede3_cbc(); break; + case 3: cipher=EVP_des_ede3_ecb(); break; + case 4: cipher=EVP_des_ede3_cbc(); break; + default: cipher=EVP_des_ede3_ecb(); break; + } + r = piv_get_3des_key(card, key); if (r != SC_SUCCESS) goto err; @@ -1123,18 +1148,32 @@ static int piv_general_mutual_authenticate(sc_card_t *card, r = SC_ERROR_INVALID_DATA; goto err; } - N = *(rbuf + 3); /* assuming 2 * N + 6 < 128 and N = 8 */ + N = *(rbuf + 3); /* assuming N + sizeof(nonce) + 6 < 128 */ - /* needs work, as challenge may be other then 8 bytes */ /* prepare the response */ p = sbuf; *p++ = 0x7c; - *p++ = 2 * N + 4; + *p++ = N + sizeof(nonce)+ 4; *p++ = 0x80; *p++ = (u8)N; - EVP_DecryptInit_ex(&ctx, EVP_des_ede3(), NULL, key, NULL); - if (!EVP_DecryptUpdate(&ctx, p, &outl, q, 8)) { + /* decrypt the data from the card */ + if (!EVP_DecryptInit(&ctx, cipher, key, NULL)) { + /* may fail if des parity of key is wrong. depends on OpenSSL options */ + r = SC_ERROR_INTERNAL; + goto err; + } + EVP_CIPHER_CTX_set_padding(&ctx,0); + if (!EVP_DecryptUpdate(&ctx, p, &outl, q, N)) { + r = SC_ERROR_INTERNAL; + goto err; + } + if(!EVP_DecryptFinal(&ctx, p+outl, &outl2)) { + r = SC_ERROR_INTERNAL; + goto err; + } + + if (outl+outl2 != N) { r = SC_ERROR_INTERNAL; goto err; } @@ -1142,9 +1181,9 @@ static int piv_general_mutual_authenticate(sc_card_t *card, p += N; *p++ = 0x81; - *p++ = N; - memcpy(p, &nonce, N); /* we use a fixed nonce for now */ - p += N; + *p++ = sizeof(nonce); + memcpy(p, &nonce, sizeof(nonce)); /* we use a fixed nonce for now */ + p += sizeof(nonce); free(rbuf); rbuf = NULL; @@ -1159,14 +1198,28 @@ static int piv_general_mutual_authenticate(sc_card_t *card, r = SC_ERROR_INVALID_DATA; goto err; } + N = *(rbuf + 3); p = sbuf; - if (!EVP_DecryptUpdate(&ctx, p, &outl, q, 8)) { + + EVP_CIPHER_CTX_cleanup(&ctx); + EVP_CIPHER_CTX_init(&ctx); + + if (!EVP_DecryptInit(&ctx, cipher, key, NULL)) { + r = SC_ERROR_INTERNAL; + goto err; + } + EVP_CIPHER_CTX_set_padding(&ctx,0); + if (!EVP_DecryptUpdate(&ctx, p, &outl, q, N)) { + r = SC_ERROR_INTERNAL; + goto err; + } + if(!EVP_DecryptFinal(&ctx, p+outl, &outl2)) { r = SC_ERROR_INTERNAL; goto err; } - if (memcmp(nonce, p, N) != 0) { + if (outl+outl2 != sizeof(nonce) || memcmp(nonce, p, sizeof(nonce)) != 0) { sc_debug(card->ctx, "mutual authentication failed, card returned wrong value"); r = SC_ERROR_DECRYPT_FAILED; goto err; @@ -1187,17 +1240,28 @@ static int piv_general_external_authenticate(sc_card_t *card, unsigned int key_ref, unsigned int alg_id) { /* unused: piv_private_data_t * priv = PIV_DATA(card); */ - int r, outl; + int r, outl, outl2; + int N; + int locked = 0; u8 *rbuf = NULL; size_t rbuflen; u8 sbuf[255], key[24]; u8 *p, *q; EVP_CIPHER_CTX ctx; + const EVP_CIPHER *cipher; SC_FUNC_CALLED(card->ctx,1); EVP_CIPHER_CTX_init(&ctx); + switch (alg_id) { + case 1: cipher=EVP_des_ede3_ecb(); break; + case 2: cipher=EVP_des_ede3_cbc(); break; + case 3: cipher=EVP_des_ede3_ecb(); break; + case 4: cipher=EVP_des_ede3_cbc(); break; + default: cipher=EVP_des_ede3_ecb(); break; + } + r = piv_get_3des_key(card, key); if (r != SC_SUCCESS) goto err; @@ -1205,6 +1269,7 @@ static int piv_general_external_authenticate(sc_card_t *card, r = sc_lock(card); if (r != SC_SUCCESS) goto err; + locked = 1; p = sbuf; q = rbuf; @@ -1227,25 +1292,38 @@ static int piv_general_external_authenticate(sc_card_t *card, goto err; } - /* needs work, as challenge may be other then 8 bytes */ + /* assuming challenge and response are same size i.e. des3 */ p = sbuf; *p++ = 0x7c; *p++ = *(rbuf + 1); *p++ = 0x82; *p++ = *(rbuf + 3); + N = *(rbuf + 3); /* assuming 2 * N + 6 < 128 */ - EVP_EncryptInit_ex(&ctx, EVP_des_ede3(), NULL, key, NULL); - if (!EVP_EncryptUpdate(&ctx, p, &outl, q, 8)) { + if (!EVP_EncryptInit(&ctx, cipher, key, NULL)) { + r = SC_ERROR_INTERNAL; + goto err; + } + EVP_CIPHER_CTX_set_padding(&ctx,0); + if (!EVP_EncryptUpdate(&ctx, p, &outl, q, N)) { r = SC_ERROR_INTERNAL; goto err; } - p += *(rbuf + 3); + if(!EVP_EncryptFinal(&ctx, p+outl, &outl2)) { + r = SC_ERROR_INTERNAL; + goto err; + } + if (outl+outl2 != N) { + r = SC_ERROR_INTERNAL; + goto err; + } + p += N; r = piv_general_io(card, 0x87, alg_id, key_ref, sbuf, p - sbuf, NULL, NULL); - sc_unlock(card); - err: + if (locked) + sc_unlock(card); EVP_CIPHER_CTX_cleanup(&ctx); sc_mem_clear(key, sizeof(key)); if (rbuf) @@ -1535,13 +1613,19 @@ static int piv_select_file(sc_card_t *card, const sc_path_t *in_path, path += 2; pathlen -= 2; } - if (pathlen != 2) - SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS); i = piv_find_obj_by_containerid(card, path); if (i< 0) SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_FILE_NOT_FOUND); + + /* + * pkcs15 will use a 2 byte path or a 4 byte path + * with cece added to path to request only the cert from the cert obj + * PIV "Container ID" is used as the path, and are two bytes long + */ + priv->return_only_cert = (pathlen == 4 && path[2] == 0xce && path[3] == 0xce); + priv->selected_obj = i; priv->eof = 0; diff --git a/src/libopensc/pkcs15-piv.c b/src/libopensc/pkcs15-piv.c index 2c5885e9..a7a6b805 100644 --- a/src/libopensc/pkcs15-piv.c +++ b/src/libopensc/pkcs15-piv.c @@ -111,18 +111,29 @@ static int piv_detect_card(sc_pkcs15_card_t *p15card) static int sc_pkcs15emu_piv_init(sc_pkcs15_card_t *p15card) { + /* The cert objects will return all the data */ const objdata objects[] = { {"1", "Card Capability Container", "2.16.840.1.101.3.7.1.219.0", NULL, "DB00", 0}, {"2", "Card Holder Unique Identifier", "2.16.840.1.101.3.7.2.48.0", NULL, "3000", 0}, - {"3", "Card Holder Fingerprints", + {"3", "Unsigned Card Holder Unique Identifier", + "2.16.840.1.101.3.7.2.48.2", NULL, "3002", 0}, + {"4", "X.509 Certificate for PIV Authentication", + "2.16.840.1.101.3.7.2.1.1", NULL, "0101", 0}, + {"5", "Card Holder Fingerprints", "2.16.840.1.101.3.7.2.96.16", "1", "6010", SC_PKCS15_CO_FLAG_PRIVATE}, - {"4", "Printed Information", + {"6", "Printed Information", "2.16.840.1.101.3.7.2.48.1", "1", "3001", SC_PKCS15_CO_FLAG_PRIVATE}, - {"5", "Card Holder Facial Image", + {"7", "Card Holder Facial Image", "2.16.840.1.101.3.7.2.96.48", "1", "6030", SC_PKCS15_CO_FLAG_PRIVATE}, - {"6", "Security Object", + {"8", "X.509 Certificate for Digital Signature", + "2.16.840.1.101.3.7.2.1.0", NULL, "0100", 0}, + {"9", "X.509 Certificate for Key Management", + "2.16.840.1.101.3.7.2.1.2", NULL, "0102", 0}, + {"10","X.509 Certificate for Card Authentication", + "2.16.840.1.101.3.7.2.5.0", NULL, "0500", 0}, + {"11", "Security Object", "2.16.840.1.101.3.7.2.144.0", NULL, "9000", 0}, {NULL, NULL, NULL, NULL, NULL, 0} }; @@ -133,11 +144,13 @@ const objdata objects[] = { * that do enforce it. Code later on will allow SC_PKCS15_CO_FLAG_PRIVATE * to be set. */ + /* certs will be pulled out from the cert objects */ cdata certs[] = { - {"1", "Certificate for PIV Authentication", 0, "0101", 0, 0}, - {"2", "Certificate for Digital Signature", 0, "0100", 0, 0}, - {"3", "Certificate for Key Management", 0, "0102", 0, 0}, - {"4", "Certificate for Card Authentication", 0, "0500", 0, 0}, + {"1", "Certificate for PIV Authentication", 0, "0101cece", 0, 0}, + + {"2", "Certificate for Digital Signature", 0, "0100cece", 0, 0}, + {"3", "Certificate for Key Management", 0, "0102cece", 0, 0}, + {"4", "Certificate for Card Authentication", 0, "0500cece", 0, 0}, {NULL, NULL, 0, NULL, 0, 0} };