opensc/src/libopensc/card-gids.c

2126 lines
80 KiB
C
Raw Normal View History

/*
* card-gids.c: Support for GIDS smart cards.
*
* Copyright (C) 2015 Vincent Le Toux (My Smart Logon) <vincent.letoux@mysmartlogon.com>
*
* 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
*/
/*
The GIDS specification can be viewed here:
https://msdn.microsoft.com/en-us/library/windows/hardware/dn642100%28v=vs.85%29.aspx
and its formatting into the MS minidriver specification.
Some features are undocumented like the format used to store certificates. They have been reverse engineered.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include "../common/compat_strlcpy.h"
#ifdef ENABLE_OPENSSL
/* openssl only needed for card administration */
#include <openssl/evp.h>
#include <openssl/rand.h>
#endif /* ENABLE_OPENSSL */
#include "internal.h"
#include "asn1.h"
#include "cardctl.h"
#include "iso7816.h"
#ifdef ENABLE_ZLIB
#include "compression.h"
// used for changing the default label if used twice
#include "../pkcs15init/pkcs15-init.h"
#include "card-gids.h"
#define GIDS_STATE_NONE 0
#define GIDS_STATE_READ_DATA_PRESENT 1
#define INS_ACTIVATE_FILE 0x44
#define INS_CREATE_FILE 0xE0
#define INS_DELETE_FILE 0xE4
#define INS_GENERAL_AUTHENTICATE 0x87
#define INS_GENERATE_ASYMECTRIC_KEY_PAIR 0x47
#define INS_GET_DATA 0xCB
#define INS_MANAGE_SECURITY_ENVIRONMENT 0x22
#define INS_PUT_DATA 0xDB
#define INS_SELECT 0xA4
#define INS_VERIFY 0x20
#define P1_SELECT_DF_OR_EF_WITH_EFID 0x00
#define P1_SELECT_DF_BY_NAME 0x04
#define P1_DECIPHERMENT_INTERNAL_AUTHENTICATE_KEY_AGREEMENT 0x41
#define P2_SELECT_FIRST_OR_ONLY_OCCURENCE 0x00
#define P2_PIN_DEAUTHENTICATE 0x82
#define P2_DIGITAL_SIGNATURE 0xB6
#define P2_DECIPHERMENT 0xB8
#define GIDS_PIN_STATUS_OBJECT_IDENTIFIER 0x7F71
#define GIDS_PUK_STATUS_OBJECT_IDENTIFIER 0x7F73
#define GIDS_APPLET_EFID 0x3FFF
#define GIDS_PUT_KEY_DO 0x70
#define GIDS_RSA_1024_IDENTIFIER 0x06
#define GIDS_RSA_2048_IDENTIFIER 0x07
#define GIDS_RSA_3072_IDENTIFIER 0x08
#define GIDS_RSA_4096_IDENTIFIER 0x09
#define GIDS_ECC_192_IDENTIFIER 0x0A
#define GIDS_ECC_224_IDENTIFIER 0x0B
#define GIDS_ECC_256_IDENTIFIER 0x0C
#define GIDS_ECC_384_IDENTIFIER 0x0D
#define GIDS_ECC_521_IDENTIFIER 0x0E
#define GIDS_PUBKEY_TAG 0x7F49
#define GIDS_PUBKEY_TAG_MODULUS 0x81
#define GIDS_PUBKEY_TAG_EXPONENT 0x82
#define GIDS_FIRST_KEY_IDENTIFIER 0x81
#define GIDS_PIN_IDENTIFIER 0x80
#define GIDS_PUK_IDENTIFIER 0x81
#define GIDS_TRY_COUNTER_OLD_TAG 0x9F17
#define GIDS_TRY_COUNTER_TAG 0x97
#define GIDS_TRY_LIMIT_TAG 0x93
#define GIDS_APPLICATION_TEMPLATE_TAG 0x61
#define GIDS_APPLICATION_AID_TAG 0x4F
#define GIDS_KEY_TYPE_AT_KEYEXCHANGE 0x9A
#define GIDS_KEY_TYPE_AT_SIGNATURE 0x9C
static struct sc_card_operations *iso_ops;
static struct sc_card_operations gids_ops;
static struct sc_card_driver gids_drv = {
"GIDS Smart Card",
"gids",
&gids_ops,
NULL, 0, NULL
};
struct gids_aid {
int enumtag;
size_t len_short; /* min length without version */
size_t len_long; /* With version and other stuff */
u8 *value;
};
/* GIDS AID */
struct sc_aid gids_aid = { { 0xA0,0x00,0x00,0x03,0x97,0x42,0x54,0x46,0x59 }, 9 };
static struct gids_aid gids_aids[] = {
{SC_CARD_TYPE_GIDS_V1,
9, 10, (u8 *) "\xA0\x00\x00\x03\x97\x42\x54\x46\x59\x01" },
{SC_CARD_TYPE_GIDS_V2,
9, 10, (u8 *) "\xA0\x00\x00\x03\x97\x42\x54\x46\x59\x02" },
{0, 9, 0, NULL }
};
// stolen from cardmod.h for the cardcf file
typedef struct _CARD_CACHE_FILE_FORMAT
{
unsigned char bVersion; // Cache version
unsigned char bPinsFreshness; // Card PIN
unsigned short wContainersFreshness;
unsigned short wFilesFreshness;
} CARD_CACHE_FILE_FORMAT, *PCARD_CACHE_FILE_FORMAT;
struct gids_private_data {
u8 masterfile[MAX_GIDS_FILE_SIZE];
size_t masterfilesize;
u8 cmapfile[MAX_GIDS_FILE_SIZE];
size_t cmapfilesize;
unsigned short currentEFID;
unsigned short currentDO;
int state;
u8 buffer[SC_MAX_EXT_APDU_BUFFER_SIZE];
size_t buffersize;
};
// LOW LEVEL API
///////////////////////////////////////////
// find file identifier & DO identifier from the masterfile for a directory/file
static int gids_get_identifiers(sc_card_t* card, u8* masterfile, size_t masterfilesize, char *directory, char *filename, int *fileIdentifier, int *dataObjectIdentifier) {
gids_mf_record_t *records = (gids_mf_record_t *) (masterfile+1);
size_t recordcount = ((masterfilesize-1) / sizeof(gids_mf_record_t));
size_t i;
assert(masterfilesize >= 1);
for (i = 0; i < recordcount; i++) {
if (strcmp(directory, records[i].directory) == 0 && strcmp(filename, records[i].filename) == 0) {
*fileIdentifier = records[i].fileIdentifier;
*dataObjectIdentifier = records[i].dataObjectIdentifier;
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
"Identifiers of %s %s is fileIdentifier=%x, dataObjectIdentifier=%x\n", directory, filename, *fileIdentifier, *dataObjectIdentifier);
return 0;
}
}
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "file %s %s not found\n", directory, filename);
return SC_ERROR_FILE_NOT_FOUND;
}
// used when storing a new certificates
static int gids_find_available_DO(sc_card_t *card, u8* masterfile, size_t masterfilesize, int* fileIdentifier, int *dataObjectIdentifier) {
// find the first available DO from the masterfile since A010 DF21
// A010 = read everyone, card user write
gids_mf_record_t *records = (gids_mf_record_t *) (masterfile+1);
size_t recordcount = (masterfilesize / sizeof(gids_mf_record_t));
size_t i;
assert(masterfilesize >= 1);
*fileIdentifier = CERT_FI;
for (*dataObjectIdentifier = CARDAPPS_DO; *dataObjectIdentifier < GIDS_MAX_DO; (*dataObjectIdentifier)++) {
for (i = 0; i < recordcount; i++) {
if (records[i].fileIdentifier == *fileIdentifier && records[i].dataObjectIdentifier == *dataObjectIdentifier) {
break;
}
}
if (i == recordcount) {
return SC_SUCCESS;
}
}
return SC_ERROR_NOT_ENOUGH_MEMORY;
}
// read a DO from the card
static int gids_get_DO(sc_card_t* card, int fileIdentifier, int dataObjectIdentifier, u8* response, size_t *responselen) {
sc_apdu_t apdu;
int r;
u8 data[4] = {0x5C, 0x02, (dataObjectIdentifier&0xFF00)>>8, (dataObjectIdentifier&0xFF)};
size_t datasize = 0;
const u8* p;
u8 buffer[MAX_GIDS_FILE_SIZE];
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
"Got args: fileIdentifier=%x, dataObjectIdentifier=%x, response=%p, responselen=%"SC_FORMAT_LEN_SIZE_T"u\n",
fileIdentifier, dataObjectIdentifier, response,
responselen ? *responselen : 0);
sc_format_apdu(card, &apdu,
response == NULL ? SC_APDU_CASE_3_SHORT : SC_APDU_CASE_4_SHORT, INS_GET_DATA, (fileIdentifier&0xFF00)>>8, (fileIdentifier&0xFF));
apdu.lc = 04;
apdu.data = data;
apdu.datalen = 04;
apdu.resp = buffer;
apdu.resplen = sizeof(buffer);
apdu.le = 256;
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids get data failed");
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return");
p = sc_asn1_find_tag(card->ctx, buffer, sizeof(buffer), dataObjectIdentifier, &datasize);
if (!p) {
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_FILE_NOT_FOUND);
}
if (datasize > *responselen) {
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_BUFFER_TOO_SMALL);
}
memcpy(response, p, datasize);
*responselen = datasize;
return SC_SUCCESS;
}
// write a DO to the card
static int gids_put_DO(sc_card_t* card, int fileIdentifier, int dataObjectIdentifier, u8 *data, size_t datalen) {
sc_apdu_t apdu;
int r;
u8 buffer[SC_MAX_EXT_APDU_BUFFER_SIZE];
u8* p = buffer;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
"Got args: fileIdentifier=%x, dataObjectIdentifier=%x, data=%p, datalen=%"SC_FORMAT_LEN_SIZE_T"u\n",
fileIdentifier, dataObjectIdentifier, data, datalen);
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, INS_PUT_DATA, (fileIdentifier&0xFF00)>>8, (fileIdentifier&0xFF));
r = sc_asn1_put_tag(dataObjectIdentifier, data, datalen, buffer, sizeof(buffer), &p);
LOG_TEST_RET(card->ctx, r, "Error handling TLV.");
apdu.data = buffer;
apdu.datalen = (size_t) (p - buffer);
apdu.lc = apdu.datalen;
apdu.flags |= SC_APDU_FLAGS_CHAINING;
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids put data failed");
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return");
return SC_SUCCESS;
}
// select the GIDS applet
static int gids_select_aid(sc_card_t* card, u8* aid, size_t aidlen, u8* response, size_t *responselen)
{
sc_apdu_t apdu;
int r;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
"Got args: aid=%p, aidlen=%"SC_FORMAT_LEN_SIZE_T"u, response=%p, responselen=%"SC_FORMAT_LEN_SIZE_T"u\n",
aid, aidlen, response, responselen ? *responselen : 0);
sc_format_apdu(card, &apdu,
response == NULL ? SC_APDU_CASE_3_SHORT : SC_APDU_CASE_4_SHORT, INS_SELECT, P1_SELECT_DF_BY_NAME, P2_SELECT_FIRST_OR_ONLY_OCCURENCE);
apdu.lc = aidlen;
apdu.data = aid;
apdu.datalen = aidlen;
apdu.resp = response;
apdu.resplen = responselen ? *responselen : 0;
apdu.le = response == NULL ? 0 : 256; /* could be 21 for fci */
r = sc_transmit_apdu(card, &apdu);
if (responselen)
*responselen = apdu.resplen;
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids select failed");
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2));
}
// DIRECT FILE MANIPULATION
///////////////////////////////////////////
// read a file given the masterfile
static int gids_read_gidsfile_without_cache(sc_card_t* card, u8* masterfile, size_t masterfilesize, char *directory, char *filename, u8* response, size_t *responselen) {
int r;
int fileIdentifier;
int dataObjectIdentifier;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
r = gids_get_identifiers(card, masterfile, masterfilesize, directory, filename, &fileIdentifier, &dataObjectIdentifier);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to get the identifier for the gids file");
r = gids_get_DO(card, fileIdentifier, dataObjectIdentifier, response, responselen);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to get the data from the file");
return r;
}
// write a file given the masterfile
static int gids_write_gidsfile_without_cache(sc_card_t* card, u8* masterfile, size_t masterfilesize, char *directory, char *filename, u8* data, size_t datalen) {
int r;
int fileIdentifier;
int dataObjectIdentifier;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
if (datalen > MAX_GIDS_FILE_SIZE) {
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_DATA);
}
r = gids_get_identifiers(card, masterfile, masterfilesize, directory, filename, &fileIdentifier, &dataObjectIdentifier);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to get the identifier for the gids file");
r = gids_put_DO(card, fileIdentifier, dataObjectIdentifier, data, datalen);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to get the data from the file");
return r;
}
// read the masterfile from the card
static int gids_read_masterfile(sc_card_t* card) {
struct gids_private_data* data = (struct gids_private_data*) card->drv_data;
int r = SC_SUCCESS;
data->masterfilesize = sizeof(data->masterfile);
r = gids_get_DO(card, MF_FI, MF_DO, data->masterfile, &data->masterfilesize);
if (r<0) {
data->masterfilesize = sizeof(data->masterfile);
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_CARD);
}
if (data->masterfilesize < 1 || data->masterfile[0] != 1) {
data->masterfilesize = sizeof(data->masterfile);
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_CARD);
}
return r;
}
// signal to the windows minidriver that something changed on the card and that it should refresh its cache
// the format of this file is specified in the minidriver specification
static int gids_update_cardcf(sc_card_t* card, int file, int container) {
struct gids_private_data* data = (struct gids_private_data*) card->drv_data;
u8 cardcf[6];
int r;
size_t cardcfsize = sizeof(cardcf);
r = gids_read_gidsfile_without_cache(card, data->masterfile, data->masterfilesize, "", "cardcf", cardcf, &cardcfsize);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to get the cardcf");
if (file) {
short filefreshness = cardcf[4] + cardcf[5] * 0x100;
filefreshness++;
cardcf[4] = filefreshness & 0xFF;
cardcf[5] = (filefreshness>>8) & 0xFF;
}
if (container) {
short containerfreshness = cardcf[2] + cardcf[3] * 0x100;
containerfreshness++;
cardcf[2] = containerfreshness & 0xFF;
cardcf[3] = (containerfreshness>>8) & 0xFF;
}
r = gids_write_gidsfile_without_cache(card, data->masterfile, data->masterfilesize, "", "cardcf", cardcf, 6);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to update the cardcf file");
return r;
}
// read a file
static int gids_read_gidsfile(sc_card_t* card, char *directory, char *filename, u8* response, size_t *responselen) {
struct gids_private_data* privatedata = (struct gids_private_data*) card->drv_data;
int r;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
if (privatedata->masterfilesize == sizeof(privatedata->masterfile)) {
r = gids_read_masterfile(card);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to get the masterfile");
}
r = gids_read_gidsfile_without_cache(card, privatedata->masterfile, privatedata->masterfilesize,
directory, filename, response, responselen);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to read the file");
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,r);
}
// check for the existence of a file
static int gids_does_file_exists(sc_card_t *card, char* directory, char* filename) {
struct gids_private_data* privatedata = (struct gids_private_data*) card->drv_data;
int fileIdentifier, dataObjectIdentifier;
return gids_get_identifiers(card, privatedata->masterfile, privatedata->masterfilesize, directory, filename,
&fileIdentifier, &dataObjectIdentifier);
}
// write a file already existing
static int gids_write_gidsfile(sc_card_t* card, char *directory, char *filename, u8* data, size_t datalen) {
struct gids_private_data* privatedata = (struct gids_private_data*) card->drv_data;
int r;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
r = gids_update_cardcf(card, 1, 0);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to update the cache file");
r = gids_write_gidsfile_without_cache(card, privatedata->masterfile, privatedata->masterfilesize,
directory, filename, data, datalen);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to write the file");
if (strcmp(directory, "mscp") == 0 && strcmp(filename, "cmapfile") == 0) {
// update the cmapfile cache
privatedata->cmapfilesize = datalen;
memcpy(privatedata->cmapfile, data, datalen);
}
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,r);
}
// read the cmapfile (container description)
static int gids_read_cmapfile(sc_card_t* card) {
struct gids_private_data* data = (struct gids_private_data*) card->drv_data;
int r = SC_SUCCESS;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
data->cmapfilesize = sizeof(data->cmapfile);
r = gids_read_gidsfile(card, "mscp", "cmapfile", data->cmapfile, &data->cmapfilesize);
if (r<0) {
data->cmapfilesize = sizeof(data->cmapfile);
}
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to get the cmapfile");
return r;
}
// create a file record in the masterfile
static int gids_create_file(sc_card_t *card, char* directory, char* filename) {
int r;
u8 masterfilebuffer[MAX_GIDS_FILE_SIZE];
size_t masterfilebuffersize;
struct gids_private_data* privatedata = (struct gids_private_data*) card->drv_data;
int fileIdentifier, dataObjectIdentifier;
size_t records;
int offset;
gids_mf_record_t* record;
r = gids_find_available_DO(card, privatedata->masterfile, privatedata->masterfilesize, &fileIdentifier, &dataObjectIdentifier);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to find an empty DO");
memcpy(masterfilebuffer, privatedata->masterfile, privatedata->masterfilesize);
masterfilebuffersize = privatedata->masterfilesize + sizeof(gids_mf_record_t);
if (masterfilebuffersize > MAX_GIDS_FILE_SIZE) {
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_ENOUGH_MEMORY);
}
records = ((privatedata->masterfilesize -1) / sizeof(gids_mf_record_t));
offset = 1 + sizeof(gids_mf_record_t) * records;
memcpy(masterfilebuffer + offset + sizeof(gids_mf_record_t), masterfilebuffer + offset,
privatedata->masterfilesize - offset);
memset(masterfilebuffer + offset, 0, sizeof(gids_mf_record_t));
record = (gids_mf_record_t*) (masterfilebuffer + offset);
strncpy(record->directory, directory, 8);
strlcpy(record->filename, filename, sizeof(record->filename));
record->fileIdentifier = fileIdentifier;
record->dataObjectIdentifier = dataObjectIdentifier;
r = gids_update_cardcf(card, 1, 0);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to update the cardcf");
r = gids_put_DO(card, MF_FI, MF_DO, masterfilebuffer, masterfilebuffersize);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to update the masterfile");
memcpy(privatedata->masterfile, masterfilebuffer, masterfilebuffersize);
privatedata->masterfilesize = masterfilebuffersize;
return r;
}
// CERTIFICATE HANDLING FUNCTIONS
////////////////////////////////////////////////////
// prepare a sc_path structure given a file identifier & DO
// this will be an input of the gids_read_public_key function
static int gids_build_certificate_path(sc_card_t* card, unsigned char containerindex, unsigned char issignatureonly,sc_path_t* cpath) {
struct gids_private_data* data = (struct gids_private_data*) card->drv_data;
int r, fileIdentifier, dataObjectIdentifier;
char file[9];
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
if (issignatureonly) {
snprintf(file, 9, "ksc%02X", containerindex);
} else {
snprintf(file, 9, "kxc%02X", containerindex);
}
r = gids_get_identifiers(card, data->masterfile, data->masterfilesize, "mscp", file, &fileIdentifier, &dataObjectIdentifier);
if (r < 0) return SC_ERROR_OBJECT_NOT_FOUND;
memset(cpath, 0, sizeof(sc_path_t));
cpath->type = SC_PATH_TYPE_PATH;
cpath->len = 4;
cpath->value[0] = (u8) ((fileIdentifier >> 8) & 0xFF);
cpath->value[1] = (u8) fileIdentifier & 0xFF;
cpath->value[2] = (u8) ((dataObjectIdentifier >> 8) & 0xFF);
cpath->value[3] = (u8) dataObjectIdentifier & 0xFF;
cpath->count = -1;
return SC_SUCCESS;
}
// PIN HANDLING FUNCTIONS
////////////////////////////////////////////////////
// get the pin status
static int gids_get_pin_status(sc_card_t *card, int pinreference, int *tries_left, int *max_tries) {
int dataObjectIdentifier, r;
u8 buffer[100];
const u8* p;
size_t buffersize = sizeof(buffer);
size_t datasize;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
if (tries_left) *tries_left = -1;
if (max_tries) *max_tries = -1;
switch(pinreference) {
case GIDS_PIN_IDENTIFIER:
dataObjectIdentifier = GIDS_PIN_STATUS_OBJECT_IDENTIFIER;
break;
case GIDS_PUK_IDENTIFIER:
dataObjectIdentifier = GIDS_PUK_STATUS_OBJECT_IDENTIFIER;
break;
default:
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OBJECT_NOT_FOUND);
}
r = gids_get_DO(card, GIDS_APPLET_EFID, dataObjectIdentifier, buffer, &buffersize);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to update the masterfile");
buffersize = buffersize > sizeof(buffer) ? sizeof(buffer) : buffersize;
p = sc_asn1_find_tag(card->ctx, buffer, buffersize, GIDS_TRY_COUNTER_OLD_TAG, &datasize);
if (p && datasize == 1) {
if (tries_left)
*tries_left = p[0];
}
p = sc_asn1_find_tag(card->ctx, buffer, buffersize, GIDS_TRY_COUNTER_TAG, &datasize);
if (p && datasize == 1) {
if (tries_left)
*tries_left = p[0];
}
p = sc_asn1_find_tag(card->ctx, buffer, buffersize , GIDS_TRY_LIMIT_TAG, &datasize);
if (p && datasize == 1) {
if (tries_left)
*max_tries = p[0];
}
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
"Pin information for PIN 0x%x: triesleft=%d trieslimit=%d\n", pinreference, (tries_left?*tries_left:-1), (max_tries?*max_tries:-1));
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS);
}
static int gids_match_card(sc_card_t * card)
{
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
int r,i;
size_t resplen = sizeof(rbuf);
const u8 *tag;
size_t taglen;
const u8 *aid;
size_t aidlen;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
/* Detect by selecting applet */
r = gids_select_aid(card, gids_aid.value, gids_aid.len, rbuf, &resplen);
if (r<0) return 0;
card->type = SC_CARD_TYPE_GIDS_GENERIC;
if (resplen > 2) {
tag = sc_asn1_find_tag(card->ctx, rbuf, resplen, GIDS_APPLICATION_TEMPLATE_TAG, &taglen);
if (tag != NULL) {
aid = sc_asn1_find_tag(card->ctx, tag, taglen, GIDS_APPLICATION_AID_TAG, &aidlen);
if (aid != NULL ) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"found AID");
for (i = 0; gids_aids[i].len_long != 0; i++) {
if ( aidlen > gids_aids[i].len_long && memcmp(aid, gids_aids[i].value,
gids_aids[i].len_long) == 0) {
card->type = gids_aids[i].enumtag;
break;
}
}
}
}
}
return 1;
}
// extract the serial number from the cardid file
static int gids_get_serialnr(sc_card_t * card, sc_serial_number_t * serial)
{
int r;
u8 buffer[SC_MAX_EXT_APDU_BUFFER_SIZE];
size_t buffersize;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
buffersize = sizeof(buffer);
r = gids_read_gidsfile(card, "", "cardid", buffer, &buffersize);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to read cardid");
if (SC_MAX_SERIALNR < buffersize)
{
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL);
}
/* cache serial number */
card->serialnr.len = buffersize;
memcpy(card->serialnr.value, buffer, card->serialnr.len);
/* return cached serial number */
if (serial)
memcpy(serial, &card->serialnr, sizeof(*serial));
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS);
}
// initialize the driver
static int gids_init(sc_card_t * card)
{
unsigned long flags;
struct gids_private_data *data;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
// cache some data in memory
data = (struct gids_private_data*) calloc(1, sizeof(struct gids_private_data));
if (!data) {
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY);
}
memset(data, 0, sizeof(struct gids_private_data));
card->drv_data = data;
// invalidate the master file and cmap file cache
data->cmapfilesize = sizeof(data->cmapfile);
data->masterfilesize = sizeof(data->masterfile);
/* supported RSA keys and how padding is done */
flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE | SC_ALGORITHM_ONBOARD_KEY_GEN | SC_ALGORITHM_RSA_RAW;
/* fix me: add other algorithms when the gids specification will tell how to extract the algo id from the FCP */
_sc_card_add_rsa_alg(card, 1024, flags, 0);
_sc_card_add_rsa_alg(card, 2048, flags, 0);
return SC_SUCCESS;
}
// cleanup
static int gids_finish(sc_card_t *card)
{
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
/* free the private data */
if (card->drv_data) {
free(card->drv_data);
card->drv_data = NULL;
}
return 0;
}
//see 12.5.3.1 Cryptographic Mechanism Identifier for Key with CRT
// the cmap file is used to detect the key algorithm / size
static int gids_get_crypto_identifier_from_key_ref(sc_card_t *card, const unsigned char keyref, unsigned char *cryptoidentifier) {
struct gids_private_data *data = (struct gids_private_data *) card->drv_data;
PCONTAINER_MAP_RECORD records = (PCONTAINER_MAP_RECORD) data->cmapfile;
int recordsnum = (int) (data->cmapfilesize / sizeof(CONTAINER_MAP_RECORD));
int index = keyref - GIDS_FIRST_KEY_IDENTIFIER;
if (index >= recordsnum) {
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS);
}
Coverity fixes (#1012) card-cac.c * CLANG_WARNING: The left operand of '<' is a garbage value card-coolkey.c * CLANG_WARNING: overwriting variable * CPPCHECK_WARNING: memory leak / overwrite variable * CLANG_WARNING: null pointer dereference * UNUSED_VALUE: unused return value card-gids.c * CLANG_WARNING: Branch condition evaluates to a garbage value * SIZEOF_MISMATCH: suspicious_sizeof card-myeid.c * RESOURCE_LEAK: Variable "buf" going out of scope leaks the storage it points to. * CLANG_WARNING: overwriting variable * (rewrite not to confuse coverity) pkcs15-cac.c * RESOURCE_LEAK: Variable "cert_out" going out of scope leaks the storage it points to. pkcs15-coolkey.c * UNUSED_VALUE: unused return value pkcs15-piv.c * RESOURCE_LEAK: Variable "cert_out" going out of scope leaks the storage it points to. pkcs15-sc-hsm.c * DEADCODE pkcs11/framework-pkcs15.c * RESOURCE_LEAK: Variable "p15_cert" going out of scope leaks the storage it points to. pkcs15init/pkcs15-lib.c * CLANG_WARNING: Assigned value is garbage or undefined pkcs15init/pkcs15-myeid.c * UNREACHABLE: Probably wrong placement of code block tests/p15dump.c * IDENTICAL_BRANCHES pkcs15-init.c * CLANG_WARNING: Potential leak of memory pointed to by 'args.der_encoded.value' pkcs15-tool.c * RESOURCE_LEAK: Variable "cert" going out of scope leaks the storage it points to. * MISSING_BREAK: The above case falls through to this one. sc-hsm-tool.c * CLANG_WARNING: Potential leak of memory pointed to by 'sp' westcos-tool.c * FORWARD_NULL: Passing null pointer "pin" to "unlock_pin", which dereferences it. * (rewrite not to confuse coverity) card-cac.c * Avoid malloc with 0 argument gids-tool.c * FORWARD_NULL -- copy&paste error scconf.c * CLANG_WARNING: Call to 'malloc' has an allocation size of 0 bytes closes #982
2017-04-03 11:43:30 +00:00
*cryptoidentifier = 0x00; /* initialize to zero */
if (records[index].wKeyExchangeKeySizeBits == 1024 || records[index].wSigKeySizeBits == 1024) {
*cryptoidentifier = GIDS_RSA_1024_IDENTIFIER;
return SC_SUCCESS;
}
if (records[index].wKeyExchangeKeySizeBits == 2048 || records[index].wSigKeySizeBits == 2048) {
*cryptoidentifier = GIDS_RSA_2048_IDENTIFIER;
return SC_SUCCESS;
}
if (records[index].wKeyExchangeKeySizeBits == 3072 || records[index].wSigKeySizeBits == 3072) {
*cryptoidentifier = GIDS_RSA_3072_IDENTIFIER;
return SC_SUCCESS;
}
if (records[index].wKeyExchangeKeySizeBits == 4096 || records[index].wSigKeySizeBits == 4096) {
*cryptoidentifier = GIDS_RSA_4096_IDENTIFIER;
return SC_SUCCESS;
}
if (records[index].wKeyExchangeKeySizeBits == 192 || records[index].wSigKeySizeBits == 192) {
*cryptoidentifier = GIDS_ECC_192_IDENTIFIER;
return SC_SUCCESS;
}
if (records[index].wKeyExchangeKeySizeBits == 224 || records[index].wSigKeySizeBits == 224) {
*cryptoidentifier = GIDS_ECC_224_IDENTIFIER;
return SC_SUCCESS;
}
if (records[index].wKeyExchangeKeySizeBits == 256 || records[index].wSigKeySizeBits == 256) {
*cryptoidentifier = GIDS_ECC_256_IDENTIFIER;
return SC_SUCCESS;
}
if (records[index].wKeyExchangeKeySizeBits == 384 || records[index].wSigKeySizeBits == 384) {
*cryptoidentifier = GIDS_ECC_384_IDENTIFIER;
return SC_SUCCESS;
}
if (records[index].wKeyExchangeKeySizeBits == 521 || records[index].wSigKeySizeBits == 521) {
*cryptoidentifier = GIDS_ECC_521_IDENTIFIER;
return SC_SUCCESS;
}
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS);
}
// same here
static u8 gids_get_crypto_identifier_from_prkey_info(struct sc_pkcs15_prkey_info *key_info) {
if (key_info->modulus_length > 0) {
if (key_info->modulus_length == 1024) {
return GIDS_RSA_1024_IDENTIFIER;
}
if (key_info->modulus_length == 2048) {
return GIDS_RSA_2048_IDENTIFIER;
}
if (key_info->modulus_length == 3072) {
return GIDS_RSA_3072_IDENTIFIER;
}
if (key_info->modulus_length == 4096) {
return GIDS_RSA_4096_IDENTIFIER;
}
return 0;
} else {
return 0;
}
}
// GIDS implementation of set security environment
static int gids_set_security_env(sc_card_t *card,
const sc_security_env_t *env,
int se_num)
{
struct sc_apdu apdu;
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
u8 *p;
int r, locked = 0;
assert(card != NULL && env != NULL);
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL);
2017-08-03 23:02:17 +00:00
memset(sbuf, 0, sizeof(sbuf));
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, INS_MANAGE_SECURITY_ENVIRONMENT, P1_DECIPHERMENT_INTERNAL_AUTHENTICATE_KEY_AGREEMENT, 0);
switch (env->operation) {
case SC_SEC_OPERATION_DECIPHER:
apdu.p2 = P2_DECIPHERMENT;
break;
case SC_SEC_OPERATION_SIGN:
apdu.p2 = P2_DIGITAL_SIGNATURE;
break;
default:
return SC_ERROR_INVALID_ARGUMENTS;
}
p = sbuf;
if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT) {
return SC_ERROR_NOT_SUPPORTED;
} else {
// ALG REF is mandatory
*p++ = 0x80; /* algorithm reference */
*p++ = 0x01;
gids_get_crypto_identifier_from_key_ref(card,env->key_ref[0],p);
if (env->operation == SC_SEC_OPERATION_DECIPHER) {
*p++ |= 0x40;
} else {
*p++ |= 0x50;
}
}
if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT)) {
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED);
}
if (env->flags & SC_SEC_ENV_KEY_REF_SYMMETRIC)
*p++ = 0x83;
else
*p++ = 0x84;
*p++ = (u8) env->key_ref_len;
assert(sizeof(sbuf) - (p - sbuf) >= env->key_ref_len);
memcpy(p, env->key_ref, env->key_ref_len);
p += env->key_ref_len;
r = (int) (p - sbuf);
apdu.lc = r;
apdu.datalen = r;
apdu.data = sbuf;
if (se_num > 0) {
r = sc_lock(card);
LOG_TEST_RET(card->ctx, r, "sc_lock() failed");
locked = 1;
}
if (apdu.datalen != 0) {
r = sc_transmit_apdu(card, &apdu);
if (r) {
sc_log(card->ctx, "%s: APDU transmit failed", sc_strerror(r));
goto err;
}
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
if (r) {
sc_log(card->ctx, "%s: Card returned error", sc_strerror(r));
goto err;
}
}
if (se_num <= 0)
return 0;
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, INS_MANAGE_SECURITY_ENVIRONMENT, 0xF2, se_num);
r = sc_transmit_apdu(card, &apdu);
sc_unlock(card);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
return sc_check_sw(card, apdu.sw1, apdu.sw2);
err:
if (locked)
sc_unlock(card);
return r;
}
// deauthenticate all pins
static int gids_logout(sc_card_t *card)
{
struct sc_apdu apdu;
int r;
assert(card && card->ctx);
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL);
// use the special PIN to deauthenticate
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, INS_VERIFY, 0x00, P2_PIN_DEAUTHENTICATE);
r = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2));
}
// read a public key
static int gids_read_public_key (struct sc_card *card , unsigned int algorithm,
struct sc_path * path, unsigned key_reference, unsigned modulus_length,
unsigned char **response, size_t *responselen) {
struct sc_pkcs15_pubkey_rsa rsa_key;
sc_apdu_t apdu;
size_t tlen, len;
const u8* keytemplate;
const u8* keydata;
int r;
u8 data[] = {0x70, 0x08, // retrieve key
0x84, 0x01, key_reference, // key reference
0xA5, 0x03, 0x7F, 0x49, 0x80 // key value template: only public key
};
u8 buffer[SC_MAX_EXT_APDU_BUFFER_SIZE];
size_t buffersize = sizeof(buffer);
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
"Got args: key_reference=%x, response=%p, responselen=%"SC_FORMAT_LEN_SIZE_T"u\n",
key_reference, response, responselen ? *responselen : 0);
sc_format_apdu(card, &apdu,
response == NULL ? SC_APDU_CASE_3_SHORT : SC_APDU_CASE_4_SHORT, INS_GET_DATA, 0x3F, 0xFF);
apdu.lc = sizeof(data);
apdu.data = data;
apdu.datalen = sizeof(data);
apdu.resp = buffer;
apdu.resplen = buffersize;
apdu.le = 256;
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids read public key failed");
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return");
buffersize = apdu.resplen;
keytemplate = sc_asn1_find_tag(card->ctx, buffer, buffersize, GIDS_PUBKEY_TAG, &tlen);
if (keytemplate == NULL) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid public key data: missing tag");
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
}
keydata = sc_asn1_find_tag(card->ctx, keytemplate, tlen, GIDS_PUBKEY_TAG_MODULUS, &len);
if (keydata != NULL) {
rsa_key.modulus.data = (u8*) keydata;
rsa_key.modulus.len = len;
Coverity fixes (#1012) card-cac.c * CLANG_WARNING: The left operand of '<' is a garbage value card-coolkey.c * CLANG_WARNING: overwriting variable * CPPCHECK_WARNING: memory leak / overwrite variable * CLANG_WARNING: null pointer dereference * UNUSED_VALUE: unused return value card-gids.c * CLANG_WARNING: Branch condition evaluates to a garbage value * SIZEOF_MISMATCH: suspicious_sizeof card-myeid.c * RESOURCE_LEAK: Variable "buf" going out of scope leaks the storage it points to. * CLANG_WARNING: overwriting variable * (rewrite not to confuse coverity) pkcs15-cac.c * RESOURCE_LEAK: Variable "cert_out" going out of scope leaks the storage it points to. pkcs15-coolkey.c * UNUSED_VALUE: unused return value pkcs15-piv.c * RESOURCE_LEAK: Variable "cert_out" going out of scope leaks the storage it points to. pkcs15-sc-hsm.c * DEADCODE pkcs11/framework-pkcs15.c * RESOURCE_LEAK: Variable "p15_cert" going out of scope leaks the storage it points to. pkcs15init/pkcs15-lib.c * CLANG_WARNING: Assigned value is garbage or undefined pkcs15init/pkcs15-myeid.c * UNREACHABLE: Probably wrong placement of code block tests/p15dump.c * IDENTICAL_BRANCHES pkcs15-init.c * CLANG_WARNING: Potential leak of memory pointed to by 'args.der_encoded.value' pkcs15-tool.c * RESOURCE_LEAK: Variable "cert" going out of scope leaks the storage it points to. * MISSING_BREAK: The above case falls through to this one. sc-hsm-tool.c * CLANG_WARNING: Potential leak of memory pointed to by 'sp' westcos-tool.c * FORWARD_NULL: Passing null pointer "pin" to "unlock_pin", which dereferences it. * (rewrite not to confuse coverity) card-cac.c * Avoid malloc with 0 argument gids-tool.c * FORWARD_NULL -- copy&paste error scconf.c * CLANG_WARNING: Call to 'malloc' has an allocation size of 0 bytes closes #982
2017-04-03 11:43:30 +00:00
} else {
rsa_key.modulus.len = 0;
}
keydata = sc_asn1_find_tag(card->ctx, keytemplate, tlen, GIDS_PUBKEY_TAG_EXPONENT, &len);
if (keydata != NULL) {
rsa_key.exponent.data = (u8*) keydata;
rsa_key.exponent.len = len;
Coverity fixes (#1012) card-cac.c * CLANG_WARNING: The left operand of '<' is a garbage value card-coolkey.c * CLANG_WARNING: overwriting variable * CPPCHECK_WARNING: memory leak / overwrite variable * CLANG_WARNING: null pointer dereference * UNUSED_VALUE: unused return value card-gids.c * CLANG_WARNING: Branch condition evaluates to a garbage value * SIZEOF_MISMATCH: suspicious_sizeof card-myeid.c * RESOURCE_LEAK: Variable "buf" going out of scope leaks the storage it points to. * CLANG_WARNING: overwriting variable * (rewrite not to confuse coverity) pkcs15-cac.c * RESOURCE_LEAK: Variable "cert_out" going out of scope leaks the storage it points to. pkcs15-coolkey.c * UNUSED_VALUE: unused return value pkcs15-piv.c * RESOURCE_LEAK: Variable "cert_out" going out of scope leaks the storage it points to. pkcs15-sc-hsm.c * DEADCODE pkcs11/framework-pkcs15.c * RESOURCE_LEAK: Variable "p15_cert" going out of scope leaks the storage it points to. pkcs15init/pkcs15-lib.c * CLANG_WARNING: Assigned value is garbage or undefined pkcs15init/pkcs15-myeid.c * UNREACHABLE: Probably wrong placement of code block tests/p15dump.c * IDENTICAL_BRANCHES pkcs15-init.c * CLANG_WARNING: Potential leak of memory pointed to by 'args.der_encoded.value' pkcs15-tool.c * RESOURCE_LEAK: Variable "cert" going out of scope leaks the storage it points to. * MISSING_BREAK: The above case falls through to this one. sc-hsm-tool.c * CLANG_WARNING: Potential leak of memory pointed to by 'sp' westcos-tool.c * FORWARD_NULL: Passing null pointer "pin" to "unlock_pin", which dereferences it. * (rewrite not to confuse coverity) card-cac.c * Avoid malloc with 0 argument gids-tool.c * FORWARD_NULL -- copy&paste error scconf.c * CLANG_WARNING: Call to 'malloc' has an allocation size of 0 bytes closes #982
2017-04-03 11:43:30 +00:00
} else {
rsa_key.exponent.len = 0;
}
if (rsa_key.exponent.len && rsa_key.modulus.len) {
r = sc_pkcs15_encode_pubkey_rsa(card->ctx, &rsa_key, response, responselen);
LOG_TEST_RET(card->ctx, r, "failed to read public key: cannot encode RSA public key");
} else {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "it is not a known public key");
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
}
if (response && responselen)
sc_log_hex(card->ctx, "encoded public key", *response, *responselen);
return SC_SUCCESS;
}
// emulate a filesystem given EF and DO
static int gids_select_file(sc_card_t *card, const struct sc_path *in_path,
struct sc_file **file_out) {
struct sc_file *file = NULL;
struct sc_context *ctx = card->ctx;
struct gids_private_data *data = (struct gids_private_data *) card->drv_data;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL);
data->state = GIDS_STATE_NONE;
data->currentDO = 0;
data->currentEFID = 0;
if (in_path->len == 4 && in_path->value[0] == 0xA0) {
// is it a DO pseudo file ?
// yes, succeed
data->currentEFID = in_path->value[1] + (in_path->value[0]<<8);
data->currentDO = in_path->value[3] + (in_path->value[2]<<8);
file = sc_file_new();
if (file == NULL)
LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY);
file->path = *in_path;
file->type = SC_FILE_TYPE_WORKING_EF;
file->ef_structure = SC_FILE_EF_TRANSPARENT;
file->size = SC_MAX_EXT_APDU_BUFFER_SIZE;
*file_out = file;
LOG_FUNC_RETURN(ctx, SC_SUCCESS);
} else if (in_path->len == 4 && in_path->value[0] == 0x3F && in_path->value[1] == 0xFF && in_path->type == SC_PATH_TYPE_PATH) {
// GIDS does not allow a select with a path containing a DF
// replace the file selection from SC_PATH_TYPE_PATH to SC_PATH_TYPE_FILE_ID
struct sc_path key_path;
memset(&key_path, 0, sizeof(key_path));
key_path.len = 2;
key_path.value[0] = in_path->value[2];
key_path.value[1] = in_path->value[3];
key_path.type = SC_PATH_TYPE_FILE_ID;
return iso_ops->select_file(card, &key_path, file_out);
} else {
return iso_ops->select_file(card, in_path, file_out);
}
}
static int gids_get_pin_policy(struct sc_card *card, struct sc_pin_cmd_data *data) {
int r;
if (data->pin_type != SC_AC_CHV) {
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
}
r = gids_get_pin_status(card, data->pin_reference, &(data->pin1.tries_left), &(data->pin1.max_tries));
LOG_TEST_RET(card->ctx, r, "gids_get_pin_status failed");
data->pin1.max_length = 16;
data->pin1.min_length = 4;
data->pin1.stored_length = 0;
data->pin1.encoding = SC_PIN_ENCODING_ASCII;
data->pin1.offset = 5;
data->pin1.logged_in = SC_PIN_STATE_UNKNOWN;
return SC_SUCCESS;
}
static int
gids_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) {
if (data->cmd == SC_PIN_CMD_GET_INFO) {
return gids_get_pin_policy(card, data);
} else {
return iso_ops->pin_cmd(card, data, tries_left);
}
}
// used to read existing certificates
static int gids_read_binary(sc_card_t *card, unsigned int offset,
unsigned char *buf, size_t count, unsigned long flags) {
struct gids_private_data *data = (struct gids_private_data *) card->drv_data;
struct sc_context *ctx = card->ctx;
int r;
int size;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL);
if (! data->currentDO || ! data->currentEFID) {
LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL);
}
if (data->state != GIDS_STATE_READ_DATA_PRESENT) {
// this function is called to read the certificate only
u8 buffer[SC_MAX_EXT_APDU_BUFFER_SIZE];
size_t buffersize = sizeof(buffer);
r = gids_get_DO(card, data->currentEFID, data->currentDO, buffer, &(buffersize));
if (r <0) return r;
if (buffersize < 4) {
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_DATA);
}
if (buffer[0] == 1 && buffer[1] == 0) {
size_t expectedsize = buffer[2] + buffer[3] * 0x100;
data->buffersize = sizeof(data->buffer);
r = sc_decompress(data->buffer, &(data->buffersize), buffer+4, buffersize-4, COMPRESSION_ZLIB);
if (r != SC_SUCCESS) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Zlib error: %d", r);
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
}
if (data->buffersize != expectedsize) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
"expected size: %"SC_FORMAT_LEN_SIZE_T"u real size: %"SC_FORMAT_LEN_SIZE_T"u",
expectedsize, data->buffersize);
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_DATA);
}
} else {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unknown compression method %d", buffer[0] + (buffer[1] <<8));
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_DATA);
}
data->state = GIDS_STATE_READ_DATA_PRESENT;
}
if (offset >= data->buffersize) {
return 0;
}
size = (int) MIN((data->buffersize - offset), count);
memcpy(buf, data->buffer + offset, size);
return size;
}
// refresh the internal caches and return the number of containers
static int
gids_get_all_containers(sc_card_t* card, size_t *recordsnum) {
int r;
struct gids_private_data *privatedata = (struct gids_private_data *) card->drv_data;
r = gids_read_masterfile(card);
LOG_TEST_RET(card->ctx, r, "unable to read the masterfile");
r = gids_read_cmapfile(card);
LOG_TEST_RET(card->ctx, r, "unable to read the cmapfile");
*recordsnum = (privatedata ->cmapfilesize / sizeof(CONTAINER_MAP_RECORD));
return SC_SUCCESS;
}
// return the detail about a container to emulate a pkcs15 card
static int
gids_get_container_detail(sc_card_t* card, sc_cardctl_gids_get_container_t* container) {
PCONTAINER_MAP_RECORD records = NULL;
struct gids_private_data *privatedata = (struct gids_private_data *) card->drv_data;
size_t recordsnum, num, i;
records = (PCONTAINER_MAP_RECORD) privatedata ->cmapfile;
recordsnum = (privatedata ->cmapfilesize / sizeof(CONTAINER_MAP_RECORD));
num = container->containernum ;
if (num >= recordsnum) {
return SC_ERROR_OBJECT_NOT_FOUND;
}
memset(container, 0, sizeof(sc_cardctl_gids_get_container_t));
container->containernum = num;
2017-10-27 17:11:15 +00:00
if (!(records[num].bFlags & CONTAINER_MAP_VALID_CONTAINER)) {
return SC_SUCCESS;
}
// ignore problematic containers
if (records[num].wKeyExchangeKeySizeBits > 0 && records[num].wSigKeySizeBits > 0) {
return SC_SUCCESS;
}
if (records[num].wKeyExchangeKeySizeBits == 0 && records[num].wSigKeySizeBits == 0) {
return SC_SUCCESS;
}
for (i = 0; i < MAX_CONTAINER_NAME_LEN; i++) {
container->label[i] = (char) records[num].wszGuid[i];
}
container->label[MAX_CONTAINER_NAME_LEN] = 0;
container->module_length = MAX(records[num].wKeyExchangeKeySizeBits, records[num].wSigKeySizeBits);
container->prvusage = SC_PKCS15_PRKEY_USAGE_SIGN;
container->pubusage = SC_PKCS15_PRKEY_USAGE_VERIFY;
if (records[num].wKeyExchangeKeySizeBits > 0) {
container->prvusage |= SC_PKCS15_PRKEY_USAGE_DECRYPT;
container->pubusage |= SC_PKCS15_PRKEY_USAGE_ENCRYPT;
}
// do not check for return code, typically if there is no certificate associated to the key
gids_build_certificate_path(card, (unsigned char) num, (records[num].wSigKeySizeBits > 0), &(container->certificatepath));
return SC_SUCCESS;
}
// find a new key reference
static int
gids_select_key_reference(sc_card_t *card, sc_pkcs15_prkey_info_t* key_info) {
struct gids_private_data *data = (struct gids_private_data *) card->drv_data;
PCONTAINER_MAP_RECORD records = (PCONTAINER_MAP_RECORD) data->cmapfile;
size_t recordsnum;
int r;
char ch_tmp[10];
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
// refresh the cached data in case some thing has been modified
r = gids_read_masterfile(card);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids read masterfile failed");
r = gids_read_cmapfile(card);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids read cmapfile failed");
recordsnum = (data->cmapfilesize / sizeof(CONTAINER_MAP_RECORD));
if (!key_info->key_reference) {
// new key
size_t i;
// search for a key number not used anymore
for (i = 0; i < recordsnum; i++) {
if (!(records[i].bFlags & CONTAINER_MAP_VALID_CONTAINER)) {
key_info->key_reference = (int) (GIDS_FIRST_KEY_IDENTIFIER + i);
return SC_SUCCESS;
}
}
// use a new key number
if (recordsnum > GIDS_MAX_CONTAINER) {
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_ENOUGH_MEMORY);
}
key_info->key_reference = (int) (GIDS_FIRST_KEY_IDENTIFIER + recordsnum);
} else {
// key was specified. Search if the key can be used
size_t i = key_info->key_reference - GIDS_FIRST_KEY_IDENTIFIER;
if (i > GIDS_MAX_CONTAINER) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid key ref %d", key_info->key_reference);
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS);
}
if (i > recordsnum) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
"container num is not allowed %"SC_FORMAT_LEN_SIZE_T"u %"SC_FORMAT_LEN_SIZE_T"u",
i, recordsnum);
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS);
}
}
snprintf(ch_tmp, sizeof(ch_tmp), "3FFFB0%02X", key_info->key_reference);
sc_format_path(ch_tmp, &(key_info->path));
return SC_SUCCESS;
}
// perform the creation of the key file
// try to mimic the GIDS minidriver key permission
static int gids_perform_create_keyfile(sc_card_t *card, u8 keytype, u8 kid, u8 algid) {
struct sc_apdu apdu;
int r;
u8 keyexchange[] = {0x62,0x47,
0x82,0x01,0x18, // file type
0x83,0x02,0xB0,kid, // key id = 81
0x8C,0x05,0x8F,0x10,0x10,0x10,0x00, // security
0xA5,0x37,
0xB8,0x09, // confidentiality
0x80,0x01,algid, //algo: rsa without padding
0x83,0x01,kid, // key id
0x95,0x01,0x40, // usage
0xB8,0x09, // confidentiality
0x80,0x01,0x80 + algid, // RSAES-OAEP padding
0x83,0x01,kid,
0x95,0x01,0x40,
0xB8,0x09, // confidentiality
0x80,0x01,0x40 + algid, // RSAES-PKCS1-v1_5 padding
0x83,0x01,kid,
0x95,0x01,0x40,
0xB6,0x09, // signature
0x80,0x01,0x10 + algid, // Full SHA off-card authorized
0x83,0x01,kid,
0x95,0x01,0x40,
0xB6,0x09, // signature
0x80,0x01,0x50 + algid, // RSASSA PKCS1-v 1_5 padding scheme (for RSA only; otherwise, RFU)
0x83,0x01,kid,
0x95,0x01,0x40
};
u8 sign[] = {0x62,0x26,
0x82,0x01,0x18, // file type
0x83,0x02,0xB0,kid, // key id = 81
0x8C,0x05,0x8F,0x10,0x10,0x10,0x00, // security
0xA5,0x16,
0xB6,0x09, // signature
0x80,0x01,0x10+ algid, // Full SHA off-card authorized
0x83,0x01,0x83, // key id
0x95,0x01,0x40, // usage
0xB6,0x09, // signature
0x80,0x01,0x50 + algid, // RSASSA PKCS1-v 1_5 padding scheme (for RSA only; otherwise, RFU)
0x83,0x01,0x83,
0x95,0x01,0x40};
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
// create the key file
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, INS_CREATE_FILE, 0x00, 0x00);
if (keytype == 1) {
apdu.lc = sizeof(keyexchange);
apdu.datalen = sizeof(keyexchange);
apdu.data = keyexchange;
} else if (keytype == 2) {
apdu.lc = sizeof(sign);
apdu.datalen = sizeof(sign);
apdu.data = sign;
} else {
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED);
}
r = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_TEST_RET(card->ctx, r, "Card returned error");
// activate file
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, INS_ACTIVATE_FILE, 0x00, 0x00);
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "ACTIVATE_FILE returned error");
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
}
// perform the creation of the keyfile and its registration in the cmapfile and keymap file
static int gids_create_keyfile(sc_card_t *card, sc_pkcs15_object_t *object) {
int r;
u8 keytype;
struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *) object->data;
u8 kid = key_info->key_reference;
u8 algid = gids_get_crypto_identifier_from_prkey_info(key_info);
u8 cmapbuffer[MAX_GIDS_FILE_SIZE];
size_t cmapbuffersize = 0;
u8 keymapbuffer[MAX_GIDS_FILE_SIZE];
size_t keymapbuffersize = 0;
size_t keymaprecordnum = 0;
struct gids_private_data *data = (struct gids_private_data *) card->drv_data;
size_t recordnum;
size_t containernum = key_info->key_reference - GIDS_FIRST_KEY_IDENTIFIER;
PCONTAINER_MAP_RECORD records = ((PCONTAINER_MAP_RECORD) cmapbuffer) + containernum;
struct gids_keymap_record* keymaprecord = NULL;
int i;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
// sanity check
assert((object->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_PRKEY);
if (!algid) {
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED);
}
// masterfile & cmapfile have been refreshed in gids_perform_create_keyfile
recordnum = (data->cmapfilesize / sizeof(CONTAINER_MAP_RECORD));
// sanity check
if (containernum > recordnum || containernum > GIDS_MAX_CONTAINER)
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL);
// refresh the key map file
keymapbuffersize = sizeof(keymapbuffer);
r = gids_get_DO(card, KEYMAP_FI, KEYMAP_DO, keymapbuffer, &keymapbuffersize);
if (r<0) {
// the keymap DO should be present if the cmapfile is not empty
if (recordnum > 0) {
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL);
}
// else can be empty if not record
keymapbuffersize = 0;
} else {
keymaprecordnum = (keymapbuffersize - 1) / sizeof(struct gids_keymap_record);
if (keymaprecordnum != recordnum) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL , "Error: Unable to create the key file because the keymap and cmapfile are inconsistent");
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL ,
"keymaprecordnum = %"SC_FORMAT_LEN_SIZE_T"u recordnum = %"SC_FORMAT_LEN_SIZE_T"u",
keymaprecordnum, recordnum);
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL);
}
}
// prepare the cmap & keymap buffer
if (containernum == recordnum) {
// reserve space on the cmap file
memset(cmapbuffer, 0, sizeof(cmapbuffer));
memcpy(cmapbuffer, data->cmapfile, data->cmapfilesize);
cmapbuffersize = data->cmapfilesize + sizeof(CONTAINER_MAP_RECORD);
r = gids_write_gidsfile(card, "mscp", "cmapfile", cmapbuffer, cmapbuffersize);
LOG_TEST_RET(card->ctx, r, "unable to reserve space on the cmapfile");
if (keymapbuffersize == 0) {
keymapbuffersize = 1;
keymapbuffer[0] = 1;
}
keymapbuffersize += sizeof(struct gids_keymap_record);
} else {
memcpy(cmapbuffer, data->cmapfile, data->cmapfilesize);
cmapbuffersize = data->cmapfilesize;
}
keymaprecord = ((struct gids_keymap_record*)(keymapbuffer +1)) + containernum;
memset(records, 0, sizeof(CONTAINER_MAP_RECORD));
memset(keymaprecord, 0, sizeof(struct gids_keymap_record));
if (key_info->usage & SC_PKCS15_PRKEY_USAGE_DECRYPT) {
keytype = 1; // AT_KEYEXCHANGE
records->wKeyExchangeKeySizeBits = (unsigned short) key_info->modulus_length;
keymaprecord->keytype = GIDS_KEY_TYPE_AT_KEYEXCHANGE;
} else if (key_info->usage & SC_PKCS15_PRKEY_USAGE_SIGN) {
keytype = 2; // AT_SIGNATURE
records->wSigKeySizeBits = (unsigned short) key_info->modulus_length;
keymaprecord->keytype = GIDS_KEY_TYPE_AT_SIGNATURE;
} else {
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED);
}
//the GIDS card must have unique container names
// avoid the problem with the default label by making it unique
if (strcmp(DEFAULT_PRIVATE_KEY_LABEL, object->label) == 0 && strlen(DEFAULT_PRIVATE_KEY_LABEL) + 3 < MAX_CONTAINER_NAME_LEN) {
char addition[4] = " 00";
addition[1] += containernum % 10;
addition[2] += (containernum < 0xFF) / 10;
strcat(object->label, addition);
}
// convert char to wchar
for(i = 0; i < MAX_CONTAINER_NAME_LEN && object->label[i]; i++) {
records->wszGuid[i] = object->label[i];
}
// TODO: check if a container with the same name already exists and prevent is creation or change its name
records->bFlags = CONTAINER_MAP_VALID_CONTAINER;
if (recordnum == 0) {
records->bFlags |= CONTAINER_MAP_DEFAULT_CONTAINER;
}
keymaprecord->algid = algid;
keymaprecord->state = 1;
keymaprecord->unknownWithFFFF = (unsigned short) (-1);
keymaprecord->keyref = 0xB000 + kid;
r = gids_perform_create_keyfile(card, keytype, kid, algid);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to create the key file");
r = gids_update_cardcf(card, 0, 1);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to update the cardcf file regarding container");
r = gids_put_DO(card, KEYMAP_FI, KEYMAP_DO, keymapbuffer, keymapbuffersize);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to write the keymap file");
r = gids_write_gidsfile(card, "mscp", "cmapfile", cmapbuffer, cmapbuffersize);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to write the cmap file after the container creation");
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
}
// generate a key on an existing container
static int gids_generate_key(sc_card_t *card, sc_pkcs15_object_t *object, struct sc_pkcs15_pubkey* pubkey) {
struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *) object->data;
u8 kid = key_info->key_reference;
u8 algid = gids_get_crypto_identifier_from_prkey_info(key_info);
struct sc_apdu apdu;
u8 generatekey[] = {0xAC, 0x06, // CRT template
0x80, 0x01, algid, // algorithm
0x83, 0x01, kid // key reference
};
int r;
u8 *buffer = NULL;
size_t buffersize = 0;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
assert((object->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_PRKEY);
if ((key_info->key_reference > GIDS_FIRST_KEY_IDENTIFIER + GIDS_MAX_CONTAINER) || (kid < GIDS_FIRST_KEY_IDENTIFIER)) {
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_DATA);
}
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, INS_GENERATE_ASYMECTRIC_KEY_PAIR, 0x00, 0x00);
apdu.lc = sizeof(generatekey);
apdu.datalen = sizeof(generatekey);
apdu.data = generatekey;
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "generate key returned error");
r = gids_read_public_key(card, 0, NULL, kid, 0, &buffer, &buffersize);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "read public key returned error");
r = sc_pkcs15_decode_pubkey(card->ctx, pubkey, buffer, buffersize);
if (buffer)
free(buffer);
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
}
// import the key in an existing container
static int gids_import_key(sc_card_t *card, sc_pkcs15_object_t *object, sc_pkcs15_prkey_t *key) {
struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *) object->data;
int version = 0;
int keytype = 2; // RSA
u8 kid = key_info->key_reference;
size_t len = 1;
int encryptkeyref = 0; //NONE
int r;
u8* buffer = NULL;
size_t buflen = 0;
struct sc_asn1_entry asn1_key_usage_template[] = {
{ "keyReference", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING | SC_ASN1_CTX, 0, NULL, NULL },
{ "KeyValueTemplate", SC_ASN1_STRUCT, SC_ASN1_TAG_NULL | SC_ASN1_CTX | SC_ASN1_CONS, 0, NULL, NULL },
{ NULL, 0, 0, 0, NULL, NULL }
};
struct sc_asn1_entry asn1_key_value_template[] = {
{ "keyType", SC_ASN1_INTEGER, SC_ASN1_TAG_BIT_STRING | SC_ASN1_CTX, 0, NULL, NULL },
{ "encryptKeyRef", SC_ASN1_INTEGER, SC_ASN1_TAG_OCTET_STRING | SC_ASN1_CTX, 0, NULL, NULL },
{ "keyValue", SC_ASN1_STRUCT, SC_ASN1_TAG_OBJECT_DESCRIPTOR | SC_ASN1_CTX, 0, NULL, NULL },
{ NULL, 0, 0, 0, NULL, NULL }
};
struct sc_asn1_entry asn1_key_data[] = {
{ "keyData", SC_ASN1_STRUCT, SC_ASN1_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL },
{ NULL, 0, 0, 0, NULL, NULL }
};
struct sc_asn1_entry asn1_rsa_priv_coefficients_gids[] = {
{ "version", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL },
{ "modulus", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC, NULL, NULL },
{ "publicExponent", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC, NULL, NULL },
{ "privateExponent", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC, NULL, NULL },
{ "p", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC, NULL, NULL },
{ "q", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC, NULL, NULL },
{ "dmp1", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC, NULL, NULL },
{ "dmq1", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC, NULL, NULL },
{ "iqmp", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC, NULL, NULL },
{ NULL, 0, 0, 0, NULL, NULL }
};
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
assert((object->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_PRKEY);
if (object->type != SC_PKCS15_TYPE_PRKEY_RSA || key->algorithm != SC_ALGORITHM_RSA) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "GIDS supports RSA keys only (but may support ECC one day).");
return SC_ERROR_NOT_SUPPORTED;
}
if (!key->u.rsa.dmp1.len || !key->u.rsa.dmq1.len || !key->u.rsa.iqmp.len) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "GIDS needs dmp1 & dmq1 & iqmp");
return SC_ERROR_NOT_SUPPORTED;
}
sc_format_asn1_entry(asn1_rsa_priv_coefficients_gids + 0, &version, NULL, 1);
sc_format_asn1_entry(asn1_rsa_priv_coefficients_gids + 1, key->u.rsa.modulus.data, &key->u.rsa.modulus.len, 1);
sc_format_asn1_entry(asn1_rsa_priv_coefficients_gids + 2, key->u.rsa.exponent.data, &key->u.rsa.exponent.len, 1);
sc_format_asn1_entry(asn1_rsa_priv_coefficients_gids + 3, key->u.rsa.d.data, &key->u.rsa.d.len, 1);
sc_format_asn1_entry(asn1_rsa_priv_coefficients_gids + 4, key->u.rsa.p.data, &key->u.rsa.p.len, 1);
sc_format_asn1_entry(asn1_rsa_priv_coefficients_gids + 5, key->u.rsa.q.data, &key->u.rsa.q.len, 1);
sc_format_asn1_entry(asn1_rsa_priv_coefficients_gids + 6, key->u.rsa.dmp1.data, &key->u.rsa.dmp1.len, 1);
sc_format_asn1_entry(asn1_rsa_priv_coefficients_gids + 7, key->u.rsa.dmq1.data, &key->u.rsa.dmq1.len, 1);
sc_format_asn1_entry(asn1_rsa_priv_coefficients_gids + 8, key->u.rsa.iqmp.data, &key->u.rsa.iqmp.len, 1);
sc_format_asn1_entry(asn1_key_data + 0, asn1_rsa_priv_coefficients_gids, NULL, 1);
sc_format_asn1_entry(asn1_key_value_template + 0, &keytype, NULL, 1);
sc_format_asn1_entry(asn1_key_value_template + 1, &encryptkeyref, NULL, 1);
sc_format_asn1_entry(asn1_key_value_template + 2, asn1_key_data, NULL, 1);
sc_format_asn1_entry(asn1_key_usage_template + 0, &kid, &len, 1);
sc_format_asn1_entry(asn1_key_usage_template + 1, asn1_key_value_template, NULL, 1);
r = sc_asn1_encode(card->ctx, asn1_key_usage_template, &buffer, &buflen);
SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to encode the private key");
r = gids_put_DO(card, GIDS_APPLET_EFID, GIDS_PUT_KEY_DO, buffer, buflen);
SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to put the private key - key greater than 2048 bits ?");
r = SC_SUCCESS;
err:
Coverity fixes (#1012) card-cac.c * CLANG_WARNING: The left operand of '<' is a garbage value card-coolkey.c * CLANG_WARNING: overwriting variable * CPPCHECK_WARNING: memory leak / overwrite variable * CLANG_WARNING: null pointer dereference * UNUSED_VALUE: unused return value card-gids.c * CLANG_WARNING: Branch condition evaluates to a garbage value * SIZEOF_MISMATCH: suspicious_sizeof card-myeid.c * RESOURCE_LEAK: Variable "buf" going out of scope leaks the storage it points to. * CLANG_WARNING: overwriting variable * (rewrite not to confuse coverity) pkcs15-cac.c * RESOURCE_LEAK: Variable "cert_out" going out of scope leaks the storage it points to. pkcs15-coolkey.c * UNUSED_VALUE: unused return value pkcs15-piv.c * RESOURCE_LEAK: Variable "cert_out" going out of scope leaks the storage it points to. pkcs15-sc-hsm.c * DEADCODE pkcs11/framework-pkcs15.c * RESOURCE_LEAK: Variable "p15_cert" going out of scope leaks the storage it points to. pkcs15init/pkcs15-lib.c * CLANG_WARNING: Assigned value is garbage or undefined pkcs15init/pkcs15-myeid.c * UNREACHABLE: Probably wrong placement of code block tests/p15dump.c * IDENTICAL_BRANCHES pkcs15-init.c * CLANG_WARNING: Potential leak of memory pointed to by 'args.der_encoded.value' pkcs15-tool.c * RESOURCE_LEAK: Variable "cert" going out of scope leaks the storage it points to. * MISSING_BREAK: The above case falls through to this one. sc-hsm-tool.c * CLANG_WARNING: Potential leak of memory pointed to by 'sp' westcos-tool.c * FORWARD_NULL: Passing null pointer "pin" to "unlock_pin", which dereferences it. * (rewrite not to confuse coverity) card-cac.c * Avoid malloc with 0 argument gids-tool.c * FORWARD_NULL -- copy&paste error scconf.c * CLANG_WARNING: Call to 'malloc' has an allocation size of 0 bytes closes #982
2017-04-03 11:43:30 +00:00
sc_mem_clear(buffer, buflen);
LOG_FUNC_RETURN(card->ctx, r);
}
// remove a crt file
static int gids_delete_key_file(sc_card_t *card, int containernum) {
int r;
char ch_tmp[10];
sc_path_t cpath;
snprintf(ch_tmp, sizeof(ch_tmp), "B0%02X",containernum + GIDS_FIRST_KEY_IDENTIFIER);
sc_format_path(ch_tmp, &cpath);
r = iso_ops->select_file(card, &cpath, NULL);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to select the key file");
// delete current selected file
memset(&cpath, 0, sizeof(cpath));
r = iso_ops->delete_file(card, &cpath);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to delete the key file");
return r;
}
// encode a certificate using the minidriver compression
static int gids_encode_certificate(sc_card_t *card, u8* source, size_t sourcesize, u8* destination, size_t* destinationsize) {
int r;
size_t outlen;
if (*destinationsize < 4) {
return SC_ERROR_BUFFER_TOO_SMALL;
}
if (sourcesize > 0xFFFF) {
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY);
}
// format is:
// 2 bytes for compression version
// 2 bytes for uncompressed file size
// ZLIB compression of the certificate
destination[0] = 1;
destination[1] = 0;
destination[2] = sourcesize & 0xFF;
destination[3] = (sourcesize & 0xFF00) >> 8;
outlen = *destinationsize - 4;
r = sc_compress(destination + 4, &outlen, source, sourcesize, COMPRESSION_ZLIB);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to compress the certificate");
*destinationsize = outlen + 4;
return SC_SUCCESS;
}
// save a certificate associated to a container to the card
static int gids_save_certificate(sc_card_t *card, sc_pkcs15_object_t *certobject,
sc_pkcs15_object_t *privkeyobject, struct sc_path *path) {
int r;
u8 certbuffer[MAX_GIDS_FILE_SIZE];
size_t certbuffersize = sizeof(certbuffer);
struct sc_pkcs15_cert_info *cert_info = (struct sc_pkcs15_cert_info *) certobject->data;
struct sc_pkcs15_prkey_info *prkey_info = (struct sc_pkcs15_prkey_info *) privkeyobject->data;
unsigned char containernum;
char filename[9];
assert((certobject->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_CERT);
assert((privkeyobject->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_PRKEY);
// refresh the cached data in case some thing has been modified
r = gids_read_masterfile(card);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids read masterfile failed");
r= gids_read_cmapfile(card);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids read cmapfile failed");
// compress the certificate according to the minidriver specification
r = gids_encode_certificate(card, cert_info->value.value, cert_info->value.len, certbuffer, &certbuffersize);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to encode the certificate");
// save it to a minidriver file
containernum = prkey_info->key_reference - GIDS_FIRST_KEY_IDENTIFIER;
if (!(prkey_info->usage & SC_PKCS15_PRKEY_USAGE_DECRYPT)) {
snprintf(filename, sizeof(filename), "ksc%02X", containernum);
} else {
snprintf(filename, sizeof(filename), "kxc%02X", containernum);
}
r = gids_does_file_exists(card, "mscp", filename);
if (r == SC_ERROR_FILE_NOT_FOUND) {
r = gids_create_file(card, "mscp", filename);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to create the certificate file");
}
r = gids_write_gidsfile(card, "mscp", filename, certbuffer, certbuffersize);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to write the certificate data");
// return the path to the DO
r = gids_build_certificate_path(card, containernum, !(prkey_info->usage & SC_PKCS15_PRKEY_USAGE_DECRYPT), path);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to build the certificate path");
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
// remove a container and its registration in the cmapfile
static int gids_delete_container_num(sc_card_t *card, size_t containernum) {
int r;
u8 cmapbuffer[MAX_GIDS_FILE_SIZE];
size_t cmapbuffersize = 0;
u8 keymapbuffer[MAX_GIDS_FILE_SIZE];
size_t keymapbuffersize = 0;
size_t keymaprecordnum = 0;
struct gids_private_data *data = (struct gids_private_data *) card->drv_data;
size_t recordnum;
PCONTAINER_MAP_RECORD records = ((PCONTAINER_MAP_RECORD) cmapbuffer) + containernum;
struct gids_keymap_record* keymaprecord = NULL;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
// masterfile & cmapfile have been refreshed before
recordnum = (data->cmapfilesize / sizeof(CONTAINER_MAP_RECORD));
// sanity check
if (containernum >= recordnum || recordnum > GIDS_MAX_CONTAINER)
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL);
// refresh the key map file
keymapbuffersize = sizeof(keymapbuffer);
r = gids_get_DO(card, KEYMAP_FI, KEYMAP_DO, keymapbuffer, &keymapbuffersize);
if (r<0) {
// the keymap DO should be present if the cmapfile is not empty
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL);
}
keymaprecordnum = (keymapbuffersize - 1) / sizeof(struct gids_keymap_record);
if (keymaprecordnum != recordnum) {
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL);
}
// update the key map file
memcpy(cmapbuffer, data->cmapfile, data->cmapfilesize);
cmapbuffersize = data->cmapfilesize;
keymaprecord = ((struct gids_keymap_record*)(keymapbuffer +1)) + containernum;
memset(records, 0, sizeof(CONTAINER_MAP_RECORD));
memset(keymaprecord, 0, sizeof(struct gids_keymap_record));
keymaprecord->unknownWithFFFF = (unsigned short) (-1);
keymaprecord->keyref =(unsigned short) (-1);
// remove the key, update the key map & cmap file and signal the change
r = gids_delete_key_file(card, (int) containernum);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to delete the key file");
r = gids_update_cardcf(card, 0, 1);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to update the cardcf file regarding container");
r = gids_put_DO(card, KEYMAP_FI, KEYMAP_DO, keymapbuffer, keymapbuffersize);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to write the keymap file");
r = gids_write_gidsfile(card, "mscp", "cmapfile", cmapbuffer, cmapbuffersize);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to write the cmap file after the container creation");
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS);
}
// delete a certificate associated to a container
static int gids_delete_cert(sc_card_t *card, sc_pkcs15_object_t* object) {
int r;
struct gids_private_data *privatedata = (struct gids_private_data *) card->drv_data;
struct sc_pkcs15_cert_info *cert_info = (struct sc_pkcs15_cert_info *) object->data;
unsigned short fileIdentifier, DO;
u8 masterfilebuffer[MAX_GIDS_FILE_SIZE];
size_t masterfilebuffersize = 0;
gids_mf_record_t *records = (gids_mf_record_t *) masterfilebuffer;
size_t recordcount, recordnum = (size_t) -1;
size_t i;
assert((object->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_CERT);
// refresh the cached data in case some thing has been modified
r = gids_read_masterfile(card);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids read masterfile failed");
r= gids_read_cmapfile(card);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids read cmapfile failed");
// remove the file reference from the masterfile
if (cert_info->path.len != 4) {
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL);
}
fileIdentifier = cert_info->path.value[0] * 0x100 + cert_info->path.value[1];
DO = cert_info->path.value[2] * 0x100 + cert_info->path.value[3];
memcpy(masterfilebuffer, privatedata->masterfile, privatedata->masterfilesize);
masterfilebuffersize = privatedata->masterfilesize;
recordcount = (masterfilebuffersize / sizeof(gids_mf_record_t));
for (i = 0; i < recordcount; i++) {
if (records[i].fileIdentifier == fileIdentifier && records[i].dataObjectIdentifier == DO) {
recordnum = i;
break;
}
}
if (recordnum == (size_t) -1) {
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_FILE_NOT_FOUND);
}
for (i = (recordnum+1) * sizeof(gids_mf_record_t); i < masterfilebuffersize; i++) {
masterfilebuffer[i - sizeof(gids_mf_record_t)] = masterfilebuffer[i];
}
masterfilebuffersize -= sizeof(gids_mf_record_t);
// remove the DO, update the masterfile, and signal the change
r = gids_update_cardcf(card, 1, 0);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to update the cache file");
r = gids_put_DO(card, fileIdentifier, DO, NULL, 0);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to delete the certificate DO");
r = gids_put_DO(card, MF_FI, MF_DO, masterfilebuffer, masterfilebuffersize);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to update the masterfile");
memcpy(privatedata->masterfile, masterfilebuffer, masterfilebuffersize);
privatedata->masterfilesize = masterfilebuffersize;
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
static int gids_delete_key(sc_card_t *card, sc_pkcs15_object_t* object) {
int r;
size_t containernum;
struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *) object->data;
assert((object->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_PRKEY);
// refresh the cached data in case some thing has been modified
r = gids_read_masterfile(card);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids read masterfile failed");
r = gids_read_cmapfile(card);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids read cmapfile failed");
containernum = key_info->key_reference - GIDS_FIRST_KEY_IDENTIFIER;
r = gids_delete_container_num(card, containernum);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to delete the container");
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
// used by gids_initialize to create the filesystem
static int gids_initialize_create_file(sc_card_t *card, u8* command, size_t commandsize) {
int r;
sc_apdu_t apdu;
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, INS_CREATE_FILE, 0x00, 0x00);
apdu.lc = commandsize;
apdu.data = command;
apdu.datalen = commandsize;
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU1 transmit failed");
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return");
// activate file
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, INS_ACTIVATE_FILE, 0x00, 0x00);
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU2 transmit failed");
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return");
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
}
// used by gids_initialize to set the admin key
static int gids_set_administrator_key(sc_card_t *card, u8* key) {
int r;
u8 adminKeyData[] = {0x84,0x01,0x80, // key reference
0xA5,0x1F, // key template
// key value
0x87,0x18,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x01,
0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x01,0x02,
0x03,0x04,0x05,0x06,0x07,0x08,
// key file
0x88, 0x03,0xB0,0x73,0xDC};
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
memcpy(adminKeyData+7, key, 24);
r = gids_put_DO(card, GIDS_APPLET_EFID, GIDS_PUT_KEY_DO, adminKeyData, sizeof(adminKeyData));
sc_mem_clear(adminKeyData, sizeof(adminKeyData));
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to set the admin key");
return SC_SUCCESS;
}
static int gids_check_that_card_is_new(sc_card_t *card) {
int r;
// retrieve the masterfile
// if it succeed, the card has already been initialized
r = gids_read_masterfile(card);
if (r == SC_SUCCESS) {
r = SC_ERROR_INVALID_CARD;
LOG_TEST_RET(card->ctx, r, "unable to read the masterfile");
}
return SC_SUCCESS;
}
// initialize a card
// see the minidriver specification annex for the details about this
static int gids_initialize(sc_card_t *card, sc_cardctl_gids_init_param_t* param) {
sc_apdu_t apdu;
int r;
#ifdef ENABLE_OPENSSL
int i;
#endif
// hardcoded file setting
// File type=39=TLV structure for BER-TLV DOs then ACL varies depending on the file)
// this DO EF are used like DF file so the permission has to be set only once
u8 UserCreateDeleteDirAc[] = {0x62,0x0C,0x82,0x01,0x39,0x83,0x02,0xA0,0x00,0x8C,0x03,0x03,0x30,0x00};
u8 EveryoneReadUserWriteAc[] = {0x62,0x0C,0x82,0x01,0x39,0x83,0x02,0xA0,0x10,0x8C,0x03,0x03,0x30,0x00};
u8 UserWriteExecuteAc[] = {0x62,0x0C,0x82,0x01,0x39,0x83,0x02,0xA0,0x11,0x8C,0x03,0x03,0x30,0xFF};
u8 EveryoneReadAdminWriteAc[] = {0x62,0x0C,0x82,0x01,0x39,0x83,0x02,0xA0,0x12,0x8C,0x03,0x03,0x20,0x00};
u8 UserReadWriteAc[] = {0x62,0x0C,0x82,0x01,0x39,0x83,0x02,0xA0,0x13,0x8C,0x03,0x03,0x30,0x30};
u8 AdminReadWriteAc[] = {0x62,0x0C,0x82,0x01,0x39,0x83,0x02,0xA0,0x14,0x8C,0x03,0x03,0x20,0x20};
// File type=18=key file ; type = symmetric key
u8 AdminKey[] = {0x62,0x1A,0x82,0x01,0x18,0x83,0x02,0xB0,0x80,0x8C,0x04,0x87,0x00,0x20,0xFF,0xA5,
0x0B,0xA4,0x09,0x80,0x01,0x02,0x83,0x01,0x80,0x95,0x01,0xC0};
// file used to store other file references. Format undocumented.
u8 masterfile[] = {0x01,0x6d,0x73,0x63,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x63,0x61,0x72,0x64,0x69,0x64,0x00,0x00,0x00,0x00,0x00,0x20,0xdf,
0x00,0x00,0x12,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x61,
0x72,0x64,0x61,0x70,0x70,0x73,0x00,0x00,0x00,0x21,0xdf,0x00,0x00,0x10,0xa0,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x61,0x72,0x64,0x63,0x66,0x00,0x00,
0x00,0x00,0x00,0x22,0xdf,0x00,0x00,0x10,0xa0,0x00,0x00,0x6d,0x73,0x63,0x70,0x00,0x00,
0x00,0x00,0x00,0x63,0x6d,0x61,0x70,0x66,0x69,0x6c,0x65,0x00,0x00,0x00,0x23,0xdf,0x00,
0x00,0x10,0xa0,0x00,0x00};
// list the application on the card - defined in the minidriver specification
u8 cardapps[] = {0x6d,0x73,0x63,0x70,0x00,0x00,0x00,0x00};
// used to detect if modifications have been done outside of the minidriver - defined in the minidriver specification
u8 cardcf[] = {0x00,0x00,0x00,0x00,0x00,0x00};
struct sc_pin_cmd_data pindata;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
// avoid multiple initialization
r = gids_check_that_card_is_new(card);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "card seems to have been already initialized");
memset(&pindata, 0, sizeof(pindata));
// create PIN & PUK
pindata.cmd = SC_PIN_CMD_CHANGE;
pindata.pin_type = SC_AC_CHV;
pindata.pin2.len = param->user_pin_len;
pindata.pin2.data = param->user_pin;
pindata.pin_reference = 0x80;
r = sc_pin_cmd(card, &pindata, NULL);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids set pin");
// create file
r = gids_initialize_create_file(card, UserCreateDeleteDirAc, sizeof(UserCreateDeleteDirAc));
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids to create the file UserCreateDeleteDirAc");
r = gids_initialize_create_file(card, EveryoneReadUserWriteAc, sizeof(EveryoneReadUserWriteAc));
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids to create the file EveryoneReadUserWriteAc");
r = gids_initialize_create_file(card, UserWriteExecuteAc, sizeof(UserWriteExecuteAc));
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids to create the file UserWriteExecuteAc");
r = gids_initialize_create_file(card, EveryoneReadAdminWriteAc, sizeof(EveryoneReadAdminWriteAc));
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids to create the file EveryoneReadAdminWriteAc");
r = gids_initialize_create_file(card, UserReadWriteAc, sizeof(UserReadWriteAc));
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids to create the file UserReadWriteAc");
r = gids_initialize_create_file(card, AdminReadWriteAc, sizeof(AdminReadWriteAc));
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids to create the file AdminReadWriteAc");
//admin key
r = gids_initialize_create_file(card, AdminKey, sizeof(AdminKey));
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids to create the file AdminKey");
r = gids_set_administrator_key(card, param->init_code);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to set the admin key");
// create the filesystem
r = gids_put_DO(card, MF_FI, MF_DO, masterfile, sizeof(masterfile));
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to save the masterfile");
r = gids_put_DO(card, CARDAPPS_FI, CARDAPPS_DO, cardapps, sizeof(cardapps));
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to save the cardapps");
r = gids_put_DO(card, CARDCF_FI, CARDCF_DO, cardcf, sizeof(cardcf));
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to save the cardcf");
r = gids_put_DO(card, CMAP_FI, CMAP_DO, NULL, 0);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to save the cmapfile");
#ifdef ENABLE_OPENSSL
for (i = sizeof(param->cardid) -1; i >= 0; i--) {
if (param->cardid[i]) break;
}
if (i < 0) {
// set a random cardid if not set
r = RAND_bytes(param->cardid, sizeof(param->cardid));
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to set a random serial number");
}
#endif
r = gids_put_DO(card, CARDID_FI, CARDID_DO, param->cardid, sizeof(param->cardid));
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to save the cardid");
//select applet
sc_format_apdu(card, &apdu, SC_APDU_CASE_3, INS_SELECT, 0x00, 0x0C);
apdu.lc = 2;
apdu.data = (const unsigned char *) "\x3F\xFF";
apdu.datalen = 2;
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return");
// activate file
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, INS_ACTIVATE_FILE, 0x00, 0x00);
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return");
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
}
// execute an admin authentication based on a secret key
// this is a 3DES authentication with a secret key
// the card mechanism is described in the GIDS specification and the computer side on the minidriver specification
// the minidriver specification is incorrect because it is not ECB but CBC
// then the GIDS specification is incorrect because the z1 key should be 8 bytes instead of 7
// this data comes from the reverse of the GIDS minidriver.
static int gids_authenticate_admin(sc_card_t *card, u8* key) {
#ifndef ENABLE_OPENSSL
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED);
#else
Use OpenSSL versions OpenSSL-0.9.7 to 1.1.0a for OpenSC OpenSSL-1.1.0 was released 8/25/2016 OpenSSL-1.1.0a was released 9/22/2016 https://www.openssl.org/news/openssl-1.1.0-notes.html Changes to allow the OpenSC code base to work with OpenSSL versions from 0.9.7 to 1.1.0 with few changes. This is an update and rebased version of my prep-openssl-1.1.0-pre6 branch. No attempt was made to back port any OpenSSL features. These changes just allow an updated OpenSC code base to use what is in the various OpenSSL releases. A new header libopensc/sc-ossl-compat.h contains extra defines to reduce the need for so many #if OPENSSL_VERSION_NUMBER statements in the source code. The OpenSC source can now use the OpenSSL 1.1 API. The libopensc/sc-ossl-compat.h has defines for the new API for use with older versions of OpenSSL. sc-ossl-compat.h is included by libopensc/internal.h so all OpenSC library routines can take advantage of it. For the tools, which do not use libopensc/internal.h, libopensc/sc-ossl-compat.h is included by the tools. The OpenSC source has been modified to use OpenSSL functions to access hidden structures, such X509, BIGNUM, EVP_CIPHER_CTX, and use XXX_new functions to allocate structures which must use pointer such as BIGNUM and EVP_CIPHER_CTX. For backward compatability sc-ossl-compat.h now defines inline routines to emulate the RSA and DSA access routines in OpenSSL-1.1.0. Thus the same OpenSC source code can be used with openSSL versions from 0.9.7 to 1.1.0. Inline routines were chosen, because using macros does not work on all platforms. Having OpenSC versions of these routines in libopensc would be a posibility, but they are only used for older version of OpenSSL, and could be removed in the future. Changes to be committed: modified: src/libopensc/card-entersafe.c modified: src/libopensc/card-epass2003.c modified: src/libopensc/card-gids.c modified: src/libopensc/card-gpk.c modified: src/libopensc/card-oberthur.c modified: src/libopensc/card-piv.c modified: src/libopensc/card-westcos.c modified: src/libopensc/cwa-dnie.c modified: src/libopensc/cwa14890.c modified: src/libopensc/internal.h modified: src/libopensc/p15card-helper.c modified: src/libopensc/pkcs15-itacns.c modified: src/libopensc/pkcs15-prkey.c modified: src/libopensc/pkcs15-pubkey.c new file: src/libopensc/sc-ossl-compat.h modified: src/pkcs11/openssl.c modified: src/pkcs15init/pkcs15-lib.c modified: src/pkcs15init/pkcs15-oberthur-awp.c modified: src/pkcs15init/pkcs15-oberthur.c modified: src/pkcs15init/pkcs15-oberthur.h modified: src/pkcs15init/pkcs15-westcos.c modified: src/tools/cryptoflex-tool.c modified: src/tools/gids-tool.c modified: src/tools/netkey-tool.c modified: src/tools/piv-tool.c modified: src/tools/pkcs11-tool.c modified: src/tools/pkcs15-init.c modified: src/tools/sc-hsm-tool.c modified: src/tools/westcos-tool.c
2016-01-06 14:40:59 +00:00
EVP_CIPHER_CTX *ctx = NULL;
int r;
u8 apduSetRandom[20] = {0x7C,0x12,0x81,0x10,0};
u8* randomR1 = apduSetRandom + 4;
u8 apduSetRandomResponse[256];
u8* randomR2 = apduSetRandomResponse+4;
u8 apduSendReponse[40 + 4] = {0x7C,0x2A,0x82,0x28};
u8 z1[8];
u8 buffer[16+16+8];
u8* buffer2 = apduSendReponse + 4;
int buffer2size = 40;
u8 apduSendResponseResponse[256];
2017-09-15 19:22:26 +00:00
u8 buffer3[16+16+8];
int buffer3size = 40;
sc_apdu_t apdu;
const EVP_CIPHER *cipher;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
// this is CBC instead of ECB
cipher = EVP_des_ede3_cbc();
if (!cipher) {
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL);
}
// select the admin key
sc_format_apdu(card, &apdu, SC_APDU_CASE_3, INS_MANAGE_SECURITY_ENVIRONMENT, 0xC1, 0xA4);
apdu.lc = 3;
apdu.data = (const unsigned char *) "\x83\x01\x80";
apdu.datalen = 3;
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return");
// generate a challenge
r = RAND_bytes(randomR1, 16);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to set computer random");
// send it to the card
memcpy(apduSetRandom+4, randomR1, 16);
sc_format_apdu(card, &apdu, SC_APDU_CASE_4, INS_GENERAL_AUTHENTICATE, 0x00, 0x00);
apdu.lc = sizeof(apduSetRandom);
apdu.data = apduSetRandom;
apdu.datalen = sizeof(apduSetRandom);
apdu.resp = apduSetRandomResponse;
apdu.resplen = sizeof(apduSetRandomResponse);
apdu.le = 256;
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return");
// compute the half size of the mutual authentication secret
r = RAND_bytes(z1, 7);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to set computer random");
// set the padding
z1[7] = 0x80;
// Encrypt R2||R1||Z1
memcpy(buffer, randomR2, 16);
memcpy(buffer+16, randomR1, 16);
memcpy(buffer+32, z1, sizeof(z1));
Use OpenSSL versions OpenSSL-0.9.7 to 1.1.0a for OpenSC OpenSSL-1.1.0 was released 8/25/2016 OpenSSL-1.1.0a was released 9/22/2016 https://www.openssl.org/news/openssl-1.1.0-notes.html Changes to allow the OpenSC code base to work with OpenSSL versions from 0.9.7 to 1.1.0 with few changes. This is an update and rebased version of my prep-openssl-1.1.0-pre6 branch. No attempt was made to back port any OpenSSL features. These changes just allow an updated OpenSC code base to use what is in the various OpenSSL releases. A new header libopensc/sc-ossl-compat.h contains extra defines to reduce the need for so many #if OPENSSL_VERSION_NUMBER statements in the source code. The OpenSC source can now use the OpenSSL 1.1 API. The libopensc/sc-ossl-compat.h has defines for the new API for use with older versions of OpenSSL. sc-ossl-compat.h is included by libopensc/internal.h so all OpenSC library routines can take advantage of it. For the tools, which do not use libopensc/internal.h, libopensc/sc-ossl-compat.h is included by the tools. The OpenSC source has been modified to use OpenSSL functions to access hidden structures, such X509, BIGNUM, EVP_CIPHER_CTX, and use XXX_new functions to allocate structures which must use pointer such as BIGNUM and EVP_CIPHER_CTX. For backward compatability sc-ossl-compat.h now defines inline routines to emulate the RSA and DSA access routines in OpenSSL-1.1.0. Thus the same OpenSC source code can be used with openSSL versions from 0.9.7 to 1.1.0. Inline routines were chosen, because using macros does not work on all platforms. Having OpenSC versions of these routines in libopensc would be a posibility, but they are only used for older version of OpenSSL, and could be removed in the future. Changes to be committed: modified: src/libopensc/card-entersafe.c modified: src/libopensc/card-epass2003.c modified: src/libopensc/card-gids.c modified: src/libopensc/card-gpk.c modified: src/libopensc/card-oberthur.c modified: src/libopensc/card-piv.c modified: src/libopensc/card-westcos.c modified: src/libopensc/cwa-dnie.c modified: src/libopensc/cwa14890.c modified: src/libopensc/internal.h modified: src/libopensc/p15card-helper.c modified: src/libopensc/pkcs15-itacns.c modified: src/libopensc/pkcs15-prkey.c modified: src/libopensc/pkcs15-pubkey.c new file: src/libopensc/sc-ossl-compat.h modified: src/pkcs11/openssl.c modified: src/pkcs15init/pkcs15-lib.c modified: src/pkcs15init/pkcs15-oberthur-awp.c modified: src/pkcs15init/pkcs15-oberthur.c modified: src/pkcs15init/pkcs15-oberthur.h modified: src/pkcs15init/pkcs15-westcos.c modified: src/tools/cryptoflex-tool.c modified: src/tools/gids-tool.c modified: src/tools/netkey-tool.c modified: src/tools/piv-tool.c modified: src/tools/pkcs11-tool.c modified: src/tools/pkcs15-init.c modified: src/tools/sc-hsm-tool.c modified: src/tools/westcos-tool.c
2016-01-06 14:40:59 +00:00
// init crypto
ctx = EVP_CIPHER_CTX_new();
if (ctx == NULL) {
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL);
}
if (!EVP_EncryptInit(ctx, cipher, key, NULL)) {
EVP_CIPHER_CTX_free(ctx);
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL);
}
Use OpenSSL versions OpenSSL-0.9.7 to 1.1.0a for OpenSC OpenSSL-1.1.0 was released 8/25/2016 OpenSSL-1.1.0a was released 9/22/2016 https://www.openssl.org/news/openssl-1.1.0-notes.html Changes to allow the OpenSC code base to work with OpenSSL versions from 0.9.7 to 1.1.0 with few changes. This is an update and rebased version of my prep-openssl-1.1.0-pre6 branch. No attempt was made to back port any OpenSSL features. These changes just allow an updated OpenSC code base to use what is in the various OpenSSL releases. A new header libopensc/sc-ossl-compat.h contains extra defines to reduce the need for so many #if OPENSSL_VERSION_NUMBER statements in the source code. The OpenSC source can now use the OpenSSL 1.1 API. The libopensc/sc-ossl-compat.h has defines for the new API for use with older versions of OpenSSL. sc-ossl-compat.h is included by libopensc/internal.h so all OpenSC library routines can take advantage of it. For the tools, which do not use libopensc/internal.h, libopensc/sc-ossl-compat.h is included by the tools. The OpenSC source has been modified to use OpenSSL functions to access hidden structures, such X509, BIGNUM, EVP_CIPHER_CTX, and use XXX_new functions to allocate structures which must use pointer such as BIGNUM and EVP_CIPHER_CTX. For backward compatability sc-ossl-compat.h now defines inline routines to emulate the RSA and DSA access routines in OpenSSL-1.1.0. Thus the same OpenSC source code can be used with openSSL versions from 0.9.7 to 1.1.0. Inline routines were chosen, because using macros does not work on all platforms. Having OpenSC versions of these routines in libopensc would be a posibility, but they are only used for older version of OpenSSL, and could be removed in the future. Changes to be committed: modified: src/libopensc/card-entersafe.c modified: src/libopensc/card-epass2003.c modified: src/libopensc/card-gids.c modified: src/libopensc/card-gpk.c modified: src/libopensc/card-oberthur.c modified: src/libopensc/card-piv.c modified: src/libopensc/card-westcos.c modified: src/libopensc/cwa-dnie.c modified: src/libopensc/cwa14890.c modified: src/libopensc/internal.h modified: src/libopensc/p15card-helper.c modified: src/libopensc/pkcs15-itacns.c modified: src/libopensc/pkcs15-prkey.c modified: src/libopensc/pkcs15-pubkey.c new file: src/libopensc/sc-ossl-compat.h modified: src/pkcs11/openssl.c modified: src/pkcs15init/pkcs15-lib.c modified: src/pkcs15init/pkcs15-oberthur-awp.c modified: src/pkcs15init/pkcs15-oberthur.c modified: src/pkcs15init/pkcs15-oberthur.h modified: src/pkcs15init/pkcs15-westcos.c modified: src/tools/cryptoflex-tool.c modified: src/tools/gids-tool.c modified: src/tools/netkey-tool.c modified: src/tools/piv-tool.c modified: src/tools/pkcs11-tool.c modified: src/tools/pkcs15-init.c modified: src/tools/sc-hsm-tool.c modified: src/tools/westcos-tool.c
2016-01-06 14:40:59 +00:00
EVP_CIPHER_CTX_set_padding(ctx,0);
if (!EVP_EncryptUpdate(ctx, buffer2, &buffer2size, buffer, sizeof(buffer))) {
EVP_CIPHER_CTX_free(ctx);
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL);
}
Use OpenSSL versions OpenSSL-0.9.7 to 1.1.0a for OpenSC OpenSSL-1.1.0 was released 8/25/2016 OpenSSL-1.1.0a was released 9/22/2016 https://www.openssl.org/news/openssl-1.1.0-notes.html Changes to allow the OpenSC code base to work with OpenSSL versions from 0.9.7 to 1.1.0 with few changes. This is an update and rebased version of my prep-openssl-1.1.0-pre6 branch. No attempt was made to back port any OpenSSL features. These changes just allow an updated OpenSC code base to use what is in the various OpenSSL releases. A new header libopensc/sc-ossl-compat.h contains extra defines to reduce the need for so many #if OPENSSL_VERSION_NUMBER statements in the source code. The OpenSC source can now use the OpenSSL 1.1 API. The libopensc/sc-ossl-compat.h has defines for the new API for use with older versions of OpenSSL. sc-ossl-compat.h is included by libopensc/internal.h so all OpenSC library routines can take advantage of it. For the tools, which do not use libopensc/internal.h, libopensc/sc-ossl-compat.h is included by the tools. The OpenSC source has been modified to use OpenSSL functions to access hidden structures, such X509, BIGNUM, EVP_CIPHER_CTX, and use XXX_new functions to allocate structures which must use pointer such as BIGNUM and EVP_CIPHER_CTX. For backward compatability sc-ossl-compat.h now defines inline routines to emulate the RSA and DSA access routines in OpenSSL-1.1.0. Thus the same OpenSC source code can be used with openSSL versions from 0.9.7 to 1.1.0. Inline routines were chosen, because using macros does not work on all platforms. Having OpenSC versions of these routines in libopensc would be a posibility, but they are only used for older version of OpenSSL, and could be removed in the future. Changes to be committed: modified: src/libopensc/card-entersafe.c modified: src/libopensc/card-epass2003.c modified: src/libopensc/card-gids.c modified: src/libopensc/card-gpk.c modified: src/libopensc/card-oberthur.c modified: src/libopensc/card-piv.c modified: src/libopensc/card-westcos.c modified: src/libopensc/cwa-dnie.c modified: src/libopensc/cwa14890.c modified: src/libopensc/internal.h modified: src/libopensc/p15card-helper.c modified: src/libopensc/pkcs15-itacns.c modified: src/libopensc/pkcs15-prkey.c modified: src/libopensc/pkcs15-pubkey.c new file: src/libopensc/sc-ossl-compat.h modified: src/pkcs11/openssl.c modified: src/pkcs15init/pkcs15-lib.c modified: src/pkcs15init/pkcs15-oberthur-awp.c modified: src/pkcs15init/pkcs15-oberthur.c modified: src/pkcs15init/pkcs15-oberthur.h modified: src/pkcs15init/pkcs15-westcos.c modified: src/tools/cryptoflex-tool.c modified: src/tools/gids-tool.c modified: src/tools/netkey-tool.c modified: src/tools/piv-tool.c modified: src/tools/pkcs11-tool.c modified: src/tools/pkcs15-init.c modified: src/tools/sc-hsm-tool.c modified: src/tools/westcos-tool.c
2016-01-06 14:40:59 +00:00
if(!EVP_EncryptFinal(ctx, buffer2+buffer2size, &buffer2size)) {
EVP_CIPHER_CTX_free(ctx);
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL);
}
Use OpenSSL versions OpenSSL-0.9.7 to 1.1.0a for OpenSC OpenSSL-1.1.0 was released 8/25/2016 OpenSSL-1.1.0a was released 9/22/2016 https://www.openssl.org/news/openssl-1.1.0-notes.html Changes to allow the OpenSC code base to work with OpenSSL versions from 0.9.7 to 1.1.0 with few changes. This is an update and rebased version of my prep-openssl-1.1.0-pre6 branch. No attempt was made to back port any OpenSSL features. These changes just allow an updated OpenSC code base to use what is in the various OpenSSL releases. A new header libopensc/sc-ossl-compat.h contains extra defines to reduce the need for so many #if OPENSSL_VERSION_NUMBER statements in the source code. The OpenSC source can now use the OpenSSL 1.1 API. The libopensc/sc-ossl-compat.h has defines for the new API for use with older versions of OpenSSL. sc-ossl-compat.h is included by libopensc/internal.h so all OpenSC library routines can take advantage of it. For the tools, which do not use libopensc/internal.h, libopensc/sc-ossl-compat.h is included by the tools. The OpenSC source has been modified to use OpenSSL functions to access hidden structures, such X509, BIGNUM, EVP_CIPHER_CTX, and use XXX_new functions to allocate structures which must use pointer such as BIGNUM and EVP_CIPHER_CTX. For backward compatability sc-ossl-compat.h now defines inline routines to emulate the RSA and DSA access routines in OpenSSL-1.1.0. Thus the same OpenSC source code can be used with openSSL versions from 0.9.7 to 1.1.0. Inline routines were chosen, because using macros does not work on all platforms. Having OpenSC versions of these routines in libopensc would be a posibility, but they are only used for older version of OpenSSL, and could be removed in the future. Changes to be committed: modified: src/libopensc/card-entersafe.c modified: src/libopensc/card-epass2003.c modified: src/libopensc/card-gids.c modified: src/libopensc/card-gpk.c modified: src/libopensc/card-oberthur.c modified: src/libopensc/card-piv.c modified: src/libopensc/card-westcos.c modified: src/libopensc/cwa-dnie.c modified: src/libopensc/cwa14890.c modified: src/libopensc/internal.h modified: src/libopensc/p15card-helper.c modified: src/libopensc/pkcs15-itacns.c modified: src/libopensc/pkcs15-prkey.c modified: src/libopensc/pkcs15-pubkey.c new file: src/libopensc/sc-ossl-compat.h modified: src/pkcs11/openssl.c modified: src/pkcs15init/pkcs15-lib.c modified: src/pkcs15init/pkcs15-oberthur-awp.c modified: src/pkcs15init/pkcs15-oberthur.c modified: src/pkcs15init/pkcs15-oberthur.h modified: src/pkcs15init/pkcs15-westcos.c modified: src/tools/cryptoflex-tool.c modified: src/tools/gids-tool.c modified: src/tools/netkey-tool.c modified: src/tools/piv-tool.c modified: src/tools/pkcs11-tool.c modified: src/tools/pkcs15-init.c modified: src/tools/sc-hsm-tool.c modified: src/tools/westcos-tool.c
2016-01-06 14:40:59 +00:00
EVP_CIPHER_CTX_free(ctx);
ctx = NULL;
// send it to the card
sc_format_apdu(card, &apdu, SC_APDU_CASE_4, INS_GENERAL_AUTHENTICATE, 0x00, 0x00);
apdu.lc = sizeof(apduSendReponse);
apdu.data = apduSendReponse;
apdu.datalen = sizeof(apduSendReponse);
apdu.resp = apduSendResponseResponse;
apdu.resplen = sizeof(apduSendResponseResponse);
apdu.le = 256;
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return");
2017-09-15 19:22:26 +00:00
if (apdu.resplen != 44)
{
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Expecting a response len of 44 - found %d",(int) apdu.resplen);
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL);
}
// init crypto
ctx = EVP_CIPHER_CTX_new();
if (ctx == NULL) {
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL);
}
if (!EVP_DecryptInit(ctx, cipher, key, NULL)) {
EVP_CIPHER_CTX_free(ctx);
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL);
}
EVP_CIPHER_CTX_set_padding(ctx,0);
if (!EVP_DecryptUpdate(ctx, buffer3, &buffer3size, apdu.resp + 4, apdu.resplen - 4)) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unable to decrypt data");
EVP_CIPHER_CTX_free(ctx);
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_PIN_CODE_INCORRECT);
}
if(!EVP_DecryptFinal(ctx, buffer3+buffer3size, &buffer3size)) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unable to decrypt final data");
EVP_CIPHER_CTX_free(ctx);
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_PIN_CODE_INCORRECT);
}
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "data has been decrypted using the key");
if (memcmp(buffer3, randomR1, 16) != 0) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "R1 doesn't match");
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_PIN_CODE_INCORRECT);
}
if (memcmp(buffer3 + 16, randomR2, 16) != 0) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "R2 doesn't match");
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_PIN_CODE_INCORRECT);
}
if (buffer[39] != 0x80) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Padding not found");
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_PIN_CODE_INCORRECT);
}
EVP_CIPHER_CTX_free(ctx);
ctx = NULL;
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS);
#endif
}
static int gids_card_ctl(sc_card_t * card, unsigned long cmd, void *ptr)
{
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL);
switch (cmd) {
case SC_CARDCTL_GET_SERIALNR:
return gids_get_serialnr(card, (sc_serial_number_t *) ptr);
case SC_CARDCTL_GIDS_GET_ALL_CONTAINERS:
return gids_get_all_containers(card, (size_t*) ptr);
case SC_CARDCTL_GIDS_GET_CONTAINER_DETAIL:
return gids_get_container_detail(card, (sc_cardctl_gids_get_container_t*) ptr);
case SC_CARDCTL_GIDS_SELECT_KEY_REFERENCE:
return gids_select_key_reference(card, (sc_pkcs15_prkey_info_t*) ptr);
case SC_CARDCTL_GIDS_CREATE_KEY:
return gids_create_keyfile(card, (sc_pkcs15_object_t*) ptr);
case SC_CARDCTL_GIDS_GENERATE_KEY:
return gids_generate_key(card, ((struct sc_cardctl_gids_genkey*) ptr)->object, ((struct sc_cardctl_gids_genkey*) ptr)->pubkey);
case SC_CARDCTL_GIDS_IMPORT_KEY:
return gids_import_key(card, ((struct sc_cardctl_gids_importkey*) ptr)->object, ((struct sc_cardctl_gids_importkey*) ptr)->key);
case SC_CARDCTL_GIDS_SAVE_CERT:
return gids_save_certificate(card, ((struct sc_cardctl_gids_save_cert*) ptr)->certobject,
((struct sc_cardctl_gids_save_cert*) ptr)->privkeyobject, ((struct sc_cardctl_gids_save_cert*) ptr)->path);
case SC_CARDCTL_GIDS_DELETE_CERT:
return gids_delete_cert(card, (sc_pkcs15_object_t*) ptr);
case SC_CARDCTL_GIDS_DELETE_KEY:
return gids_delete_key(card, (sc_pkcs15_object_t*) ptr);
case SC_CARDCTL_GIDS_INITIALIZE:
return gids_initialize(card, (sc_cardctl_gids_init_param_t*) ptr);
case SC_CARDCTL_GIDS_SET_ADMIN_KEY:
return gids_set_administrator_key(card, (u8*) ptr);
case SC_CARDCTL_GIDS_AUTHENTICATE_ADMIN:
return gids_authenticate_admin(card, (u8*) ptr);
default:
return SC_ERROR_NOT_SUPPORTED;
}
}
static int gids_card_reader_lock_obtained(sc_card_t *card, int was_reset)
{
int r = SC_SUCCESS;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
if (was_reset > 0) {
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
size_t resplen = sizeof(rbuf);
r = gids_select_aid(card, gids_aid.value, gids_aid.len, rbuf, &resplen);
}
LOG_FUNC_RETURN(card->ctx, r);
}
static struct sc_card_driver *sc_get_driver(void)
{
if (iso_ops == NULL)
iso_ops = sc_get_iso7816_driver()->ops;
gids_ops.match_card = gids_match_card;
gids_ops.init = gids_init;
gids_ops.finish = gids_finish;
gids_ops.read_binary = gids_read_binary;
gids_ops.write_binary = NULL;
gids_ops.update_binary = NULL;
gids_ops.erase_binary = NULL;
gids_ops.read_record = NULL;
gids_ops.write_record = NULL;
gids_ops.append_record = NULL;
gids_ops.update_record = NULL;
gids_ops.select_file = gids_select_file;
gids_ops.get_response = iso_ops->get_response;
gids_ops.get_challenge = NULL;
gids_ops.verify = NULL; // see pin_cmd
gids_ops.logout = gids_logout;
gids_ops.restore_security_env = NULL;
gids_ops.set_security_env = gids_set_security_env;
gids_ops.decipher = iso_ops->decipher;
gids_ops.compute_signature = iso_ops->compute_signature;
gids_ops.change_reference_data = NULL; // see pin_cmd
gids_ops.reset_retry_counter = NULL; // see pin_cmd
gids_ops.create_file = iso_ops->create_file;
gids_ops.delete_file = NULL;
gids_ops.list_files = NULL;
gids_ops.check_sw = iso_ops->check_sw;
gids_ops.card_ctl = gids_card_ctl;
gids_ops.process_fci = iso_ops->process_fci;
gids_ops.construct_fci = iso_ops->construct_fci;
gids_ops.pin_cmd = gids_pin_cmd;
gids_ops.get_data = NULL;
gids_ops.put_data = NULL;
gids_ops.delete_record = NULL;
gids_ops.read_public_key = gids_read_public_key;
gids_ops.card_reader_lock_obtained = gids_card_reader_lock_obtained;
return &gids_drv;
}
struct sc_card_driver *sc_get_gids_driver(void)
{
return sc_get_driver();
}
#else
struct sc_card_driver *sc_get_gids_driver(void)
{
return NULL;
}
#endif