2017-02-27 10:05:12 +00:00
|
|
|
/*
|
|
|
|
* card-cac.c: Support for CAC from NIST SP800-73
|
|
|
|
* card-default.c: Support for cards with no driver
|
|
|
|
*
|
|
|
|
* Copyright (C) 2001, 2002 Juha Yrjölä <juha.yrjola@iki.fi>
|
|
|
|
* Copyright (C) 2005,2006,2007,2008,2009,2010 Douglas E. Engert <deengert@anl.gov>
|
|
|
|
* Copyright (C) 2006, Identity Alliance, Thomas Harning <thomas.harning@identityalliance.com>
|
|
|
|
* Copyright (C) 2007, EMC, Russell Larner <rlarner@rsa.com>
|
2018-05-22 13:55:25 +00:00
|
|
|
* Copyright (C) 2016 - 2018, Red Hat, Inc.
|
2017-02-27 10:05:12 +00:00
|
|
|
*
|
|
|
|
* CAC driver author: Robert Relyea <rrelyea@redhat.com>
|
2018-05-22 13:55:25 +00:00
|
|
|
* Further work: Jakub Jelen <jjelen@redhat.com>
|
2017-02-27 10:05:12 +00:00
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#if HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
#include <io.h>
|
|
|
|
#else
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ENABLE_OPENSSL
|
2018-10-30 16:27:28 +00:00
|
|
|
#include <openssl/sha.h>
|
2017-02-27 10:05:12 +00:00
|
|
|
#endif /* ENABLE_OPENSSL */
|
|
|
|
|
|
|
|
#include "internal.h"
|
|
|
|
#include "simpletlv.h"
|
|
|
|
#include "cardctl.h"
|
|
|
|
#ifdef ENABLE_ZLIB
|
|
|
|
#include "compression.h"
|
|
|
|
#endif
|
|
|
|
#include "iso7816.h"
|
2018-10-30 16:27:28 +00:00
|
|
|
#include "card-cac-common.h"
|
2019-08-12 07:23:30 +00:00
|
|
|
#include "pkcs15.h"
|
2017-02-27 10:05:12 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* CAC hardware and APDU constants
|
|
|
|
*/
|
|
|
|
#define CAC_MAX_CHUNK_SIZE 240
|
2018-05-22 13:55:25 +00:00
|
|
|
#define CAC_INS_SIGN_DECRYPT 0x42 /* A crypto operation */
|
|
|
|
#define CAC_INS_READ_FILE 0x52 /* read a TL or V file */
|
|
|
|
#define CAC_INS_GET_ACR 0x4c
|
|
|
|
#define CAC_INS_GET_PROPERTIES 0x56
|
2017-02-27 10:05:12 +00:00
|
|
|
#define CAC_P1_STEP 0x80
|
|
|
|
#define CAC_P1_FINAL 0x00
|
|
|
|
#define CAC_FILE_TAG 1
|
|
|
|
#define CAC_FILE_VALUE 2
|
|
|
|
/* TAGS in a TL file */
|
|
|
|
#define CAC_TAG_CERTIFICATE 0x70
|
|
|
|
#define CAC_TAG_CERTINFO 0x71
|
2018-04-10 08:39:18 +00:00
|
|
|
#define CAC_TAG_MSCUID 0x72
|
2017-02-27 10:05:12 +00:00
|
|
|
#define CAC_TAG_CUID 0xF0
|
|
|
|
#define CAC_TAG_CC_VERSION_NUMBER 0xF1
|
|
|
|
#define CAC_TAG_GRAMMAR_VERION_NUMBER 0xF2
|
|
|
|
#define CAC_TAG_CARDURL 0xF3
|
|
|
|
#define CAC_TAG_PKCS15 0xF4
|
|
|
|
#define CAC_TAG_ACCESS_CONTROL 0xF6
|
|
|
|
#define CAC_TAG_DATA_MODEL 0xF5
|
|
|
|
#define CAC_TAG_CARD_APDU 0xF7
|
|
|
|
#define CAC_TAG_REDIRECTION 0xFA
|
|
|
|
#define CAC_TAG_CAPABILITY_TUPLES 0xFB
|
|
|
|
#define CAC_TAG_STATUS_TUPLES 0xFC
|
|
|
|
#define CAC_TAG_NEXT_CCC 0xFD
|
|
|
|
#define CAC_TAG_ERROR_CODES 0xFE
|
2018-05-22 13:55:25 +00:00
|
|
|
#define CAC_TAG_APPLET_FAMILY 0x01
|
|
|
|
#define CAC_TAG_NUMBER_APPLETS 0x94
|
|
|
|
#define CAC_TAG_APPLET_ENTRY 0x93
|
|
|
|
#define CAC_TAG_APPLET_AID 0x92
|
|
|
|
#define CAC_TAG_APPLET_INFORMATION 0x01
|
|
|
|
#define CAC_TAG_NUMBER_OF_OBJECTS 0x40
|
|
|
|
#define CAC_TAG_TV_BUFFER 0x50
|
|
|
|
#define CAC_TAG_PKI_OBJECT 0x51
|
|
|
|
#define CAC_TAG_OBJECT_ID 0x41
|
|
|
|
#define CAC_TAG_BUFFER_PROPERTIES 0x42
|
|
|
|
#define CAC_TAG_PKI_PROPERTIES 0x43
|
|
|
|
|
|
|
|
#define CAC_APP_TYPE_GENERAL 0x01
|
|
|
|
#define CAC_APP_TYPE_SKI 0x02
|
|
|
|
#define CAC_APP_TYPE_PKI 0x04
|
|
|
|
|
|
|
|
#define CAC_ACR_ACR 0x00
|
|
|
|
#define CAC_ACR_APPLET_OBJECT 0x10
|
|
|
|
#define CAC_ACR_AMP 0x20
|
|
|
|
#define CAC_ACR_SERVICE 0x21
|
2017-02-27 10:05:12 +00:00
|
|
|
|
2020-01-29 14:07:24 +00:00
|
|
|
#define CAC_MAX_CCC_DEPTH 16
|
|
|
|
|
2017-02-27 10:05:12 +00:00
|
|
|
/* hardware data structures (returned in the CCC) */
|
|
|
|
/* part of the card_url */
|
|
|
|
typedef struct cac_access_profile {
|
|
|
|
u8 GCACR_listID;
|
|
|
|
u8 GCACR_readTagListACRID;
|
|
|
|
u8 GCACR_updatevalueACRID;
|
|
|
|
u8 GCACR_readvalueACRID;
|
|
|
|
u8 GCACR_createACRID;
|
|
|
|
u8 GCACR_deleteACRID;
|
|
|
|
u8 CryptoACR_listID;
|
|
|
|
u8 CryptoACR_getChallengeACRID;
|
|
|
|
u8 CryptoACR_internalAuthenicateACRID;
|
|
|
|
u8 CryptoACR_pkiComputeACRID;
|
|
|
|
u8 CryptoACR_readTagListACRID;
|
|
|
|
u8 CryptoACR_updatevalueACRID;
|
|
|
|
u8 CryptoACR_readvalueACRID;
|
|
|
|
u8 CryptoACR_createACRID;
|
|
|
|
u8 CryptoACR_deleteACRID;
|
|
|
|
} cac_access_profile_t;
|
|
|
|
|
|
|
|
/* part of the card url */
|
|
|
|
typedef struct cac_access_key_info {
|
|
|
|
u8 keyFileID[2];
|
|
|
|
u8 keynumber;
|
|
|
|
} cac_access_key_info_t;
|
|
|
|
|
|
|
|
typedef struct cac_card_url {
|
|
|
|
u8 rid[5];
|
|
|
|
u8 cardApplicationType;
|
|
|
|
u8 objectID[2];
|
|
|
|
u8 applicationID[2];
|
|
|
|
cac_access_profile_t accessProfile;
|
|
|
|
u8 pinID; /* not used for VM cards */
|
|
|
|
cac_access_key_info_t accessKeyInfo; /* not used for VM cards */
|
|
|
|
u8 keyCryptoAlgorithm; /* not used for VM cards */
|
|
|
|
} cac_card_url_t;
|
|
|
|
|
2018-06-06 07:39:04 +00:00
|
|
|
#define CAC_MAX_OBJECTS 16
|
2018-05-23 11:31:52 +00:00
|
|
|
|
2018-05-22 13:55:25 +00:00
|
|
|
typedef struct {
|
2018-05-23 11:31:52 +00:00
|
|
|
/* OID has two bytes */
|
2018-05-28 07:28:10 +00:00
|
|
|
unsigned char oid[2];
|
2018-05-23 11:31:52 +00:00
|
|
|
/* Format is NOT SimpleTLV? */
|
2018-05-28 07:28:10 +00:00
|
|
|
unsigned char simpletlv;
|
2018-05-23 11:31:52 +00:00
|
|
|
/* Is certificate object and private key is initialized */
|
2018-05-28 07:28:10 +00:00
|
|
|
unsigned char privatekey;
|
|
|
|
} cac_properties_object_t;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
unsigned int num_objects;
|
|
|
|
cac_properties_object_t objects[CAC_MAX_OBJECTS];
|
2018-05-22 13:55:25 +00:00
|
|
|
} cac_properties_t;
|
|
|
|
|
2017-02-27 10:05:12 +00:00
|
|
|
/*
|
|
|
|
* Flags for Current Selected Object Type
|
|
|
|
* CAC files are TLV files, with TL and V separated. For generic
|
|
|
|
* containers we reintegrate the TL anv V portions into a single
|
|
|
|
* file to read. Certs are also TLV files, but pkcs15 wants the
|
|
|
|
* actual certificate. At select time we know the patch which tells
|
|
|
|
* us what time of files we want to read. We remember that type
|
|
|
|
* so that read_binary can do the appropriate processing.
|
|
|
|
*/
|
|
|
|
#define CAC_OBJECT_TYPE_CERT 1
|
|
|
|
#define CAC_OBJECT_TYPE_TLV_FILE 4
|
2018-05-23 11:31:52 +00:00
|
|
|
#define CAC_OBJECT_TYPE_GENERIC 5
|
2017-02-27 10:05:12 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up the normal CAC paths
|
|
|
|
*/
|
|
|
|
#define CAC_2_RID "\xA0\x00\x00\x01\x16"
|
|
|
|
|
2017-11-03 09:55:35 +00:00
|
|
|
static const sc_path_t cac_ACA_Path = {
|
|
|
|
"", 0,
|
|
|
|
0,0,SC_PATH_TYPE_DF_NAME,
|
|
|
|
{ CAC_TO_AID(CAC_1_RID "\x10\x00") }
|
|
|
|
};
|
|
|
|
|
2017-02-27 10:05:12 +00:00
|
|
|
static const sc_path_t cac_CCC_Path = {
|
|
|
|
"", 0,
|
|
|
|
0,0,SC_PATH_TYPE_DF_NAME,
|
|
|
|
{ CAC_TO_AID(CAC_2_RID "\xDB\x00") }
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
2018-07-10 11:35:24 +00:00
|
|
|
* CAC general objects defined in 4.3.1.2 of CAC Applet Developer Guide Version 1.0.
|
2017-02-27 10:05:12 +00:00
|
|
|
* doubles as a source for CAC-2 labels.
|
|
|
|
*/
|
2018-07-10 11:35:24 +00:00
|
|
|
static const cac_object_t cac_objects[] = {
|
2017-02-27 10:05:12 +00:00
|
|
|
{ "Person Instance", 0x200, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME,
|
|
|
|
{ CAC_TO_AID(CAC_1_RID "\x02\x00") }}},
|
|
|
|
{ "Personnel", 0x201, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME,
|
|
|
|
{ CAC_TO_AID(CAC_1_RID "\x02\x01") }}},
|
|
|
|
{ "Benefits", 0x202, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME,
|
|
|
|
{ CAC_TO_AID(CAC_1_RID "\x02\x02") }}},
|
2018-05-02 15:33:47 +00:00
|
|
|
{ "Other Benefits", 0x203, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME,
|
|
|
|
{ CAC_TO_AID(CAC_1_RID "\x02\x03") }}},
|
2017-02-27 10:05:12 +00:00
|
|
|
{ "PKI Credential", 0x2FD, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME,
|
|
|
|
{ CAC_TO_AID(CAC_1_RID "\x02\xFD") }}},
|
|
|
|
{ "PKI Certificate", 0x2FE, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME,
|
|
|
|
{ CAC_TO_AID(CAC_1_RID "\x02\xFE") }}},
|
|
|
|
};
|
|
|
|
|
2018-07-10 11:35:24 +00:00
|
|
|
static const int cac_object_count = sizeof(cac_objects)/sizeof(cac_objects[0]);
|
2017-02-27 10:05:12 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* use the object id to find our object info on the object in our CAC-1 list
|
|
|
|
*/
|
|
|
|
static const cac_object_t *cac_find_obj_by_id(unsigned short object_id)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2018-07-10 11:35:24 +00:00
|
|
|
for (i = 0; i < cac_object_count; i++) {
|
|
|
|
if (cac_objects[i].fd == object_id) {
|
|
|
|
return &cac_objects[i];
|
2017-02-27 10:05:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Lookup the path in the pki list to see if it is a cert path
|
|
|
|
*/
|
|
|
|
static int cac_is_cert(cac_private_data_t * priv, const sc_path_t *in_path)
|
|
|
|
{
|
|
|
|
cac_object_t test_obj;
|
|
|
|
test_obj.path = *in_path;
|
|
|
|
test_obj.path.index = 0;
|
|
|
|
test_obj.path.count = 0;
|
|
|
|
|
|
|
|
return (list_contains(&priv->pki_list, &test_obj) != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Send a command and receive data.
|
|
|
|
*
|
|
|
|
* A caller may provide a buffer, and length to read. If not provided,
|
|
|
|
* an internal 4096 byte buffer is used, and a copy is returned to the
|
|
|
|
* caller. that need to be freed by the caller.
|
|
|
|
*
|
2018-04-14 17:38:34 +00:00
|
|
|
* modelled after a similar function in card-piv.c
|
2017-02-27 10:05:12 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
static int cac_apdu_io(sc_card_t *card, int ins, int p1, int p2,
|
|
|
|
const u8 * sendbuf, size_t sendbuflen, u8 ** recvbuf,
|
|
|
|
size_t * recvbuflen)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
sc_apdu_t apdu;
|
|
|
|
u8 rbufinitbuf[CAC_MAX_SIZE];
|
|
|
|
u8 *rbuf;
|
|
|
|
size_t rbuflen;
|
2017-06-26 14:04:48 +00:00
|
|
|
unsigned int apdu_case = SC_APDU_CASE_1;
|
2017-02-27 10:05:12 +00:00
|
|
|
|
|
|
|
|
|
|
|
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
|
|
|
|
2018-11-22 08:31:29 +00:00
|
|
|
sc_log(card->ctx,
|
2017-03-14 19:02:30 +00:00
|
|
|
"%02x %02x %02x %"SC_FORMAT_LEN_SIZE_T"u : %"SC_FORMAT_LEN_SIZE_T"u %"SC_FORMAT_LEN_SIZE_T"u\n",
|
|
|
|
ins, p1, p2, sendbuflen, card->max_send_size,
|
|
|
|
card->max_recv_size);
|
2017-02-27 10:05:12 +00:00
|
|
|
|
|
|
|
rbuf = rbufinitbuf;
|
|
|
|
rbuflen = sizeof(rbufinitbuf);
|
|
|
|
|
|
|
|
/* if caller provided a buffer and length */
|
|
|
|
if (recvbuf && *recvbuf && recvbuflen && *recvbuflen) {
|
|
|
|
rbuf = *recvbuf;
|
|
|
|
rbuflen = *recvbuflen;
|
|
|
|
}
|
|
|
|
|
2017-06-26 14:04:48 +00:00
|
|
|
if (recvbuf) {
|
|
|
|
if (sendbuf)
|
|
|
|
apdu_case = SC_APDU_CASE_4_SHORT;
|
|
|
|
else
|
|
|
|
apdu_case = SC_APDU_CASE_2_SHORT;
|
|
|
|
} else if (sendbuf)
|
|
|
|
apdu_case = SC_APDU_CASE_3_SHORT;
|
|
|
|
|
|
|
|
|
|
|
|
sc_format_apdu(card, &apdu, apdu_case, ins, p1, p2);
|
2017-02-27 10:05:12 +00:00
|
|
|
|
|
|
|
apdu.lc = sendbuflen;
|
|
|
|
apdu.datalen = sendbuflen;
|
|
|
|
apdu.data = sendbuf;
|
|
|
|
|
|
|
|
if (recvbuf) {
|
|
|
|
apdu.resp = rbuf;
|
|
|
|
apdu.le = (rbuflen > 255) ? 255 : rbuflen;
|
|
|
|
apdu.resplen = rbuflen;
|
|
|
|
} else {
|
|
|
|
apdu.resp = rbuf;
|
|
|
|
apdu.le = 0;
|
|
|
|
apdu.resplen = 0;
|
|
|
|
}
|
|
|
|
|
2018-11-22 08:31:29 +00:00
|
|
|
sc_log(card->ctx,
|
2017-03-14 19:02:30 +00:00
|
|
|
"calling sc_transmit_apdu flags=%lx le=%"SC_FORMAT_LEN_SIZE_T"u, resplen=%"SC_FORMAT_LEN_SIZE_T"u, resp=%p",
|
|
|
|
apdu.flags, apdu.le, apdu.resplen, apdu.resp);
|
2017-02-27 10:05:12 +00:00
|
|
|
|
|
|
|
/* with new adpu.c and chaining, this actually reads the whole object */
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
|
2018-11-22 08:31:29 +00:00
|
|
|
sc_log(card->ctx,
|
2017-03-14 19:02:30 +00:00
|
|
|
"result r=%d apdu.resplen=%"SC_FORMAT_LEN_SIZE_T"u sw1=%02x sw2=%02x",
|
|
|
|
r, apdu.resplen, apdu.sw1, apdu.sw2);
|
2017-02-27 10:05:12 +00:00
|
|
|
if (r < 0) {
|
2018-11-22 08:31:29 +00:00
|
|
|
sc_log(card->ctx, "Transmit failed");
|
2017-02-27 10:05:12 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2017-08-04 06:48:24 +00:00
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2017-02-27 10:05:12 +00:00
|
|
|
|
|
|
|
if (r < 0) {
|
2018-11-22 08:31:29 +00:00
|
|
|
sc_log(card->ctx, "Card returned error ");
|
2017-02-27 10:05:12 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (recvbuflen) {
|
|
|
|
if (recvbuf && *recvbuf == NULL) {
|
|
|
|
*recvbuf = malloc(apdu.resplen);
|
|
|
|
if (*recvbuf == NULL) {
|
|
|
|
r = SC_ERROR_OUT_OF_MEMORY;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
memcpy(*recvbuf, rbuf, apdu.resplen);
|
|
|
|
}
|
|
|
|
*recvbuflen = apdu.resplen;
|
|
|
|
r = *recvbuflen;
|
|
|
|
}
|
|
|
|
|
|
|
|
err:
|
2018-11-22 22:10:49 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
2017-02-27 10:05:12 +00:00
|
|
|
}
|
|
|
|
|
2018-05-22 13:55:25 +00:00
|
|
|
/*
|
|
|
|
* Get ACR of currently ACA applet identified by the acr_type
|
|
|
|
* 5.3.3.5 Get ACR APDU
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
cac_get_acr(sc_card_t *card, int acr_type, u8 **out_buf, size_t *out_len)
|
|
|
|
{
|
|
|
|
u8 *out = NULL;
|
|
|
|
/* XXX assuming it will not be longer than 255 B */
|
|
|
|
size_t len = 256;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
|
|
|
|
|
|
|
/* for simplicity we support only ACR without arguments now */
|
|
|
|
if (acr_type != 0x00 && acr_type != 0x10
|
|
|
|
&& acr_type != 0x20 && acr_type != 0x21) {
|
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
}
|
|
|
|
|
2018-05-28 07:28:10 +00:00
|
|
|
r = cac_apdu_io(card, CAC_INS_GET_ACR, acr_type, 0, NULL, 0, &out, &len);
|
2018-05-22 13:55:25 +00:00
|
|
|
if (len == 0) {
|
|
|
|
r = SC_ERROR_FILE_NOT_FOUND;
|
|
|
|
}
|
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
2018-05-28 07:28:10 +00:00
|
|
|
"got %"SC_FORMAT_LEN_SIZE_T"u bytes out=%p", len, out);
|
2018-05-22 13:55:25 +00:00
|
|
|
|
|
|
|
*out_len = len;
|
|
|
|
*out_buf = out;
|
|
|
|
return SC_SUCCESS;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
if (out)
|
|
|
|
free(out);
|
2018-05-28 07:28:10 +00:00
|
|
|
*out_buf = NULL;
|
2018-05-22 13:55:25 +00:00
|
|
|
*out_len = 0;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2017-02-27 10:05:12 +00:00
|
|
|
/*
|
|
|
|
* Read a CAC TLV file. Parameters specify if the TLV file is TL (Tag/Length) file or a V (value) file
|
|
|
|
*/
|
|
|
|
#define HIGH_BYTE_OF_SHORT(x) (((x)>> 8) & 0xff)
|
|
|
|
#define LOW_BYTE_OF_SHORT(x) ((x) & 0xff)
|
|
|
|
static int cac_read_file(sc_card_t *card, int file_type, u8 **out_buf, size_t *out_len)
|
|
|
|
{
|
|
|
|
u8 params[2];
|
|
|
|
u8 count[2];
|
|
|
|
u8 *out = NULL;
|
|
|
|
u8 *out_ptr;
|
|
|
|
size_t offset = 0;
|
|
|
|
size_t size = 0;
|
|
|
|
size_t left = 0;
|
|
|
|
size_t len;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
params[0] = file_type;
|
|
|
|
params[1] = 2;
|
|
|
|
|
|
|
|
/* get the size */
|
|
|
|
len = sizeof(count);
|
|
|
|
out_ptr = count;
|
|
|
|
r = cac_apdu_io(card, CAC_INS_READ_FILE, 0, 0, ¶ms[0], sizeof(params), &out_ptr, &len);
|
2018-01-11 10:16:57 +00:00
|
|
|
if (len == 0) {
|
|
|
|
r = SC_ERROR_FILE_NOT_FOUND;
|
|
|
|
}
|
2017-02-27 10:05:12 +00:00
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
left = size = lebytes2ushort(count);
|
2017-01-25 22:27:27 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
2017-03-14 19:02:30 +00:00
|
|
|
"got %"SC_FORMAT_LEN_SIZE_T"u bytes out_ptr=%p count&=%p count[0]=0x%02x count[1]=0x%02x, len=0x%04"SC_FORMAT_LEN_SIZE_T"x (%"SC_FORMAT_LEN_SIZE_T"u)",
|
2017-01-25 22:27:27 +00:00
|
|
|
len, out_ptr, &count, count[0], count[1], size, size);
|
2017-02-27 10:05:12 +00:00
|
|
|
out = out_ptr = malloc(size);
|
|
|
|
if (out == NULL) {
|
|
|
|
r = SC_ERROR_OUT_OF_MEMORY;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
for (offset += 2; left > 0; offset += len, left -= len, out_ptr += len) {
|
|
|
|
len = MIN(left, CAC_MAX_CHUNK_SIZE);
|
|
|
|
params[1] = len;
|
|
|
|
r = cac_apdu_io(card, CAC_INS_READ_FILE, HIGH_BYTE_OF_SHORT(offset), LOW_BYTE_OF_SHORT(offset),
|
|
|
|
¶ms[0], sizeof(params), &out_ptr, &len);
|
2018-01-11 10:16:57 +00:00
|
|
|
/* if there is no data, assume there is no file */
|
|
|
|
if (len == 0) {
|
|
|
|
r = SC_ERROR_FILE_NOT_FOUND;
|
|
|
|
}
|
2017-02-27 10:05:12 +00:00
|
|
|
if (r < 0) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*out_len = size;
|
|
|
|
*out_buf = out;
|
|
|
|
return SC_SUCCESS;
|
|
|
|
fail:
|
|
|
|
if (out)
|
|
|
|
free(out);
|
|
|
|
*out_len = 0;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Callers of this may be expecting a certificate,
|
|
|
|
* select file will have saved the object type for us
|
|
|
|
* as well as set that we want the cert from the object.
|
|
|
|
*/
|
|
|
|
static int cac_read_binary(sc_card_t *card, unsigned int idx,
|
|
|
|
unsigned char *buf, size_t count, unsigned long flags)
|
|
|
|
{
|
|
|
|
cac_private_data_t * priv = CAC_DATA(card);
|
|
|
|
int r = 0;
|
|
|
|
u8 *tl = NULL, *val = NULL;
|
2019-01-15 10:15:35 +00:00
|
|
|
const u8 *tl_ptr, *val_ptr, *tl_start;
|
|
|
|
u8 *tlv_ptr;
|
|
|
|
const u8 *cert_ptr;
|
2017-02-27 10:05:12 +00:00
|
|
|
size_t tl_len, val_len, tlv_len;
|
|
|
|
size_t len, tl_head_len, cert_len;
|
|
|
|
u8 cert_type, tag;
|
|
|
|
|
|
|
|
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
|
|
|
|
|
|
|
/* if we didn't return it all last time, return the remainder */
|
|
|
|
if (priv->cached) {
|
2018-11-22 08:31:29 +00:00
|
|
|
sc_log(card->ctx,
|
2017-03-14 19:02:30 +00:00
|
|
|
"returning cached value idx=%d count=%"SC_FORMAT_LEN_SIZE_T"u",
|
|
|
|
idx, count);
|
2017-02-27 10:05:12 +00:00
|
|
|
if (idx > priv->cache_buf_len) {
|
2018-11-22 22:10:49 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_END_REACHED);
|
2017-02-27 10:05:12 +00:00
|
|
|
}
|
|
|
|
len = MIN(count, priv->cache_buf_len-idx);
|
|
|
|
memcpy(buf, &priv->cache_buf[idx], len);
|
2018-11-22 22:10:49 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, len);
|
2017-02-27 10:05:12 +00:00
|
|
|
}
|
|
|
|
|
2018-11-22 08:31:29 +00:00
|
|
|
sc_log(card->ctx,
|
2017-03-14 19:02:30 +00:00
|
|
|
"clearing cache idx=%d count=%"SC_FORMAT_LEN_SIZE_T"u",
|
|
|
|
idx, count);
|
2017-02-27 10:05:12 +00:00
|
|
|
if (priv->cache_buf) {
|
|
|
|
free(priv->cache_buf);
|
|
|
|
priv->cache_buf = NULL;
|
|
|
|
priv->cache_buf_len = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (priv->object_type <= 0)
|
2018-11-22 22:10:49 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
|
2017-02-27 10:05:12 +00:00
|
|
|
|
2018-07-10 11:35:24 +00:00
|
|
|
r = cac_read_file(card, CAC_FILE_TAG, &tl, &tl_len);
|
|
|
|
if (r < 0) {
|
|
|
|
goto done;
|
2017-08-04 06:48:24 +00:00
|
|
|
}
|
2017-02-27 10:05:12 +00:00
|
|
|
|
2018-07-10 11:35:24 +00:00
|
|
|
r = cac_read_file(card, CAC_FILE_VALUE, &val, &val_len);
|
|
|
|
if (r < 0)
|
|
|
|
goto done;
|
2017-02-27 10:05:12 +00:00
|
|
|
|
|
|
|
switch (priv->object_type) {
|
|
|
|
case CAC_OBJECT_TYPE_TLV_FILE:
|
|
|
|
tlv_len = tl_len + val_len;
|
|
|
|
priv->cache_buf = malloc(tlv_len);
|
|
|
|
if (priv->cache_buf == NULL) {
|
|
|
|
r = SC_ERROR_OUT_OF_MEMORY;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
priv->cache_buf_len = tlv_len;
|
|
|
|
|
|
|
|
for (tl_ptr = tl, val_ptr=val, tlv_ptr = priv->cache_buf;
|
2018-05-28 07:28:10 +00:00
|
|
|
tl_len >= 2 && tlv_len > 0;
|
2017-02-27 10:05:12 +00:00
|
|
|
val_len -= len, tlv_len -= len, val_ptr += len, tlv_ptr += len) {
|
|
|
|
/* get the tag and the length */
|
|
|
|
tl_start = tl_ptr;
|
2018-06-19 12:54:31 +00:00
|
|
|
r = sc_simpletlv_read_tag(&tl_ptr, tl_len, &tag, &len);
|
|
|
|
if (r != SC_SUCCESS && r != SC_ERROR_TLV_END_OF_CONTENTS)
|
2017-02-27 10:05:12 +00:00
|
|
|
break;
|
|
|
|
tl_head_len = (tl_ptr - tl_start);
|
|
|
|
sc_simpletlv_put_tag(tag, len, tlv_ptr, tlv_len, &tlv_ptr);
|
|
|
|
tlv_len -= tl_head_len;
|
|
|
|
tl_len -= tl_head_len;
|
|
|
|
|
|
|
|
/* don't crash on bad data */
|
|
|
|
if (val_len < len) {
|
2018-06-19 12:54:31 +00:00
|
|
|
sc_log(card->ctx, "Received too long value %"SC_FORMAT_LEN_SIZE_T"u, "
|
|
|
|
"while only %"SC_FORMAT_LEN_SIZE_T"u left. Truncating", len, val_len);
|
2017-02-27 10:05:12 +00:00
|
|
|
len = val_len;
|
|
|
|
}
|
|
|
|
/* if we run out of return space, truncate */
|
|
|
|
if (tlv_len < len) {
|
|
|
|
len = tlv_len;
|
|
|
|
}
|
|
|
|
memcpy(tlv_ptr, val_ptr, len);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CAC_OBJECT_TYPE_CERT:
|
|
|
|
/* read file */
|
2018-11-22 08:31:29 +00:00
|
|
|
sc_log(card->ctx,
|
2017-03-14 19:02:30 +00:00
|
|
|
" obj= cert_file, val_len=%"SC_FORMAT_LEN_SIZE_T"u (0x%04"SC_FORMAT_LEN_SIZE_T"x)",
|
|
|
|
val_len, val_len);
|
2017-02-27 10:05:12 +00:00
|
|
|
cert_len = 0;
|
|
|
|
cert_ptr = NULL;
|
|
|
|
cert_type = 0;
|
2018-05-25 12:54:47 +00:00
|
|
|
for (tl_ptr = tl, val_ptr = val; tl_len >= 2;
|
|
|
|
val_len -= len, val_ptr += len, tl_len -= tl_head_len) {
|
2017-02-27 10:05:12 +00:00
|
|
|
tl_start = tl_ptr;
|
2018-06-19 12:54:31 +00:00
|
|
|
r = sc_simpletlv_read_tag(&tl_ptr, tl_len, &tag, &len);
|
|
|
|
if (r != SC_SUCCESS && r != SC_ERROR_TLV_END_OF_CONTENTS)
|
2017-02-27 10:05:12 +00:00
|
|
|
break;
|
|
|
|
tl_head_len = tl_ptr - tl_start;
|
2018-05-25 12:54:47 +00:00
|
|
|
|
|
|
|
/* incomplete value */
|
2018-06-19 12:54:31 +00:00
|
|
|
if (val_len < len) {
|
|
|
|
sc_log(card->ctx, "Read incomplete value %"SC_FORMAT_LEN_SIZE_T"u, "
|
|
|
|
"while only %"SC_FORMAT_LEN_SIZE_T"u left", len, val_len);
|
2018-05-25 12:54:47 +00:00
|
|
|
break;
|
2018-06-19 12:54:31 +00:00
|
|
|
}
|
2018-05-25 12:54:47 +00:00
|
|
|
|
2017-02-27 10:05:12 +00:00
|
|
|
if (tag == CAC_TAG_CERTIFICATE) {
|
|
|
|
cert_len = len;
|
|
|
|
cert_ptr = val_ptr;
|
|
|
|
}
|
|
|
|
if (tag == CAC_TAG_CERTINFO) {
|
|
|
|
if ((len >= 1) && (val_len >=1)) {
|
|
|
|
cert_type = *val_ptr;
|
|
|
|
}
|
2018-04-10 08:39:18 +00:00
|
|
|
}
|
|
|
|
if (tag == CAC_TAG_MSCUID) {
|
|
|
|
sc_log_hex(card->ctx, "MSCUID", val_ptr, len);
|
2017-02-27 10:05:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/* if the info byte is 1, then the cert is compressed, decompress it */
|
|
|
|
if ((cert_type & 0x3) == 1) {
|
|
|
|
#ifdef ENABLE_ZLIB
|
|
|
|
r = sc_decompress_alloc(&priv->cache_buf, &priv->cache_buf_len,
|
|
|
|
cert_ptr, cert_len, COMPRESSION_AUTO);
|
|
|
|
#else
|
2017-04-03 11:43:30 +00:00
|
|
|
sc_log(card->ctx, "CAC compression not supported, no zlib");
|
2017-02-27 10:05:12 +00:00
|
|
|
r = SC_ERROR_NOT_SUPPORTED;
|
|
|
|
#endif
|
|
|
|
if (r)
|
|
|
|
goto done;
|
2017-04-03 11:43:30 +00:00
|
|
|
} else if (cert_len > 0) {
|
2017-02-27 10:05:12 +00:00
|
|
|
priv->cache_buf = malloc(cert_len);
|
|
|
|
if (priv->cache_buf == NULL) {
|
|
|
|
r = SC_ERROR_OUT_OF_MEMORY;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
priv->cache_buf_len = cert_len;
|
|
|
|
memcpy(priv->cache_buf, cert_ptr, cert_len);
|
2017-04-03 11:43:30 +00:00
|
|
|
} else {
|
|
|
|
sc_log(card->ctx, "Can't read zero-length certificate");
|
|
|
|
goto done;
|
2017-02-27 10:05:12 +00:00
|
|
|
}
|
|
|
|
break;
|
2018-05-23 11:31:52 +00:00
|
|
|
case CAC_OBJECT_TYPE_GENERIC:
|
|
|
|
/* TODO
|
|
|
|
* We have some two buffers in unknown encoding that we
|
|
|
|
* need to present in PKCS#15 layer.
|
|
|
|
*/
|
2017-02-27 10:05:12 +00:00
|
|
|
default:
|
|
|
|
/* Unknown object type */
|
2018-05-17 07:44:07 +00:00
|
|
|
sc_log(card->ctx, "Unknown object type: %x", priv->object_type);
|
2017-02-27 10:05:12 +00:00
|
|
|
r = SC_ERROR_INTERNAL;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* OK we've read the data, now copy the required portion out to the callers buffer */
|
|
|
|
priv->cached = 1;
|
|
|
|
len = MIN(count, priv->cache_buf_len-idx);
|
|
|
|
memcpy(buf, &priv->cache_buf[idx], len);
|
|
|
|
r = len;
|
|
|
|
done:
|
|
|
|
if (tl)
|
|
|
|
free(tl);
|
|
|
|
if (val)
|
|
|
|
free(val);
|
2018-11-22 22:10:49 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
2017-02-27 10:05:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* CAC driver is read only */
|
|
|
|
static int cac_write_binary(sc_card_t *card, unsigned int idx,
|
|
|
|
const u8 *buf, size_t count, unsigned long flags)
|
|
|
|
{
|
|
|
|
|
|
|
|
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
2018-11-22 22:10:49 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
|
2017-02-27 10:05:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* initialize getting a list and return the number of elements in the list */
|
|
|
|
static int cac_get_init_and_get_count(list_t *list, cac_object_t **entry, int *countp)
|
|
|
|
{
|
|
|
|
*countp = list_size(list);
|
|
|
|
list_iterator_start(list);
|
|
|
|
*entry = list_iterator_next(list);
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* finalize the list iterator */
|
|
|
|
static int cac_final_iterator(list_t *list)
|
|
|
|
{
|
|
|
|
list_iterator_stop(list);
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* fill in the obj_info for the current object on the list and advance to the next object */
|
|
|
|
static int cac_fill_object_info(list_t *list, cac_object_t **entry, sc_pkcs15_data_info_t *obj_info)
|
|
|
|
{
|
|
|
|
memset(obj_info, 0, sizeof(sc_pkcs15_data_info_t));
|
|
|
|
if (*entry == NULL) {
|
|
|
|
return SC_ERROR_FILE_END_REACHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
obj_info->path = (*entry)->path;
|
|
|
|
obj_info->path.count = CAC_MAX_SIZE-1; /* read something from the object */
|
|
|
|
obj_info->id.value[0] = ((*entry)->fd >> 8) & 0xff;
|
|
|
|
obj_info->id.value[1] = (*entry)->fd & 0xff;
|
|
|
|
obj_info->id.len = 2;
|
|
|
|
strncpy(obj_info->app_label, (*entry)->name, SC_PKCS15_MAX_LABEL_SIZE-1);
|
|
|
|
*entry = list_iterator_next(list);
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cac_get_serial_nr_from_CUID(sc_card_t* card, sc_serial_number_t* serial)
|
|
|
|
{
|
|
|
|
cac_private_data_t * priv = CAC_DATA(card);
|
|
|
|
|
2018-11-22 08:14:50 +00:00
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
2017-02-27 10:05:12 +00:00
|
|
|
if (card->serialnr.len) {
|
|
|
|
*serial = card->serialnr;
|
2018-11-22 22:10:49 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
|
2017-02-27 10:05:12 +00:00
|
|
|
}
|
|
|
|
if (priv->cac_id_len) {
|
|
|
|
serial->len = MIN(priv->cac_id_len, SC_MAX_SERIALNR);
|
2018-05-25 21:34:14 +00:00
|
|
|
memcpy(serial->value, priv->cac_id, serial->len);
|
2018-11-22 22:10:49 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
|
2017-02-27 10:05:12 +00:00
|
|
|
}
|
2018-11-22 22:10:49 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_NOT_FOUND);
|
2017-02-27 10:05:12 +00:00
|
|
|
}
|
|
|
|
|
2017-11-06 11:37:40 +00:00
|
|
|
static int cac_get_ACA_path(sc_card_t *card, sc_path_t *path)
|
|
|
|
{
|
|
|
|
cac_private_data_t * priv = CAC_DATA(card);
|
|
|
|
|
2018-11-22 08:14:50 +00:00
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
2017-11-06 11:37:40 +00:00
|
|
|
if (priv->aca_path) {
|
|
|
|
*path = *priv->aca_path;
|
|
|
|
}
|
2018-11-22 22:10:49 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
|
2017-11-06 11:37:40 +00:00
|
|
|
}
|
2017-02-27 10:05:12 +00:00
|
|
|
|
|
|
|
static int cac_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr)
|
|
|
|
{
|
|
|
|
cac_private_data_t * priv = CAC_DATA(card);
|
|
|
|
|
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
sc_log(card->ctx, "cmd=%ld ptr=%p", cmd, ptr);
|
|
|
|
|
|
|
|
if (priv == NULL) {
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
|
|
|
|
}
|
|
|
|
switch(cmd) {
|
2017-11-06 11:37:40 +00:00
|
|
|
case SC_CARDCTL_CAC_GET_ACA_PATH:
|
|
|
|
return cac_get_ACA_path(card, (sc_path_t *) ptr);
|
2017-02-27 10:05:12 +00:00
|
|
|
case SC_CARDCTL_GET_SERIALNR:
|
|
|
|
return cac_get_serial_nr_from_CUID(card, (sc_serial_number_t *) ptr);
|
|
|
|
case SC_CARDCTL_CAC_INIT_GET_GENERIC_OBJECTS:
|
|
|
|
return cac_get_init_and_get_count(&priv->general_list, &priv->general_current, (int *)ptr);
|
|
|
|
case SC_CARDCTL_CAC_INIT_GET_CERT_OBJECTS:
|
|
|
|
return cac_get_init_and_get_count(&priv->pki_list, &priv->pki_current, (int *)ptr);
|
|
|
|
case SC_CARDCTL_CAC_GET_NEXT_GENERIC_OBJECT:
|
|
|
|
return cac_fill_object_info(&priv->general_list, &priv->general_current, (sc_pkcs15_data_info_t *)ptr);
|
|
|
|
case SC_CARDCTL_CAC_GET_NEXT_CERT_OBJECT:
|
|
|
|
return cac_fill_object_info(&priv->pki_list, &priv->pki_current, (sc_pkcs15_data_info_t *)ptr);
|
|
|
|
case SC_CARDCTL_CAC_FINAL_GET_GENERIC_OBJECTS:
|
|
|
|
return cac_final_iterator(&priv->general_list);
|
|
|
|
case SC_CARDCTL_CAC_FINAL_GET_CERT_OBJECTS:
|
|
|
|
return cac_final_iterator(&priv->pki_list);
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cac_get_challenge(sc_card_t *card, u8 *rnd, size_t len)
|
|
|
|
{
|
2018-01-23 12:53:23 +00:00
|
|
|
/* CAC requires 8 byte response */
|
2017-02-27 10:05:12 +00:00
|
|
|
u8 rbuf[8];
|
2018-06-20 10:52:17 +00:00
|
|
|
u8 *rbufp = &rbuf[0];
|
2018-01-23 12:53:23 +00:00
|
|
|
size_t out_len = sizeof rbuf;
|
2017-02-27 10:05:12 +00:00
|
|
|
int r;
|
|
|
|
|
2018-01-23 12:53:23 +00:00
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
2017-02-27 10:05:12 +00:00
|
|
|
|
2018-06-20 10:52:17 +00:00
|
|
|
r = cac_apdu_io(card, 0x84, 0x00, 0x00, NULL, 0, &rbufp, &out_len);
|
2018-01-23 12:53:23 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Could not get challenge");
|
2017-02-27 10:05:12 +00:00
|
|
|
|
2018-01-23 12:53:23 +00:00
|
|
|
if (len < out_len) {
|
|
|
|
out_len = len;
|
2017-02-27 10:05:12 +00:00
|
|
|
}
|
2018-01-23 12:53:23 +00:00
|
|
|
memcpy(rnd, rbuf, out_len);
|
2017-02-27 10:05:12 +00:00
|
|
|
|
2018-11-22 22:10:49 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, (int) out_len);
|
2017-02-27 10:05:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int cac_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num)
|
|
|
|
{
|
|
|
|
int r = SC_SUCCESS;
|
|
|
|
|
|
|
|
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
|
|
|
|
2018-11-22 08:31:29 +00:00
|
|
|
sc_log(card->ctx,
|
2017-03-14 19:02:30 +00:00
|
|
|
"flags=%08lx op=%d alg=%d algf=%08x algr=%08x kr0=%02x, krfl=%"SC_FORMAT_LEN_SIZE_T"u\n",
|
|
|
|
env->flags, env->operation, env->algorithm,
|
|
|
|
env->algorithm_flags, env->algorithm_ref, env->key_ref[0],
|
|
|
|
env->key_ref_len);
|
2017-02-27 10:05:12 +00:00
|
|
|
|
|
|
|
if (env->algorithm != SC_ALGORITHM_RSA) {
|
|
|
|
r = SC_ERROR_NO_CARD_SUPPORT;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int cac_restore_security_env(sc_card_t *card, int se_num)
|
|
|
|
{
|
|
|
|
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
|
|
|
|
2018-11-22 22:10:49 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
|
2017-02-27 10:05:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int cac_rsa_op(sc_card_t *card,
|
|
|
|
const u8 * data, size_t datalen,
|
|
|
|
u8 * out, size_t outlen)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
u8 *outp, *rbuf;
|
|
|
|
size_t rbuflen, outplen;
|
|
|
|
|
|
|
|
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
2018-11-22 08:31:29 +00:00
|
|
|
sc_log(card->ctx,
|
2017-03-14 19:02:30 +00:00
|
|
|
"datalen=%"SC_FORMAT_LEN_SIZE_T"u outlen=%"SC_FORMAT_LEN_SIZE_T"u\n",
|
|
|
|
datalen, outlen);
|
2017-02-27 10:05:12 +00:00
|
|
|
|
|
|
|
outp = out;
|
|
|
|
outplen = outlen;
|
|
|
|
|
|
|
|
/* Not strictly necessary. This code requires the caller to have selected the correct PKI container
|
|
|
|
* and authenticated to that container with the verifyPin command... All of this under the reader lock.
|
|
|
|
* The PKCS #15 higher level driver code does all this correctly (it's the same for all cards, just
|
|
|
|
* different sets of APDU's that need to be called), so this call is really a little bit of paranoia */
|
|
|
|
r = sc_lock(card);
|
|
|
|
if (r != SC_SUCCESS)
|
2018-11-22 22:10:49 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
2017-02-27 10:05:12 +00:00
|
|
|
|
|
|
|
|
|
|
|
rbuf = NULL;
|
|
|
|
rbuflen = 0;
|
|
|
|
for (; datalen > CAC_MAX_CHUNK_SIZE; data += CAC_MAX_CHUNK_SIZE, datalen -= CAC_MAX_CHUNK_SIZE) {
|
|
|
|
r = cac_apdu_io(card, CAC_INS_SIGN_DECRYPT, CAC_P1_STEP, 0,
|
|
|
|
data, CAC_MAX_CHUNK_SIZE, &rbuf, &rbuflen);
|
|
|
|
if (r < 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (rbuflen != 0) {
|
|
|
|
int n = MIN(rbuflen, outplen);
|
|
|
|
memcpy(outp,rbuf, n);
|
|
|
|
outp += n;
|
|
|
|
outplen -= n;
|
|
|
|
}
|
|
|
|
free(rbuf);
|
|
|
|
rbuf = NULL;
|
|
|
|
rbuflen = 0;
|
|
|
|
}
|
|
|
|
if (r < 0) {
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
rbuf = NULL;
|
|
|
|
rbuflen = 0;
|
|
|
|
r = cac_apdu_io(card, CAC_INS_SIGN_DECRYPT, CAC_P1_FINAL, 0, data, datalen, &rbuf, &rbuflen);
|
|
|
|
if (r < 0) {
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (rbuflen != 0) {
|
|
|
|
int n = MIN(rbuflen, outplen);
|
|
|
|
memcpy(outp,rbuf, n);
|
2018-05-22 13:55:25 +00:00
|
|
|
/*outp += n; unused */
|
2017-02-27 10:05:12 +00:00
|
|
|
outplen -= n;
|
|
|
|
}
|
|
|
|
free(rbuf);
|
|
|
|
rbuf = NULL;
|
|
|
|
r = outlen-outplen;
|
|
|
|
|
|
|
|
err:
|
|
|
|
sc_unlock(card);
|
|
|
|
if (r < 0) {
|
|
|
|
sc_mem_clear(out, outlen);
|
|
|
|
}
|
|
|
|
if (rbuf) {
|
|
|
|
free(rbuf);
|
|
|
|
}
|
|
|
|
|
2018-11-22 22:10:49 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
2017-02-27 10:05:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int cac_compute_signature(sc_card_t *card,
|
|
|
|
const u8 * data, size_t datalen,
|
|
|
|
u8 * out, size_t outlen)
|
|
|
|
{
|
|
|
|
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
|
|
|
|
|
|
|
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, cac_rsa_op(card, data, datalen, out, outlen));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cac_decipher(sc_card_t *card,
|
|
|
|
const u8 * data, size_t datalen,
|
|
|
|
u8 * out, size_t outlen)
|
|
|
|
{
|
|
|
|
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
|
|
|
|
|
|
|
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, cac_rsa_op(card, data, datalen, out, outlen));
|
|
|
|
}
|
|
|
|
|
2018-06-11 12:42:12 +00:00
|
|
|
static int cac_parse_properties_object(sc_card_t *card, u8 type,
|
2019-01-15 10:15:35 +00:00
|
|
|
const u8 *data, size_t data_len, cac_properties_object_t *object)
|
2018-05-23 11:31:52 +00:00
|
|
|
{
|
|
|
|
size_t len;
|
2019-01-15 10:15:35 +00:00
|
|
|
const u8 *val, *val_end;
|
|
|
|
u8 tag;
|
2018-05-23 11:31:52 +00:00
|
|
|
int parsed = 0;
|
|
|
|
|
|
|
|
if (data_len < 11)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Initilize: non-PKI applet */
|
2018-05-28 07:28:10 +00:00
|
|
|
object->privatekey = 0;
|
2018-05-23 11:31:52 +00:00
|
|
|
|
|
|
|
val = data;
|
|
|
|
val_end = data + data_len;
|
|
|
|
for (; val < val_end; val += len) {
|
|
|
|
/* get the tag and the length */
|
|
|
|
if (sc_simpletlv_read_tag(&val, val_end - val, &tag, &len) != SC_SUCCESS)
|
|
|
|
break;
|
|
|
|
|
|
|
|
switch (tag) {
|
|
|
|
case CAC_TAG_OBJECT_ID:
|
2018-05-30 09:03:39 +00:00
|
|
|
if (len != 2) {
|
|
|
|
sc_log(card->ctx, "TAG: Object ID: "
|
|
|
|
"Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len);
|
|
|
|
break;
|
|
|
|
}
|
2018-05-23 11:31:52 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
|
|
|
"TAG: Object ID = 0x%02x 0x%02x", val[0], val[1]);
|
2018-05-28 07:28:10 +00:00
|
|
|
memcpy(&object->oid, val, 2);
|
2018-05-23 11:31:52 +00:00
|
|
|
parsed++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CAC_TAG_BUFFER_PROPERTIES:
|
2018-05-30 09:03:39 +00:00
|
|
|
if (len != 5) {
|
|
|
|
sc_log(card->ctx, "TAG: Buffer Properties: "
|
|
|
|
"Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len);
|
|
|
|
break;
|
|
|
|
}
|
2018-05-23 11:31:52 +00:00
|
|
|
/* First byte is "Type of Tag Supported" */
|
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
|
|
|
"TAG: Buffer Properties: Type of Tag Supported = 0x%02x",
|
|
|
|
val[0]);
|
2018-05-28 07:28:10 +00:00
|
|
|
object->simpletlv = val[0];
|
2018-05-23 11:31:52 +00:00
|
|
|
parsed++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CAC_TAG_PKI_PROPERTIES:
|
|
|
|
/* 4th byte is "Private Key Initialized" */
|
2018-05-30 09:03:39 +00:00
|
|
|
if (len != 4) {
|
|
|
|
sc_log(card->ctx, "TAG: PKI Properties: "
|
|
|
|
"Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (type != CAC_TAG_PKI_OBJECT) {
|
|
|
|
sc_log(card->ctx, "TAG: PKI Properties outside of PKI Object");
|
|
|
|
break;
|
|
|
|
}
|
2018-05-23 11:31:52 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
|
|
|
"TAG: PKI Properties: Private Key Initialized = 0x%02x",
|
|
|
|
val[2]);
|
2018-05-28 07:28:10 +00:00
|
|
|
object->privatekey = val[2];
|
2018-05-23 11:31:52 +00:00
|
|
|
parsed++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* ignore tags we don't understand */
|
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
|
|
|
"TAG: Unknown (0x%02x)",tag );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (parsed < 2)
|
|
|
|
return SC_ERROR_INVALID_DATA;
|
|
|
|
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cac_get_properties(sc_card_t *card, cac_properties_t *prop)
|
|
|
|
{
|
|
|
|
u8 *rbuf = NULL;
|
|
|
|
size_t rbuflen = 0, len;
|
2019-01-15 10:15:35 +00:00
|
|
|
const u8 *val, *val_end;
|
|
|
|
u8 tag;
|
2018-05-23 11:31:52 +00:00
|
|
|
size_t i = 0;
|
|
|
|
int r;
|
|
|
|
prop->num_objects = 0;
|
|
|
|
|
|
|
|
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
|
|
|
|
|
|
|
r = cac_apdu_io(card, CAC_INS_GET_PROPERTIES, 0x01, 0x00, NULL, 0,
|
|
|
|
&rbuf, &rbuflen);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
val = rbuf;
|
|
|
|
val_end = val + rbuflen;
|
|
|
|
for (; val < val_end; val += len) {
|
|
|
|
/* get the tag and the length */
|
|
|
|
if (sc_simpletlv_read_tag(&val, val_end - val, &tag, &len) != SC_SUCCESS)
|
|
|
|
break;
|
|
|
|
|
|
|
|
switch (tag) {
|
|
|
|
case CAC_TAG_APPLET_INFORMATION:
|
2018-05-30 09:03:39 +00:00
|
|
|
if (len != 5) {
|
|
|
|
sc_log(card->ctx, "TAG: Applet Information: "
|
|
|
|
"Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len);
|
2018-05-23 11:31:52 +00:00
|
|
|
break;
|
2018-05-30 09:03:39 +00:00
|
|
|
}
|
2018-05-23 11:31:52 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
|
|
|
"TAG: Applet Information: Family: 0x%0x", val[0]);
|
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
|
|
|
" Applet Version: 0x%02x 0x%02x 0x%02x 0x%02x",
|
|
|
|
val[1], val[2], val[3], val[4]);
|
|
|
|
break;
|
2018-05-30 09:03:39 +00:00
|
|
|
|
2018-05-23 11:31:52 +00:00
|
|
|
case CAC_TAG_NUMBER_OF_OBJECTS:
|
2018-05-30 09:03:39 +00:00
|
|
|
if (len != 1) {
|
|
|
|
sc_log(card->ctx, "TAG: Num objects: "
|
|
|
|
"Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len);
|
|
|
|
break;
|
|
|
|
}
|
2018-05-23 11:31:52 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
|
|
|
"TAG: Num objects = %hhd", *val);
|
|
|
|
/* make sure we do not overrun buffer */
|
|
|
|
prop->num_objects = MIN(val[0], CAC_MAX_OBJECTS);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CAC_TAG_TV_BUFFER:
|
2018-05-30 09:03:39 +00:00
|
|
|
if (len != 17) {
|
|
|
|
sc_log(card->ctx, "TAG: TV Object: "
|
|
|
|
"Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
|
|
|
"TAG: TV Object nr. %"SC_FORMAT_LEN_SIZE_T"u", i);
|
2018-06-13 07:47:16 +00:00
|
|
|
if (i >= CAC_MAX_OBJECTS) {
|
|
|
|
free(rbuf);
|
2018-05-30 09:03:39 +00:00
|
|
|
return SC_SUCCESS;
|
2018-06-13 07:47:16 +00:00
|
|
|
}
|
2018-05-30 09:03:39 +00:00
|
|
|
|
2018-06-11 12:42:12 +00:00
|
|
|
if (cac_parse_properties_object(card, tag, val, len,
|
2018-05-30 09:03:39 +00:00
|
|
|
&prop->objects[i]) == SC_SUCCESS)
|
|
|
|
i++;
|
|
|
|
break;
|
|
|
|
|
2018-05-23 11:31:52 +00:00
|
|
|
case CAC_TAG_PKI_OBJECT:
|
2018-05-30 09:03:39 +00:00
|
|
|
if (len != 17) {
|
|
|
|
sc_log(card->ctx, "TAG: PKI Object: "
|
|
|
|
"Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len);
|
|
|
|
break;
|
|
|
|
}
|
2018-05-23 11:31:52 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
2018-05-30 09:03:39 +00:00
|
|
|
"TAG: PKI Object nr. %"SC_FORMAT_LEN_SIZE_T"u", i);
|
2018-06-13 07:47:16 +00:00
|
|
|
if (i >= CAC_MAX_OBJECTS) {
|
|
|
|
free(rbuf);
|
2018-05-23 11:31:52 +00:00
|
|
|
return SC_SUCCESS;
|
2018-06-13 07:47:16 +00:00
|
|
|
}
|
2018-05-23 11:31:52 +00:00
|
|
|
|
2018-06-11 12:42:12 +00:00
|
|
|
if (cac_parse_properties_object(card, tag, val, len,
|
2018-05-28 07:28:10 +00:00
|
|
|
&prop->objects[i]) == SC_SUCCESS)
|
|
|
|
i++;
|
2018-05-23 11:31:52 +00:00
|
|
|
break;
|
2018-05-30 09:03:39 +00:00
|
|
|
|
2018-05-23 11:31:52 +00:00
|
|
|
default:
|
|
|
|
/* ignore tags we don't understand */
|
2018-06-18 11:57:29 +00:00
|
|
|
sc_log(card->ctx, "TAG: Unknown (0x%02x), len=%"
|
|
|
|
SC_FORMAT_LEN_SIZE_T"u", tag, len);
|
2018-05-23 11:31:52 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-06-13 07:47:16 +00:00
|
|
|
free(rbuf);
|
2018-05-23 11:31:52 +00:00
|
|
|
/* sanity */
|
|
|
|
if (i != prop->num_objects)
|
2018-06-18 11:57:29 +00:00
|
|
|
sc_log(card->ctx, "The announced number of objects (%u) "
|
|
|
|
"did not match reality (%"SC_FORMAT_LEN_SIZE_T"u)",
|
|
|
|
prop->num_objects, i);
|
|
|
|
prop->num_objects = i;
|
2018-05-23 11:31:52 +00:00
|
|
|
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2017-02-27 10:05:12 +00:00
|
|
|
/*
|
|
|
|
* CAC cards use SC_PATH_SELECT_OBJECT_ID rather than SC_PATH_SELECT_FILE_ID. In order to use more
|
|
|
|
* of the PKCS #15 structure, we call the selection SC_PATH_SELECT_FILE_ID, but we set p1 to 2 instead
|
|
|
|
* of 0. Also cac1 does not do any FCI, but it doesn't understand not selecting it. It returns invalid INS
|
|
|
|
* if it doesn't like anything about the select, so we always 'request' FCI for CAC1
|
|
|
|
*
|
|
|
|
* The rest is just copied from iso7816_select_file
|
|
|
|
*/
|
2018-10-30 16:27:28 +00:00
|
|
|
static int cac_select_file_by_type(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out)
|
2017-02-27 10:05:12 +00:00
|
|
|
{
|
|
|
|
struct sc_context *ctx;
|
|
|
|
struct sc_apdu apdu;
|
|
|
|
unsigned char buf[SC_MAX_APDU_BUFFER_SIZE];
|
|
|
|
unsigned char pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf;
|
|
|
|
int r, pathlen, pathtype;
|
|
|
|
struct sc_file *file = NULL;
|
|
|
|
cac_private_data_t * priv = CAC_DATA(card);
|
|
|
|
|
|
|
|
assert(card != NULL && in_path != NULL);
|
|
|
|
ctx = card->ctx;
|
|
|
|
|
|
|
|
SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE);
|
|
|
|
|
|
|
|
memcpy(path, in_path->value, in_path->len);
|
|
|
|
pathlen = in_path->len;
|
|
|
|
pathtype = in_path->type;
|
|
|
|
|
2017-03-14 19:02:30 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
2018-10-30 16:27:28 +00:00
|
|
|
"path=%s, path->value=%s path->type=%d (%x)",
|
|
|
|
sc_print_path(in_path),
|
|
|
|
sc_dump_hex(in_path->value, in_path->len),
|
|
|
|
in_path->type, in_path->type);
|
2017-01-25 22:27:27 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "file_out=%p index=%d count=%d\n",
|
2018-10-30 16:27:28 +00:00
|
|
|
file_out, in_path->index, in_path->count);
|
2017-02-27 10:05:12 +00:00
|
|
|
|
2018-10-30 16:27:28 +00:00
|
|
|
/* Sigh, iso7816_select_file expects paths to keys to have specific
|
|
|
|
* formats. There is no override. We have to add some bytes to the
|
|
|
|
* path to make it happy.
|
|
|
|
* We only need to do this for private keys.
|
|
|
|
*/
|
2017-02-27 10:05:12 +00:00
|
|
|
if ((pathlen > 2) && (pathlen <= 4) && memcmp(path, "\x3F\x00", 2) == 0) {
|
|
|
|
if (pathlen > 2) {
|
|
|
|
path += 2;
|
|
|
|
pathlen -= 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* CAC has multiple different type of objects that aren't PKCS #15. When we read
|
|
|
|
* them we need convert them to something PKCS #15 would understand. Find the object
|
|
|
|
* and object type here:
|
|
|
|
*/
|
|
|
|
if (priv) { /* don't record anything if we haven't been initialized yet */
|
2018-05-23 11:31:52 +00:00
|
|
|
priv->object_type = CAC_OBJECT_TYPE_GENERIC;
|
2017-02-27 10:05:12 +00:00
|
|
|
if (cac_is_cert(priv, in_path)) {
|
|
|
|
priv->object_type = CAC_OBJECT_TYPE_CERT;
|
|
|
|
}
|
2018-05-23 11:31:52 +00:00
|
|
|
|
2018-04-14 17:38:34 +00:00
|
|
|
/* forget any old cached values */
|
2017-02-27 10:05:12 +00:00
|
|
|
if (priv->cache_buf) {
|
|
|
|
free(priv->cache_buf);
|
|
|
|
priv->cache_buf = NULL;
|
|
|
|
}
|
|
|
|
priv->cache_buf_len = 0;
|
|
|
|
priv->cached = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (in_path->aid.len) {
|
|
|
|
if (!pathlen) {
|
|
|
|
memcpy(path, in_path->aid.value, in_path->aid.len);
|
|
|
|
pathlen = in_path->aid.len;
|
|
|
|
pathtype = SC_PATH_TYPE_DF_NAME;
|
|
|
|
} else {
|
|
|
|
/* First, select the application */
|
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"select application" );
|
|
|
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 4, 0);
|
|
|
|
apdu.data = in_path->aid.value;
|
|
|
|
apdu.datalen = in_path->aid.len;
|
|
|
|
apdu.lc = in_path->aid.len;
|
|
|
|
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
LOG_TEST_RET(ctx, r, "APDU transmit failed");
|
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
|
|
|
if (r)
|
|
|
|
LOG_FUNC_RETURN(ctx, r);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0);
|
|
|
|
|
|
|
|
switch (pathtype) {
|
|
|
|
/* ideally we would had SC_PATH_TYPE_OBJECT_ID and add code to the iso7816 select.
|
|
|
|
* Unfortunately we'd also need to update the caching code as well. For now just
|
|
|
|
* use FILE_ID and change p1 here */
|
|
|
|
case SC_PATH_TYPE_FILE_ID:
|
|
|
|
apdu.p1 = 2;
|
|
|
|
if (pathlen != 2)
|
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
break;
|
|
|
|
case SC_PATH_TYPE_DF_NAME:
|
|
|
|
apdu.p1 = 4;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
|
|
|
|
}
|
|
|
|
apdu.lc = pathlen;
|
|
|
|
apdu.data = path;
|
|
|
|
apdu.datalen = pathlen;
|
|
|
|
apdu.resp = buf;
|
|
|
|
apdu.resplen = sizeof(buf);
|
|
|
|
apdu.le = sc_get_max_recv_size(card) < 256 ? sc_get_max_recv_size(card) : 256;
|
|
|
|
|
|
|
|
if (file_out != NULL) {
|
|
|
|
apdu.p2 = 0; /* first record, return FCI */
|
|
|
|
}
|
|
|
|
else {
|
2018-07-10 11:35:24 +00:00
|
|
|
apdu.p2 = 0x0C;
|
2017-02-27 10:05:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
LOG_TEST_RET(ctx, r, "APDU transmit failed");
|
2018-05-23 11:31:52 +00:00
|
|
|
|
2017-02-27 10:05:12 +00:00
|
|
|
if (file_out == NULL) {
|
|
|
|
/* For some cards 'SELECT' can be only with request to return FCI/FCP. */
|
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
|
|
|
if (apdu.sw1 == 0x6A && apdu.sw2 == 0x86) {
|
|
|
|
apdu.p2 = 0x00;
|
2017-08-04 06:48:24 +00:00
|
|
|
apdu.resplen = sizeof(buf);
|
2017-02-27 10:05:12 +00:00
|
|
|
if (sc_transmit_apdu(card, &apdu) == SC_SUCCESS)
|
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
|
|
|
}
|
|
|
|
if (apdu.sw1 == 0x61)
|
|
|
|
LOG_FUNC_RETURN(ctx, SC_SUCCESS);
|
|
|
|
LOG_FUNC_RETURN(ctx, r);
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
|
|
|
if (r)
|
|
|
|
LOG_FUNC_RETURN(ctx, r);
|
|
|
|
|
2018-05-23 11:31:52 +00:00
|
|
|
/* This needs to come after the applet selection */
|
2018-05-30 09:03:39 +00:00
|
|
|
if (priv && in_path->len >= 2) {
|
2018-05-23 11:31:52 +00:00
|
|
|
/* get applet properties to know if we can treat the
|
|
|
|
* buffer as SimpleLTV and if we have PKI applet.
|
|
|
|
*
|
|
|
|
* Do this only if we select applets for reading
|
|
|
|
* (not during driver initialization)
|
|
|
|
*/
|
|
|
|
cac_properties_t prop;
|
|
|
|
size_t i = -1;
|
|
|
|
|
|
|
|
r = cac_get_properties(card, &prop);
|
|
|
|
if (r == SC_SUCCESS) {
|
|
|
|
for (i = 0; i < prop.num_objects; i++) {
|
|
|
|
sc_log(card->ctx, "Searching for our OID: 0x%02x 0x%02x = 0x%02x 0x%02x",
|
2018-05-28 07:28:10 +00:00
|
|
|
prop.objects[i].oid[0], prop.objects[i].oid[1],
|
2018-05-23 11:31:52 +00:00
|
|
|
in_path->value[0], in_path->value[1]);
|
2018-05-28 07:28:10 +00:00
|
|
|
if (memcmp(prop.objects[i].oid,
|
2018-05-23 11:31:52 +00:00
|
|
|
in_path->value, 2) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (i < prop.num_objects) {
|
2018-05-28 07:28:10 +00:00
|
|
|
if (prop.objects[i].privatekey)
|
2018-05-23 11:31:52 +00:00
|
|
|
priv->object_type = CAC_OBJECT_TYPE_CERT;
|
2018-05-28 07:28:10 +00:00
|
|
|
else if (prop.objects[i].simpletlv == 0)
|
2018-05-23 11:31:52 +00:00
|
|
|
priv->object_type = CAC_OBJECT_TYPE_TLV_FILE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* CAC cards never return FCI, fake one */
|
2017-02-27 10:05:12 +00:00
|
|
|
file = sc_file_new();
|
|
|
|
if (file == NULL)
|
|
|
|
LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY);
|
|
|
|
file->path = *in_path;
|
|
|
|
file->size = CAC_MAX_SIZE; /* we don't know how big, just give a large size until we can read the file */
|
|
|
|
|
|
|
|
*file_out = file;
|
2018-11-22 22:10:49 +00:00
|
|
|
LOG_FUNC_RETURN(ctx, SC_SUCCESS);
|
2017-02-27 10:05:12 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cac_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out)
|
|
|
|
{
|
2018-10-30 16:27:28 +00:00
|
|
|
return cac_select_file_by_type(card, in_path, file_out);
|
2017-02-27 10:05:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int cac_finish(sc_card_t *card)
|
|
|
|
{
|
|
|
|
cac_private_data_t * priv = CAC_DATA(card);
|
|
|
|
|
|
|
|
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
|
|
|
if (priv) {
|
|
|
|
cac_free_private_data(priv);
|
|
|
|
}
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* select the Card Capabilities Container on CAC-2 */
|
|
|
|
static int cac_select_CCC(sc_card_t *card)
|
|
|
|
{
|
2018-10-30 16:27:28 +00:00
|
|
|
return cac_select_file_by_type(card, &cac_CCC_Path, NULL);
|
2017-02-27 10:05:12 +00:00
|
|
|
}
|
|
|
|
|
2017-11-03 09:55:35 +00:00
|
|
|
/* Select ACA in non-standard location */
|
|
|
|
static int cac_select_ACA(sc_card_t *card)
|
|
|
|
{
|
2018-10-30 16:27:28 +00:00
|
|
|
return cac_select_file_by_type(card, &cac_ACA_Path, NULL);
|
2017-11-03 09:55:35 +00:00
|
|
|
}
|
|
|
|
|
2017-02-27 10:05:12 +00:00
|
|
|
static int cac_path_from_cardurl(sc_card_t *card, sc_path_t *path, cac_card_url_t *val, int len)
|
|
|
|
{
|
|
|
|
if (len < 10) {
|
|
|
|
return SC_ERROR_INVALID_DATA;
|
|
|
|
}
|
|
|
|
sc_mem_clear(path, sizeof(sc_path_t));
|
|
|
|
memcpy(path->aid.value, &val->rid, sizeof(val->rid));
|
2018-06-18 13:23:46 +00:00
|
|
|
memcpy(&path->aid.value[5], val->applicationID, sizeof(val->applicationID));
|
2017-02-27 10:05:12 +00:00
|
|
|
path->aid.len = sizeof(val->rid) + sizeof(val->applicationID);
|
2018-06-18 13:23:46 +00:00
|
|
|
memcpy(path->value, val->objectID, sizeof(val->objectID));
|
2017-02-27 10:05:12 +00:00
|
|
|
path->len = sizeof(val->objectID);
|
|
|
|
path->type = SC_PATH_TYPE_FILE_ID;
|
2017-03-14 19:02:30 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
|
|
|
"path->aid=%x %x %x %x %x %x %x len=%"SC_FORMAT_LEN_SIZE_T"u, path->value = %x %x len=%"SC_FORMAT_LEN_SIZE_T"u path->type=%d (%x)",
|
|
|
|
path->aid.value[0], path->aid.value[1], path->aid.value[2],
|
|
|
|
path->aid.value[3], path->aid.value[4], path->aid.value[5],
|
|
|
|
path->aid.value[6], path->aid.len, path->value[0],
|
|
|
|
path->value[1], path->len, path->type, path->type);
|
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
|
|
|
"rid=%x %x %x %x %x len=%"SC_FORMAT_LEN_SIZE_T"u appid= %x %x len=%"SC_FORMAT_LEN_SIZE_T"u objid= %x %x len=%"SC_FORMAT_LEN_SIZE_T"u",
|
|
|
|
val->rid[0], val->rid[1], val->rid[2], val->rid[3],
|
|
|
|
val->rid[4], sizeof(val->rid), val->applicationID[0],
|
|
|
|
val->applicationID[1], sizeof(val->applicationID),
|
|
|
|
val->objectID[0], val->objectID[1], sizeof(val->objectID));
|
2017-02-27 10:05:12 +00:00
|
|
|
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2019-01-15 10:15:35 +00:00
|
|
|
static int cac_parse_aid(sc_card_t *card, cac_private_data_t *priv, const u8 *aid, int aid_len)
|
2018-05-22 13:55:25 +00:00
|
|
|
{
|
|
|
|
cac_object_t new_object;
|
|
|
|
cac_properties_t prop;
|
|
|
|
size_t i;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
|
|
|
|
2018-05-28 07:28:10 +00:00
|
|
|
/* Search for PKI applets (7 B). Ignore generic objects for now */
|
2018-06-13 08:38:20 +00:00
|
|
|
if (aid_len != 7 || (memcmp(aid, CAC_1_RID "\x01", 6) != 0
|
|
|
|
&& memcmp(aid, CAC_1_RID "\x00", 6) != 0))
|
2018-05-22 13:55:25 +00:00
|
|
|
return SC_SUCCESS;
|
|
|
|
|
|
|
|
sc_mem_clear(&new_object.path, sizeof(sc_path_t));
|
|
|
|
memcpy(new_object.path.aid.value, aid, aid_len);
|
|
|
|
new_object.path.aid.len = aid_len;
|
|
|
|
|
|
|
|
/* Call without OID set will just select the AID without subseqent
|
|
|
|
* OID selection, which we need to figure out just now
|
|
|
|
*/
|
2018-10-30 16:27:28 +00:00
|
|
|
cac_select_file_by_type(card, &new_object.path, NULL);
|
2018-05-22 13:55:25 +00:00
|
|
|
r = cac_get_properties(card, &prop);
|
|
|
|
if (r < 0)
|
|
|
|
return SC_ERROR_INTERNAL;
|
|
|
|
|
|
|
|
for (i = 0; i < prop.num_objects; i++) {
|
|
|
|
/* don't fail just because we have more certs than we can support */
|
|
|
|
if (priv->cert_next >= MAX_CAC_SLOTS)
|
|
|
|
return SC_SUCCESS;
|
|
|
|
|
2018-06-15 08:36:51 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
|
|
|
"ACA: pki_object found, cert_next=%d (%s), privkey=%d",
|
2018-10-30 16:27:28 +00:00
|
|
|
priv->cert_next, get_cac_label(priv->cert_next),
|
2018-06-15 08:36:51 +00:00
|
|
|
prop.objects[i].privatekey);
|
|
|
|
|
2018-05-23 11:31:52 +00:00
|
|
|
/* If the private key is not initialized, we can safely
|
2018-06-15 08:36:51 +00:00
|
|
|
* ignore this object here, but increase the pointer to follow
|
|
|
|
* the certificate labels
|
2018-05-23 11:31:52 +00:00
|
|
|
*/
|
2018-06-15 08:36:51 +00:00
|
|
|
if (!prop.objects[i].privatekey) {
|
|
|
|
priv->cert_next++;
|
2018-05-23 11:31:52 +00:00
|
|
|
continue;
|
2018-06-15 08:36:51 +00:00
|
|
|
}
|
2018-05-23 11:31:52 +00:00
|
|
|
|
2018-05-22 13:55:25 +00:00
|
|
|
/* OID here has always 2B */
|
2018-05-28 07:28:10 +00:00
|
|
|
memcpy(new_object.path.value, &prop.objects[i].oid, 2);
|
2018-05-22 13:55:25 +00:00
|
|
|
new_object.path.len = 2;
|
|
|
|
new_object.path.type = SC_PATH_TYPE_FILE_ID;
|
2018-10-30 16:27:28 +00:00
|
|
|
new_object.name = get_cac_label(priv->cert_next);
|
2018-05-22 13:55:25 +00:00
|
|
|
new_object.fd = priv->cert_next+1;
|
|
|
|
cac_add_object_to_list(&priv->pki_list, &new_object);
|
|
|
|
priv->cert_next++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2017-02-27 10:05:12 +00:00
|
|
|
static int cac_parse_cardurl(sc_card_t *card, cac_private_data_t *priv, cac_card_url_t *val, int len)
|
|
|
|
{
|
|
|
|
cac_object_t new_object;
|
|
|
|
const cac_object_t *obj;
|
|
|
|
unsigned short object_id;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = cac_path_from_cardurl(card, &new_object.path, val, len);
|
|
|
|
if (r != SC_SUCCESS) {
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
switch (val->cardApplicationType) {
|
|
|
|
case CAC_APP_TYPE_PKI:
|
|
|
|
/* we don't want to overflow the cac_label array. This test could
|
|
|
|
* go way if we create a label function that will create a unique label
|
|
|
|
* from a cert index.
|
|
|
|
*/
|
|
|
|
if (priv->cert_next >= MAX_CAC_SLOTS)
|
|
|
|
break; /* don't fail just because we have more certs than we can support */
|
2018-10-30 16:27:28 +00:00
|
|
|
new_object.name = get_cac_label(priv->cert_next);
|
2017-02-27 10:05:12 +00:00
|
|
|
new_object.fd = priv->cert_next+1;
|
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CARDURL: pki_object found, cert_next=%d (%s),", priv->cert_next, new_object.name);
|
|
|
|
cac_add_object_to_list(&priv->pki_list, &new_object);
|
|
|
|
priv->cert_next++;
|
|
|
|
break;
|
|
|
|
case CAC_APP_TYPE_GENERAL:
|
|
|
|
object_id = bebytes2ushort(val->objectID);
|
|
|
|
obj = cac_find_obj_by_id(object_id);
|
|
|
|
if (obj == NULL)
|
|
|
|
break; /* don't fail just because we don't recognize the object */
|
|
|
|
new_object.name = obj->name;
|
|
|
|
new_object.fd = 0;
|
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CARDURL: gen_object found, objectID=%x (%s),", object_id, new_object.name);
|
|
|
|
cac_add_object_to_list(&priv->general_list, &new_object);
|
|
|
|
break;
|
|
|
|
case CAC_APP_TYPE_SKI:
|
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CARDURL: ski_object found");
|
|
|
|
break;
|
|
|
|
default:
|
2018-05-22 13:55:25 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CARDURL: unknown object_object found (type=0x%02x)", val->cardApplicationType);
|
2017-02-27 10:05:12 +00:00
|
|
|
/* don't fail just because there is an unknown object in the CCC */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cac_parse_cuid(sc_card_t *card, cac_private_data_t *priv, cac_cuid_t *val, size_t len)
|
|
|
|
{
|
|
|
|
size_t card_id_len;
|
|
|
|
|
|
|
|
if (len < sizeof(cac_cuid_t)) {
|
|
|
|
return SC_ERROR_INVALID_DATA;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "gsc_rid=%s", sc_dump_hex(val->gsc_rid, sizeof(val->gsc_rid)));
|
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "manufacture id=%x", val->manufacturer_id);
|
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "cac_type=%d", val->card_type);
|
|
|
|
card_id_len = len - (&val->card_id - (u8 *)val);
|
2017-03-14 19:02:30 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
|
|
|
"card_id=%s (%"SC_FORMAT_LEN_SIZE_T"u)",
|
|
|
|
sc_dump_hex(&val->card_id, card_id_len),
|
|
|
|
card_id_len);
|
2017-02-27 10:05:12 +00:00
|
|
|
priv->cuid = *val;
|
2019-10-29 09:28:35 +00:00
|
|
|
free(priv->cac_id);
|
2017-02-27 10:05:12 +00:00
|
|
|
priv->cac_id = malloc(card_id_len);
|
|
|
|
if (priv->cac_id == NULL) {
|
|
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
memcpy(priv->cac_id, &val->card_id, card_id_len);
|
|
|
|
priv->cac_id_len = card_id_len;
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
2020-01-29 14:07:24 +00:00
|
|
|
static int cac_process_CCC(sc_card_t *card, cac_private_data_t *priv, int depth);
|
2017-02-27 10:05:12 +00:00
|
|
|
|
2019-01-15 10:15:35 +00:00
|
|
|
static int cac_parse_CCC(sc_card_t *card, cac_private_data_t *priv, const u8 *tl,
|
2020-01-29 14:07:24 +00:00
|
|
|
size_t tl_len, u8 *val, size_t val_len, int depth)
|
2017-02-27 10:05:12 +00:00
|
|
|
{
|
|
|
|
size_t len = 0;
|
2019-01-15 10:15:35 +00:00
|
|
|
const u8 *tl_end = tl + tl_len;
|
|
|
|
const u8 *val_end = val + val_len;
|
2017-02-27 10:05:12 +00:00
|
|
|
sc_path_t new_path;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
|
|
for (; (tl < tl_end) && (val< val_end); val += len) {
|
|
|
|
/* get the tag and the length */
|
|
|
|
u8 tag;
|
2018-07-09 12:13:41 +00:00
|
|
|
r = sc_simpletlv_read_tag(&tl, tl_end - tl, &tag, &len);
|
|
|
|
if (r != SC_SUCCESS && r != SC_ERROR_TLV_END_OF_CONTENTS) {
|
|
|
|
sc_log(card->ctx, "Failed to parse tag from buffer");
|
2017-02-27 10:05:12 +00:00
|
|
|
break;
|
2018-07-09 12:13:41 +00:00
|
|
|
}
|
|
|
|
if (val + len > val_end) {
|
|
|
|
sc_log(card->ctx, "Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len);
|
|
|
|
break;
|
|
|
|
}
|
2017-02-27 10:05:12 +00:00
|
|
|
switch (tag) {
|
|
|
|
case CAC_TAG_CUID:
|
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:CUID");
|
|
|
|
r = cac_parse_cuid(card, priv, (cac_cuid_t *)val, len);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
break;
|
|
|
|
case CAC_TAG_CC_VERSION_NUMBER:
|
2018-05-30 09:03:39 +00:00
|
|
|
if (len != 1) {
|
|
|
|
sc_log(card->ctx, "TAG: CC Version: "
|
|
|
|
"Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len);
|
|
|
|
break;
|
|
|
|
}
|
2018-04-10 08:57:17 +00:00
|
|
|
/* ignore the version numbers for now */
|
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
2018-05-30 09:03:39 +00:00
|
|
|
"TAG: CC Version = 0x%02x", *val);
|
2018-04-10 08:57:17 +00:00
|
|
|
break;
|
2017-02-27 10:05:12 +00:00
|
|
|
case CAC_TAG_GRAMMAR_VERION_NUMBER:
|
2018-05-30 09:03:39 +00:00
|
|
|
if (len != 1) {
|
|
|
|
sc_log(card->ctx, "TAG: Grammar Version: "
|
|
|
|
"Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len);
|
|
|
|
break;
|
|
|
|
}
|
2017-02-27 10:05:12 +00:00
|
|
|
/* ignore the version numbers for now */
|
2018-04-10 08:57:17 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
2018-05-30 09:03:39 +00:00
|
|
|
"TAG: Grammar Version = 0x%02x", *val);
|
2017-02-27 10:05:12 +00:00
|
|
|
break;
|
|
|
|
case CAC_TAG_CARDURL:
|
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:CARDURL");
|
|
|
|
r = cac_parse_cardurl(card, priv, (cac_card_url_t *)val, len);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
break;
|
|
|
|
/*
|
|
|
|
* The following are really for file systems cards. This code only cares about CAC VM cards
|
|
|
|
*/
|
|
|
|
case CAC_TAG_PKCS15:
|
2018-05-30 09:03:39 +00:00
|
|
|
if (len != 1) {
|
|
|
|
sc_log(card->ctx, "TAG: PKCS15: "
|
|
|
|
"Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len);
|
|
|
|
break;
|
|
|
|
}
|
2018-04-10 08:57:17 +00:00
|
|
|
/* TODO should verify that this is '0'. If it's not
|
|
|
|
* zero, we should drop out of here and let the PKCS 15
|
|
|
|
* code handle this card */
|
2018-05-30 09:03:39 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG: PKCS15 = 0x%02x", *val);
|
2017-02-27 10:05:12 +00:00
|
|
|
break;
|
|
|
|
case CAC_TAG_DATA_MODEL:
|
2018-11-19 08:34:42 +00:00
|
|
|
if (len != 1) {
|
|
|
|
sc_log(card->ctx, "TAG: Registered Data Model Number: "
|
|
|
|
"Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG: Registered Data Model Number (0x%02x)", *val);
|
|
|
|
break;
|
2017-02-27 10:05:12 +00:00
|
|
|
case CAC_TAG_CARD_APDU:
|
|
|
|
case CAC_TAG_CAPABILITY_TUPLES:
|
|
|
|
case CAC_TAG_STATUS_TUPLES:
|
|
|
|
case CAC_TAG_REDIRECTION:
|
|
|
|
case CAC_TAG_ERROR_CODES:
|
2018-11-19 08:34:42 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG: FSSpecific(0x%02x)", tag);
|
2017-02-27 10:05:12 +00:00
|
|
|
break;
|
|
|
|
case CAC_TAG_ACCESS_CONTROL:
|
2018-04-10 08:57:17 +00:00
|
|
|
/* TODO handle access control later */
|
|
|
|
sc_log_hex(card->ctx, "TAG:ACCESS Control", val, len);
|
2017-02-27 10:05:12 +00:00
|
|
|
break;
|
|
|
|
case CAC_TAG_NEXT_CCC:
|
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:NEXT CCC");
|
|
|
|
r = cac_path_from_cardurl(card, &new_path, (cac_card_url_t *)val, len);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2018-10-30 16:27:28 +00:00
|
|
|
r = cac_select_file_by_type(card, &new_path, NULL);
|
2017-02-27 10:05:12 +00:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2020-01-29 14:07:24 +00:00
|
|
|
/* Increase depth to avoid infinite recursion */
|
|
|
|
r = cac_process_CCC(card, priv, depth + 1);
|
2017-02-27 10:05:12 +00:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* ignore tags we don't understand */
|
2018-05-22 13:55:25 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:Unknown (0x%02x)",tag );
|
2017-02-27 10:05:12 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2020-01-29 14:07:24 +00:00
|
|
|
static int cac_process_CCC(sc_card_t *card, cac_private_data_t *priv, int depth)
|
2017-02-27 10:05:12 +00:00
|
|
|
{
|
|
|
|
u8 *tl = NULL, *val = NULL;
|
|
|
|
size_t tl_len, val_len;
|
|
|
|
int r;
|
|
|
|
|
2020-03-02 14:34:37 +00:00
|
|
|
if (depth > CAC_MAX_CCC_DEPTH) {
|
2020-01-29 14:07:24 +00:00
|
|
|
sc_log(card->ctx, "Too much recursive CCC found. Exiting");
|
|
|
|
return SC_ERROR_INVALID_CARD;
|
|
|
|
}
|
2017-02-27 10:05:12 +00:00
|
|
|
|
|
|
|
r = cac_read_file(card, CAC_FILE_TAG, &tl, &tl_len);
|
|
|
|
if (r < 0)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
r = cac_read_file(card, CAC_FILE_VALUE, &val, &val_len);
|
|
|
|
if (r < 0)
|
|
|
|
goto done;
|
|
|
|
|
2020-01-29 14:07:24 +00:00
|
|
|
r = cac_parse_CCC(card, priv, tl, tl_len, val, val_len, depth);
|
2017-02-27 10:05:12 +00:00
|
|
|
done:
|
|
|
|
if (tl)
|
|
|
|
free(tl);
|
|
|
|
if (val)
|
|
|
|
free(val);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2018-05-22 13:55:25 +00:00
|
|
|
/* Service Applet Table (Table 5-21) should list all the applets on the
|
|
|
|
* card, which is a good start if we don't have CCC
|
|
|
|
*/
|
|
|
|
static int cac_parse_ACA_service(sc_card_t *card, cac_private_data_t *priv,
|
2019-01-15 10:15:35 +00:00
|
|
|
const u8 *val, size_t val_len)
|
2018-05-22 13:55:25 +00:00
|
|
|
{
|
|
|
|
size_t len = 0;
|
2019-01-15 10:15:35 +00:00
|
|
|
const u8 *val_end = val + val_len;
|
2018-05-22 13:55:25 +00:00
|
|
|
int r;
|
|
|
|
|
|
|
|
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
|
|
|
|
|
|
|
for (; val < val_end; val += len) {
|
|
|
|
/* get the tag and the length */
|
|
|
|
u8 tag;
|
|
|
|
if (sc_simpletlv_read_tag(&val, val_end - val, &tag, &len) != SC_SUCCESS)
|
|
|
|
break;
|
|
|
|
|
|
|
|
switch (tag) {
|
|
|
|
case CAC_TAG_APPLET_FAMILY:
|
2018-06-04 10:42:35 +00:00
|
|
|
if (len != 5) {
|
2018-06-15 08:37:20 +00:00
|
|
|
sc_log(card->ctx, "TAG: Applet Information: "
|
|
|
|
"bad length %"SC_FORMAT_LEN_SIZE_T"u", len);
|
2018-05-22 13:55:25 +00:00
|
|
|
break;
|
2018-06-04 10:42:35 +00:00
|
|
|
}
|
2018-05-22 13:55:25 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
|
|
|
"TAG: Applet Information: Family: 0x%02x", val[0]);
|
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
|
|
|
" Applet Version: 0x%02x 0x%02x 0x%02x 0x%02x",
|
|
|
|
val[1], val[2], val[3], val[4]);
|
|
|
|
break;
|
|
|
|
case CAC_TAG_NUMBER_APPLETS:
|
2018-05-28 07:28:10 +00:00
|
|
|
if (len != 1) {
|
2018-06-15 08:37:20 +00:00
|
|
|
sc_log(card->ctx, "TAG: Num applets: "
|
|
|
|
"bad length %"SC_FORMAT_LEN_SIZE_T"u", len);
|
2018-05-28 07:28:10 +00:00
|
|
|
break;
|
|
|
|
}
|
2018-05-22 13:55:25 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
|
|
|
"TAG: Num applets = %hhd", *val);
|
|
|
|
break;
|
|
|
|
case CAC_TAG_APPLET_ENTRY:
|
2018-05-28 07:28:10 +00:00
|
|
|
/* Make sure we match the outer length */
|
2018-06-04 10:34:29 +00:00
|
|
|
if (len < 3 || val[2] != len - 3) {
|
2018-06-15 08:37:20 +00:00
|
|
|
sc_log(card->ctx, "TAG: Applet Entry: "
|
|
|
|
"bad length (%"SC_FORMAT_LEN_SIZE_T
|
|
|
|
"u) or length of internal buffer", len);
|
2018-05-28 07:28:10 +00:00
|
|
|
break;
|
|
|
|
}
|
2018-06-15 08:37:20 +00:00
|
|
|
sc_debug_hex(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
2019-01-15 10:15:35 +00:00
|
|
|
"TAG: Applet Entry: AID", val + 3, val[2]);
|
2018-05-22 13:55:25 +00:00
|
|
|
/* This is SimpleTLV prefixed with applet ID (1B) */
|
2019-01-15 10:15:35 +00:00
|
|
|
r = cac_parse_aid(card, priv, val + 3, val[2]);
|
2018-05-22 13:55:25 +00:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* ignore tags we don't understand */
|
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
|
|
|
"TAG: Unknown (0x%02x)", tag);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2018-07-10 11:35:24 +00:00
|
|
|
/* select a CAC pki applet by index */
|
2017-02-27 10:05:12 +00:00
|
|
|
static int cac_select_pki_applet(sc_card_t *card, int index)
|
|
|
|
{
|
2018-07-10 11:35:24 +00:00
|
|
|
sc_path_t applet_path = cac_cac_pki_obj.path;
|
2017-02-27 10:05:12 +00:00
|
|
|
applet_path.aid.value[applet_path.aid.len-1] = index;
|
2018-10-30 16:27:28 +00:00
|
|
|
return cac_select_file_by_type(card, &applet_path, NULL);
|
2017-02-27 10:05:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2018-07-10 11:35:24 +00:00
|
|
|
* Find the first existing CAC applet. If none found, then this isn't a CAC
|
2017-02-27 10:05:12 +00:00
|
|
|
*/
|
|
|
|
static int cac_find_first_pki_applet(sc_card_t *card, int *index_out)
|
|
|
|
{
|
|
|
|
int r, i;
|
2018-07-10 11:35:24 +00:00
|
|
|
for (i = 0; i < MAX_CAC_SLOTS; i++) {
|
2017-02-27 10:05:12 +00:00
|
|
|
r = cac_select_pki_applet(card, i);
|
|
|
|
if (r == SC_SUCCESS) {
|
2018-07-10 11:35:24 +00:00
|
|
|
/* Try to read first two bytes of the buffer to
|
|
|
|
* make sure it is not just malfunctioning card
|
|
|
|
*/
|
|
|
|
u8 params[2] = {CAC_FILE_TAG, 2};
|
|
|
|
u8 data[2], *out_ptr = data;
|
|
|
|
size_t len = 2;
|
|
|
|
r = cac_apdu_io(card, CAC_INS_READ_FILE, 0, 0,
|
|
|
|
¶ms[0], sizeof(params), &out_ptr, &len);
|
|
|
|
if (r != 2)
|
|
|
|
continue;
|
|
|
|
|
2017-02-27 10:05:12 +00:00
|
|
|
*index_out = i;
|
2018-07-10 11:35:24 +00:00
|
|
|
return SC_SUCCESS;
|
2017-02-27 10:05:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return SC_ERROR_OBJECT_NOT_FOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2018-07-10 11:35:24 +00:00
|
|
|
* This emulates CCC for Alt tokens, that do not come with CCC nor ACA applets
|
2017-02-27 10:05:12 +00:00
|
|
|
*/
|
2018-07-10 11:35:24 +00:00
|
|
|
static int cac_populate_cac_alt(sc_card_t *card, int index, cac_private_data_t *priv)
|
2017-02-27 10:05:12 +00:00
|
|
|
{
|
|
|
|
int r, i;
|
2018-07-10 11:35:24 +00:00
|
|
|
cac_object_t pki_obj = cac_cac_pki_obj;
|
2017-02-27 10:05:12 +00:00
|
|
|
u8 buf[100];
|
|
|
|
u8 *val;
|
|
|
|
|
|
|
|
/* populate PKI objects */
|
2018-07-10 11:35:24 +00:00
|
|
|
for (i = index; i < MAX_CAC_SLOTS; i++) {
|
2017-02-27 10:05:12 +00:00
|
|
|
r = cac_select_pki_applet(card, i);
|
|
|
|
if (r == SC_SUCCESS) {
|
2018-10-30 16:27:28 +00:00
|
|
|
pki_obj.name = get_cac_label(i);
|
2018-07-10 11:35:24 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
|
|
|
"CAC: pki_object found, cert_next=%d (%s),", i, pki_obj.name);
|
2017-02-27 10:05:12 +00:00
|
|
|
pki_obj.path.aid.value[pki_obj.path.aid.len-1] = i;
|
|
|
|
pki_obj.fd = i+1; /* don't use id of zero */
|
|
|
|
cac_add_object_to_list(&priv->pki_list, &pki_obj);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* populate non-PKI objects */
|
2018-07-10 11:35:24 +00:00
|
|
|
for (i=0; i < cac_object_count; i++) {
|
2018-10-30 16:27:28 +00:00
|
|
|
r = cac_select_file_by_type(card, &cac_objects[i].path, NULL);
|
2017-02-27 10:05:12 +00:00
|
|
|
if (r == SC_SUCCESS) {
|
2018-07-10 11:35:24 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
|
|
|
"CAC: obj_object found, cert_next=%d (%s),",
|
|
|
|
i, cac_objects[i].name);
|
|
|
|
cac_add_object_to_list(&priv->general_list, &cac_objects[i]);
|
2017-02-27 10:05:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* create a cuid to simulate the cac 2 cuid.
|
|
|
|
*/
|
2018-07-10 11:35:24 +00:00
|
|
|
priv->cuid = cac_cac_cuid;
|
2017-02-27 10:05:12 +00:00
|
|
|
/* create a serial number by hashing the first 100 bytes of the
|
|
|
|
* first certificate on the card */
|
|
|
|
r = cac_select_pki_applet(card, index);
|
|
|
|
if (r < 0) {
|
|
|
|
return r; /* shouldn't happen unless the card has been removed or is malfunctioning */
|
|
|
|
}
|
|
|
|
val = buf;
|
2019-04-23 10:27:03 +00:00
|
|
|
r = cac_read_binary(card, 0, val, sizeof(buf), 0);
|
|
|
|
if (r > 0) {
|
|
|
|
#ifdef ENABLE_OPENSSL
|
|
|
|
size_t val_len = r;
|
2019-10-29 09:28:35 +00:00
|
|
|
free(priv->cac_id);
|
2017-02-27 10:05:12 +00:00
|
|
|
priv->cac_id = malloc(20);
|
|
|
|
if (priv->cac_id == NULL) {
|
|
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
SHA1(val, val_len, priv->cac_id);
|
|
|
|
priv->cac_id_len = 20;
|
2018-07-10 11:35:24 +00:00
|
|
|
sc_debug_hex(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
|
|
|
"cuid", priv->cac_id, priv->cac_id_len);
|
2017-02-27 10:05:12 +00:00
|
|
|
#else
|
|
|
|
sc_log(card->ctx, "OpenSSL Required");
|
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
|
|
#endif /* ENABLE_OPENSSL */
|
|
|
|
}
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2018-01-11 10:42:06 +00:00
|
|
|
static int cac_process_ACA(sc_card_t *card, cac_private_data_t *priv)
|
|
|
|
{
|
2018-05-22 13:55:25 +00:00
|
|
|
int r;
|
|
|
|
u8 *val = NULL;
|
|
|
|
size_t val_len;
|
2018-01-11 10:42:06 +00:00
|
|
|
|
2018-05-22 13:55:25 +00:00
|
|
|
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
2018-01-11 10:42:06 +00:00
|
|
|
|
2018-05-22 13:55:25 +00:00
|
|
|
/* Assuming ACA is already selected */
|
|
|
|
r = cac_get_acr(card, CAC_ACR_SERVICE, &val, &val_len);
|
2018-01-11 10:42:06 +00:00
|
|
|
if (r < 0)
|
|
|
|
goto done;
|
|
|
|
|
2018-05-22 13:55:25 +00:00
|
|
|
r = cac_parse_ACA_service(card, priv, val, val_len);
|
2018-01-11 10:42:06 +00:00
|
|
|
if (r == SC_SUCCESS) {
|
2018-05-22 13:55:25 +00:00
|
|
|
priv->aca_path = malloc(sizeof(sc_path_t));
|
|
|
|
if (!priv->aca_path) {
|
|
|
|
r = SC_ERROR_OUT_OF_MEMORY;
|
|
|
|
goto done;
|
2018-01-11 10:42:06 +00:00
|
|
|
}
|
2018-05-22 13:55:25 +00:00
|
|
|
memcpy(priv->aca_path, &cac_ACA_Path, sizeof(sc_path_t));
|
2018-01-11 10:42:06 +00:00
|
|
|
}
|
|
|
|
done:
|
|
|
|
if (val)
|
|
|
|
free(val);
|
2018-11-22 22:10:49 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
2018-01-11 10:42:06 +00:00
|
|
|
}
|
|
|
|
|
2017-02-27 10:05:12 +00:00
|
|
|
/*
|
|
|
|
* Look for a CAC card. If it exists, initialize our data structures
|
|
|
|
*/
|
|
|
|
static int cac_find_and_initialize(sc_card_t *card, int initialize)
|
|
|
|
{
|
|
|
|
int r, index;
|
|
|
|
cac_private_data_t *priv = NULL;
|
|
|
|
|
|
|
|
/* already initialized? */
|
|
|
|
if (card->drv_data) {
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* is this a CAC-2 specified in NIST Interagency Report 6887 -
|
|
|
|
* "Government Smart Card Interoperability Specification v2.1 July 2003" */
|
|
|
|
r = cac_select_CCC(card);
|
|
|
|
if (r == SC_SUCCESS) {
|
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "CCC found, is CAC-2");
|
|
|
|
if (!initialize) /* match card only */
|
|
|
|
return r;
|
|
|
|
|
|
|
|
priv = cac_new_private_data();
|
2017-04-20 19:08:49 +00:00
|
|
|
if (!priv)
|
|
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
2020-03-02 14:34:37 +00:00
|
|
|
r = cac_process_CCC(card, priv, 0);
|
2017-02-27 10:05:12 +00:00
|
|
|
if (r == SC_SUCCESS) {
|
|
|
|
card->type = SC_CARD_TYPE_CAC_II;
|
|
|
|
card->drv_data = priv;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-03 09:55:35 +00:00
|
|
|
/* Even some ALT tokens can be missing CCC so we should try with ACA */
|
|
|
|
r = cac_select_ACA(card);
|
|
|
|
if (r == SC_SUCCESS) {
|
2018-01-11 10:42:06 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "ACA found, is CAC-2 without CCC");
|
2018-04-10 08:57:43 +00:00
|
|
|
if (!initialize) /* match card only */
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (!priv) {
|
|
|
|
priv = cac_new_private_data();
|
|
|
|
if (!priv)
|
|
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
2018-01-11 10:42:06 +00:00
|
|
|
r = cac_process_ACA(card, priv);
|
|
|
|
if (r == SC_SUCCESS) {
|
|
|
|
card->type = SC_CARD_TYPE_CAC_II;
|
|
|
|
card->drv_data = priv;
|
|
|
|
return r;
|
2017-11-03 09:55:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-10 11:35:24 +00:00
|
|
|
/* is this a CAC Alt token without any accompanying structures */
|
2017-02-27 10:05:12 +00:00
|
|
|
r = cac_find_first_pki_applet(card, &index);
|
|
|
|
if (r == SC_SUCCESS) {
|
2018-07-10 11:35:24 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "PKI applet found, is bare CAC Alt");
|
2017-02-27 10:05:12 +00:00
|
|
|
if (!initialize) /* match card only */
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (!priv) {
|
|
|
|
priv = cac_new_private_data();
|
2017-04-20 19:08:49 +00:00
|
|
|
if (!priv)
|
|
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
2017-02-27 10:05:12 +00:00
|
|
|
}
|
2018-07-10 11:35:24 +00:00
|
|
|
card->drv_data = priv; /* needed for the read_binary() */
|
|
|
|
r = cac_populate_cac_alt(card, index, priv);
|
2017-02-27 10:05:12 +00:00
|
|
|
if (r == SC_SUCCESS) {
|
2018-07-10 11:35:24 +00:00
|
|
|
card->type = SC_CARD_TYPE_CAC_II;
|
2017-02-27 10:05:12 +00:00
|
|
|
return r;
|
|
|
|
}
|
2018-07-10 11:35:24 +00:00
|
|
|
card->drv_data = NULL; /* reset on failure */
|
2017-02-27 10:05:12 +00:00
|
|
|
}
|
|
|
|
if (priv) {
|
|
|
|
cac_free_private_data(priv);
|
|
|
|
}
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* NOTE: returns a bool, 1 card matches, 0 it does not */
|
|
|
|
static int cac_match_card(sc_card_t *card)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
|
|
|
/* Since we send an APDU, the card's logout function may be called...
|
|
|
|
* however it may be in dirty memory */
|
|
|
|
card->ops->logout = NULL;
|
|
|
|
|
|
|
|
r = cac_find_and_initialize(card, 0);
|
|
|
|
return (r == SC_SUCCESS); /* never match */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int cac_init(sc_card_t *card)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
|
|
|
|
|
|
|
r = cac_find_and_initialize(card, 1);
|
|
|
|
if (r < 0) {
|
2018-11-22 22:10:49 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_CARD);
|
2017-02-27 10:05:12 +00:00
|
|
|
}
|
|
|
|
flags = SC_ALGORITHM_RSA_RAW;
|
|
|
|
|
2018-04-14 17:38:34 +00:00
|
|
|
_sc_card_add_rsa_alg(card, 1024, flags, 0); /* mandatory */
|
2017-02-27 10:05:12 +00:00
|
|
|
_sc_card_add_rsa_alg(card, 2048, flags, 0); /* optional */
|
|
|
|
_sc_card_add_rsa_alg(card, 3072, flags, 0); /* optional */
|
|
|
|
|
|
|
|
card->caps |= SC_CARD_CAP_RNG | SC_CARD_CAP_ISO7816_PIN_INFO;
|
|
|
|
|
2018-11-22 22:10:49 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
|
2017-02-27 10:05:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int cac_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left)
|
|
|
|
{
|
|
|
|
/* CAC, like PIV needs Extra validation of (new) PIN during
|
|
|
|
* a PIN change request, to ensure it's not outside the
|
|
|
|
* FIPS 201 4.1.6.1 (numeric only) and * FIPS 140-2
|
|
|
|
* (6 character minimum) requirements.
|
|
|
|
*/
|
|
|
|
struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
|
|
|
|
|
|
|
|
if (data->cmd == SC_PIN_CMD_CHANGE) {
|
|
|
|
int i = 0;
|
|
|
|
if (data->pin2.len < 6) {
|
|
|
|
return SC_ERROR_INVALID_PIN_LENGTH;
|
|
|
|
}
|
|
|
|
for(i=0; i < data->pin2.len; ++i) {
|
|
|
|
if (!isdigit(data->pin2.data[i])) {
|
|
|
|
return SC_ERROR_INVALID_DATA;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return iso_drv->ops->pin_cmd(card, data, tries_left);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct sc_card_operations cac_ops;
|
|
|
|
|
|
|
|
static struct sc_card_driver cac_drv = {
|
|
|
|
"Common Access Card (CAC)",
|
|
|
|
"cac",
|
|
|
|
&cac_ops,
|
|
|
|
NULL, 0, NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct sc_card_driver * sc_get_driver(void)
|
|
|
|
{
|
|
|
|
struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
|
|
|
|
|
|
|
|
cac_ops = *iso_drv->ops;
|
|
|
|
cac_ops.match_card = cac_match_card;
|
|
|
|
cac_ops.init = cac_init;
|
|
|
|
cac_ops.finish = cac_finish;
|
|
|
|
|
|
|
|
cac_ops.select_file = cac_select_file; /* need to record object type */
|
|
|
|
cac_ops.get_challenge = cac_get_challenge;
|
|
|
|
cac_ops.read_binary = cac_read_binary;
|
|
|
|
cac_ops.write_binary = cac_write_binary;
|
|
|
|
cac_ops.set_security_env = cac_set_security_env;
|
|
|
|
cac_ops.restore_security_env = cac_restore_security_env;
|
|
|
|
cac_ops.compute_signature = cac_compute_signature;
|
|
|
|
cac_ops.decipher = cac_decipher;
|
|
|
|
cac_ops.card_ctl = cac_card_ctl;
|
|
|
|
cac_ops.pin_cmd = cac_pin_cmd;
|
|
|
|
|
|
|
|
return &cac_drv;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct sc_card_driver * sc_get_cac_driver(void)
|
|
|
|
{
|
|
|
|
return sc_get_driver();
|
|
|
|
}
|