opensc/src/libopensc/card-isoApplet.c

1287 lines
40 KiB
C

/*
* Support for the IsoApplet JavaCard Applet.
*
* Copyright (C) 2014 Philip Wendland <wendlandphilip@gmail.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
*/
#include <stdlib.h>
#include <string.h>
#include "asn1.h"
#include "cardctl.h"
#include "internal.h"
#include "log.h"
#include "opensc.h"
#include "pkcs15.h"
#include "types.h"
#define ISOAPPLET_ALG_REF_ECDSA 0x21
#define ISOAPPLET_ALG_REF_RSA_PAD_PKCS1 0x11
#define ISOAPPLET_API_VERSION_MAJOR 0x00
#define ISOAPPLET_API_VERSION_MINOR 0x06
#define ISOAPPLET_API_FEATURE_EXT_APDU 0x01
#define ISOAPPLET_API_FEATURE_SECURE_RANDOM 0x02
#define ISOAPPLET_API_FEATURE_ECC 0x04
#define ISOAPPLET_AID_LEN 12
static const u8 isoApplet_aid[] = {0xf2,0x76,0xa2,0x88,0xbc,0xfb,0xa6,0x9d,0x34,0xf3,0x10,0x01};
struct isoApplet_drv_data
{
/* Save the current algorithm reference
* (ISOAPPLET_ALG_REF_ECDSA, ISOAPPLET_ALG_REF_RSA_PAD_PKCS1)
* to be able to distinguish between RSA and ECC operations.
* If ECC is being used, the signatures generated by the card
* have to be modified. */
unsigned int sec_env_alg_ref;
unsigned int sec_env_ec_field_length;
unsigned int isoapplet_version;
};
#define DRVDATA(card) ((struct isoApplet_drv_data *) ((card)->drv_data))
/* Operations supported by the applet. */
static struct sc_card_operations isoApplet_ops;
/* A reference to the iso7816_* functions.
* Initialized in sc_get_driver. */
static const struct sc_card_operations *iso_ops = NULL;
/* The description of the driver. */
static struct sc_card_driver isoApplet_drv =
{
"Javacard with IsoApplet",
"isoApplet",
&isoApplet_ops,
NULL, 0, NULL
};
static struct isoapplet_supported_ec_curves {
struct sc_object_id oid;
size_t size;
unsigned int min_applet_version;
} ec_curves[] = {
{{{1, 2, 840, 10045, 3, 1, 1, -1}}, 192, 0x0000}, /* secp192r1, nistp192, prime192v1, ansiX9p192r1 */
{{{1, 3, 132, 0, 33, -1}}, 224, 0x0000}, /* secp224r1, nistp224 */
{{{1, 2, 840, 10045, 3, 1, 7, -1}}, 256, 0x0000}, /* secp256r1, nistp256, prime256v1, ansiX9p256r1 */
{{{1, 3, 132, 0, 34, -1}}, 384, 0x0000}, /* secp384r1, nistp384, prime384v1, ansiX9p384r1 */
{{{1, 3, 36, 3, 3, 2, 8, 1, 1, 3, -1}}, 192, 0x0000}, /* brainpoolP192r1 */
{{{1, 3, 36, 3, 3, 2, 8, 1, 1, 5, -1}}, 224, 0x0000}, /* brainpoolP224r1 */
{{{1, 3, 36, 3, 3, 2, 8, 1, 1, 7, -1}}, 256, 0x0000}, /* brainpoolP256r1 */
{{{1, 3, 36, 3, 3, 2, 8, 1, 1, 9, -1}}, 320, 0x0000}, /* brainpoolP320r1 */
{{{1, 3, 132, 0, 31, -1}}, 192, 0x0006}, /* secp192k1 */
{{{1, 3, 132, 0, 10, -1}}, 256, 0x0006}, /* secp256k1 */
{{{-1}}, 0, 0} /* This entry must not be touched. */
};
/*
* SELECT an applet on the smartcard. (Not in the emulated filesystem.)
* The response will be written to resp.
*
* @param[in] card
* @param[in] aid The applet ID.
* @param[in] aid_len The length of aid.
* @param[out] resp The response of the applet upon selection.
* @param[in,out] resp_len In: The buffer size of resp. Out: The length of the response.
*
* @return SC_SUCCESS: The applet is present and could be selected.
* any other: Transmit failure or the card returned an error.
* The card will return an error when the applet is
* not present.
*/
static int
isoApplet_select_applet(sc_card_t *card, const u8 *aid, const size_t aid_len, u8 *resp, size_t *resp_len)
{
int rv;
sc_context_t *ctx = card->ctx;
sc_apdu_t apdu;
LOG_FUNC_CALLED(card->ctx);
if(aid_len > SC_MAX_APDU_BUFFER_SIZE)
LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL);
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xa4, 0x04, 0x00);
apdu.lc = aid_len;
apdu.data = aid;
apdu.datalen = aid_len;
apdu.resp = resp;
apdu.resplen = *resp_len;
apdu.le = 0;
rv = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(ctx, rv, "APDU transmit failure.");
rv = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_TEST_RET(card->ctx, rv, "Card returned error");
*resp_len = apdu.resplen;
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
static int
isoApplet_finish(sc_card_t *card)
{
struct isoApplet_drv_data *drvdata=DRVDATA(card);
LOG_FUNC_CALLED(card->ctx);
if (drvdata)
{
free(drvdata);
card->drv_data=NULL;
}
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
static int
isoApplet_match_card(sc_card_t *card)
{
size_t rlen = SC_MAX_APDU_BUFFER_SIZE;
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
int rv;
rv = isoApplet_select_applet(card, isoApplet_aid, ISOAPPLET_AID_LEN, rbuf, &rlen);
if(rv != SC_SUCCESS)
{
return 0;
}
/* The IsoApplet should return an API version (major and minor) and a feature bitmap.
* We expect 3 bytes: MAJOR API version - MINOR API version - API feature bitmap.
* If applet does not return API version, versions 0x00 will match */
if(rlen < 3)
{
assert(sizeof(rbuf) >= 3);
memset(rbuf, 0x00, 3);
}
if(rbuf[0] != ISOAPPLET_API_VERSION_MAJOR)
{
sc_log(card->ctx, "IsoApplet: Mismatching major API version. Not proceeding. "
"API versions: Driver (%02X-%02X), applet (%02X-%02X). Please update accordingly.",
ISOAPPLET_API_VERSION_MAJOR, ISOAPPLET_API_VERSION_MINOR, rbuf[0], rbuf[1]);
return 0;
}
if(rbuf[1] != ISOAPPLET_API_VERSION_MINOR)
{
sc_log(card->ctx, "IsoApplet: Mismatching minor API version. Proceeding anyway. "
"API versions: Driver (%02X-%02X), applet (%02X-%02X). "
"Please update accordingly whenever possible.",
ISOAPPLET_API_VERSION_MAJOR, ISOAPPLET_API_VERSION_MINOR, rbuf[0], rbuf[1]);
}
return 1;
}
static int
isoApplet_init(sc_card_t *card)
{
int i;
unsigned long flags = 0;
unsigned long ext_flags = 0;
size_t rlen = SC_MAX_APDU_BUFFER_SIZE;
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
struct isoApplet_drv_data *drvdata;
LOG_FUNC_CALLED(card->ctx);
drvdata=calloc(1, sizeof(*drvdata));
if (!drvdata)
LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY);
card->drv_data = drvdata;
card->cla = 0x00;
/* Obtain applet version and specific features */
if (0 > isoApplet_select_applet(card, isoApplet_aid, ISOAPPLET_AID_LEN, rbuf, &rlen)) {
free(card->drv_data);
card->drv_data = NULL;
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_CARD, "Error obtaining applet version.");
}
if(rlen < 3)
{
assert(sizeof(rbuf) >= 3);
memset(rbuf, 0x00, 3);
}
drvdata->isoapplet_version = ((unsigned int)rbuf[0] << 8) | rbuf[1];
if(rbuf[2] & ISOAPPLET_API_FEATURE_EXT_APDU)
card->caps |= SC_CARD_CAP_APDU_EXT;
if(rbuf[2] & ISOAPPLET_API_FEATURE_SECURE_RANDOM)
card->caps |= SC_CARD_CAP_RNG;
if(drvdata->isoapplet_version <= 0x0005 || rbuf[2] & ISOAPPLET_API_FEATURE_ECC)
{
/* There are Java Cards that do not support ECDSA at all. The IsoApplet
* started to report this with version 00.06.
*
* Curves supported by the pkcs15-init driver are indicated per curve. This
* should be kept in sync with the explicit parameters in the pkcs15-init
* driver. */
flags = 0;
flags |= SC_ALGORITHM_ECDSA_RAW;
flags |= SC_ALGORITHM_ECDSA_HASH_SHA1;
flags |= SC_ALGORITHM_ONBOARD_KEY_GEN;
ext_flags = SC_ALGORITHM_EXT_EC_UNCOMPRESES;
ext_flags |= SC_ALGORITHM_EXT_EC_NAMEDCURVE;
ext_flags |= SC_ALGORITHM_EXT_EC_F_P;
for (i=0; ec_curves[i].oid.value[0] >= 0; i++)
{
if(drvdata->isoapplet_version >= ec_curves[i].min_applet_version)
_sc_card_add_ec_alg(card, ec_curves[i].size, flags, ext_flags, &ec_curves[i].oid);
}
}
/* RSA */
flags = 0;
/* Padding schemes: */
flags |= SC_ALGORITHM_RSA_PAD_PKCS1;
/* Hashes are to be done by the host for RSA */
flags |= SC_ALGORITHM_RSA_HASH_NONE;
/* Key-generation: */
flags |= SC_ALGORITHM_ONBOARD_KEY_GEN;
/* Modulus lengths: */
_sc_card_add_rsa_alg(card, 2048, flags, 0);
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
/*
* @brief convert an OpenSC ACL entry to the security condition
* byte used by the IsoApplet.
*
* Used by IsoApplet_create_file to parse OpenSC ACL entries
* into ISO 7816-4 Table 20 security condition bytes.
*
* @param entry The OpenSC ACL entry.
*
* @return The security condition byte. No restriction (0x00)
* if unknown operation.
*/
static u8
isoApplet_acl_to_security_condition_byte(const sc_acl_entry_t *entry)
{
if(!entry)
return 0x00;
switch(entry->method)
{
case SC_AC_CHV:
return 0x90;
case SC_AC_NEVER:
return 0xFF;
case SC_AC_NONE:
default:
return 0x00;
}
}
/*
* The reason for this function is that OpenSC doesn't set any
* Security Attribute Tag in the FCI upon file creation if there
* is no file->sec_attr. I set the file->sec_attr to a format
* understood by the applet (ISO 7816-4 tables 16, 17 and 20).
* The iso7816_create_file will then set this as Tag 86 - Sec.
* Attr. Prop. Format.
* The applet will then be able to set and enforce access rights
* for any file created by OpenSC. Without this function, the
* applet would not know where to enforce security rules and
* when.
*
* Note: IsoApplet currently only supports a "onepin" option.
*
* Format of the sec_attr: 8 Bytes:
* 7 - ISO 7816-4 table 16 or 17
* 6 to 0 - ISO 7816-4 table 20
*/
static int
isoApplet_create_file(sc_card_t *card, sc_file_t *file)
{
int r = 0;
LOG_FUNC_CALLED(card->ctx);
if(file->sec_attr_len == 0)
{
u8 access_buf[8];
int idx[8], i;
if(file->type == SC_FILE_TYPE_DF)
{
const int df_idx[8] = /* These are the SC operations. */
{
0, /* Reserved. */
SC_AC_OP_DELETE_SELF, /* b6 */
SC_AC_OP_LOCK, /* b5 */
SC_AC_OP_ACTIVATE, /* b4 */
SC_AC_OP_DEACTIVATE, /* b3 */
SC_AC_OP_CREATE_DF, /* b2 */
SC_AC_OP_CREATE_EF, /* b1 */
SC_AC_OP_DELETE /* b0 */
};
for(i=0; i<8; i++)
{
idx[i] = df_idx[i];
}
}
else /* EF */
{
const int ef_idx[8] =
{
0, /* Reserved. */
SC_AC_OP_DELETE_SELF, /* b6 */
SC_AC_OP_LOCK, /* b5 */
SC_AC_OP_ACTIVATE, /* b4 */
SC_AC_OP_DEACTIVATE, /* b3 */
SC_AC_OP_WRITE, /* b2 */
SC_AC_OP_UPDATE, /* b1 */
SC_AC_OP_READ /* b0 */
};
for(i=0; i<8; i++)
{
idx[i] = ef_idx[i];
}
}
/* Now idx contains the operation identifiers.
* We now search for the OPs. */
access_buf[0] = 0xFF; /* A security condition byte is present for every OP. (Table 19) */
for(i=1; i<8; i++)
{
const sc_acl_entry_t *entry;
entry = sc_file_get_acl_entry(file, idx[i]);
access_buf[i] = isoApplet_acl_to_security_condition_byte(entry);
}
r = sc_file_set_sec_attr(file, access_buf, 8);
LOG_TEST_RET(card->ctx, r, "Error adding security attribute.");
}
r = iso_ops->create_file(card, file);
LOG_FUNC_RETURN(card->ctx, r);
}
/*
* Add an ACL entry to the OpenSC file struct, according to the operation
* and the saByte (Encoded according to IsoApplet FCI proprietary security
* information, see also ISO 7816-4 table 20).
*
* @param[in,out] file
* @param[in] operation The OpenSC operation.
* @param[in] saByte The security condition byte returned by the applet.
*/
static int
isoApplet_add_sa_to_acl(sc_file_t *file, unsigned int operation, u8 saByte)
{
int r;
switch(saByte)
{
case 0x90:
r = sc_file_add_acl_entry(file, operation, SC_AC_CHV, 1);
if(r < 0)
return r;
break;
case 0xFF:
r = sc_file_add_acl_entry(file, operation, SC_AC_NEVER, SC_AC_KEY_REF_NONE);
if(r < 0)
return r;
break;
case 0x00:
r = sc_file_add_acl_entry(file, operation, SC_AC_NONE, SC_AC_KEY_REF_NONE);
if(r < 0)
return r;
break;
default:
r = sc_file_add_acl_entry(file, operation, SC_AC_UNKNOWN, SC_AC_KEY_REF_NONE);
if(r < 0)
return r;
}
return SC_SUCCESS;
}
/*
* This function first calls the iso7816.c process_fci() for any other FCI
* information and then updates the ACL of the OpenSC file struct according
* to the FCI from the applet.
*/
static int
isoApplet_process_fci(sc_card_t *card, sc_file_t *file,
const u8 *buf, size_t buflen)
{
int r;
u8 *sa = NULL;
LOG_FUNC_CALLED(card->ctx);
r = iso_ops->process_fci(card, file, buf, buflen);
LOG_TEST_RET(card->ctx, r, "Error while processing the FCI.");
/* Construct the ACL from the sec_attr. */
if(file->sec_attr && file->sec_attr_len == 8)
{
sa = file->sec_attr;
if(sa[0] != 0xFF)
{
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA,
"File security attribute does not contain a ACL byte for every operation.");
}
if(file->type == SC_FILE_TYPE_DF)
{
r = isoApplet_add_sa_to_acl(file, SC_AC_OP_DELETE_SELF, sa[1]);
LOG_TEST_RET(card->ctx, r, "Error adding ACL entry.");
r = isoApplet_add_sa_to_acl(file, SC_AC_OP_LOCK, sa[2]);
LOG_TEST_RET(card->ctx, r, "Error adding ACL entry.");
r = isoApplet_add_sa_to_acl(file, SC_AC_OP_ACTIVATE, sa[3]);
LOG_TEST_RET(card->ctx, r, "Error adding ACL entry.");
r = isoApplet_add_sa_to_acl(file, SC_AC_OP_DEACTIVATE, sa[4]);
LOG_TEST_RET(card->ctx, r, "Error adding ACL entry.");
r = isoApplet_add_sa_to_acl(file, SC_AC_OP_CREATE_DF, sa[5]);
LOG_TEST_RET(card->ctx, r, "Error adding ACL entry.");
r = isoApplet_add_sa_to_acl(file, SC_AC_OP_CREATE_EF, sa[6]);
LOG_TEST_RET(card->ctx, r, "Error adding ACL entry.");
r = isoApplet_add_sa_to_acl(file, SC_AC_OP_DELETE, sa[7]);
LOG_TEST_RET(card->ctx, r, "Error adding ACL entry.");
}
else if(file->type == SC_FILE_TYPE_INTERNAL_EF
|| file->type == SC_FILE_TYPE_WORKING_EF)
{
r = isoApplet_add_sa_to_acl(file, SC_AC_OP_DELETE_SELF, sa[1]);
LOG_TEST_RET(card->ctx, r, "Error adding ACL entry.");
r = isoApplet_add_sa_to_acl(file, SC_AC_OP_LOCK, sa[2]);
LOG_TEST_RET(card->ctx, r, "Error adding ACL entry.");
r = isoApplet_add_sa_to_acl(file, SC_AC_OP_ACTIVATE, sa[3]);
LOG_TEST_RET(card->ctx, r, "Error adding ACL entry.");
r = isoApplet_add_sa_to_acl(file, SC_AC_OP_DEACTIVATE, sa[4]);
LOG_TEST_RET(card->ctx, r, "Error adding ACL entry.");
r = isoApplet_add_sa_to_acl(file, SC_AC_OP_WRITE, sa[5]);
LOG_TEST_RET(card->ctx, r, "Error adding ACL entry.");
r = isoApplet_add_sa_to_acl(file, SC_AC_OP_UPDATE, sa[6]);
LOG_TEST_RET(card->ctx, r, "Error adding ACL entry.");
r = isoApplet_add_sa_to_acl(file, SC_AC_OP_READ, sa[7]);
LOG_TEST_RET(card->ctx, r, "Error adding ACL entry.");
}
}
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
/*
* @brief Encode the EC parameters as a concatenation of TLV entries.
*
* The format is:
* 81 - prime
* 82 - coefficient A
* 83 - coefficient B
* 84 - base point G
* 85 - order
* 87 - cofactor
*
* @param[in] card
* @param[in] params The ECparameters containing the information of the curve.
* @param[out] out The array the encoded parameters are written to.
* @param[in] out_len The size of out
* @param[out] ptr A pointer pointing to the end of the parameters in out
* (the first untouched byte behind the parameters).
*/
static int
isoApplet_put_ec_params(sc_card_t *card, sc_cardctl_isoApplet_ec_parameters_t *params, u8 *out, size_t out_len, u8 **ptr)
{
u8 *p = out;
int r;
LOG_FUNC_CALLED(card->ctx);
if(!params
|| !params->prime.value
|| !params->coefficientA.value
|| !params->coefficientB.value
|| !params->basePointG.value
|| !params->order.value
|| !params->coFactor.value)
{
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Error: EC params not present.");
}
if(out == NULL || out_len == 0)
{
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Error: Parameter out is NULL or outlen is zero.");
}
r = sc_asn1_put_tag(0x81, params->prime.value, params->prime.len, p, out_len - (p - out), &p);
LOG_TEST_RET(card->ctx, r, "Error in handling TLV.");
r = sc_asn1_put_tag(0x82, params->coefficientA.value, params->coefficientA.len, p, out_len - (p - out), &p);
LOG_TEST_RET(card->ctx, r, "Error in handling TLV.");
r = sc_asn1_put_tag(0x83, params->coefficientB.value, params->coefficientB.len, p, out_len - (p - out), &p);
LOG_TEST_RET(card->ctx, r, "Error in handling TLV.");
r = sc_asn1_put_tag(0x84, params->basePointG.value, params->basePointG.len, p, out_len - (p - out), &p);
LOG_TEST_RET(card->ctx, r, "Error in handling TLV.");
r = sc_asn1_put_tag(0x85, params->order.value, params->order.len, p, out_len - (p - out), &p);
LOG_TEST_RET(card->ctx, r, "Error in handling TLV.");
r = sc_asn1_put_tag(0x87, params->coFactor.value, params->coFactor.len, p, out_len - (p - out), &p);
LOG_TEST_RET(card->ctx, r, "Error in handling TLV.");
if (ptr != NULL)
*ptr = p;
LOG_FUNC_RETURN(card->ctx, r);
}
/*
* @brief Generate a private key on the card.
*/
static int
isoApplet_ctl_generate_key(sc_card_t *card, sc_cardctl_isoApplet_genkey_t *args)
{
int r;
sc_apdu_t apdu;
u8 rbuf[SC_MAX_EXT_APDU_BUFFER_SIZE];
u8 sbuf[SC_MAX_EXT_APDU_BUFFER_SIZE];
u8 *p;
const u8 *inner_tag_value;
const u8 *outer_tag_value;
unsigned int tag;
size_t outer_tag_len;
size_t inner_tag_len;
unsigned int cla;
LOG_FUNC_CALLED(card->ctx);
/* MANAGE SECURITY ENVIRONMENT (SET). Set the algorithm and key references. */
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0x00);
p = sbuf;
*p++ = 0x80; /* algorithm reference */
*p++ = 0x01;
*p++ = args->algorithm_ref;
*p++ = 0x84; /* Private key reference */
*p++ = 0x01;
*p++ = args->priv_key_ref;
r = p - sbuf;
p = NULL;
apdu.lc = r;
apdu.datalen = r;
apdu.data = sbuf;
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");
/* GENERATE ASYMMETRIC KEY PAIR
* We use a larger buffer here, even if the card does not support extended apdus.
* There are two cases:
* 1) The card can do ext. apdus: The data fits in one apdu.
* 2) The card can't do ext. apdus: sc_transmit_apdu will handle that - the
* card will send SW_BYTES_REMAINING, OpenSC will automatically do a
* GET RESPONSE to get the remaining data, and will append it to the data
* buffer. */
if(args->algorithm_ref == SC_ISOAPPLET_ALG_REF_EC_GEN)
{
sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x46, 0x00, 0x00);
apdu.data = sbuf;
p = sbuf;
r = isoApplet_put_ec_params(card, &args->pubkey.ec.params, p, sizeof(sbuf), &p);
LOG_TEST_RET(card->ctx, r, "Error composing EC params.");
apdu.datalen = p - sbuf;
apdu.lc = p - sbuf;
/* Use APDU chaining if the card does not support extended apdus
* and the data does not fit in one short apdu. */
if ((apdu.datalen > 255) && !(card->caps & SC_CARD_CAP_APDU_EXT))
{
apdu.flags |= SC_APDU_FLAGS_CHAINING;
}
}
else
{
sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0x46, 0x42, 0x00);
}
apdu.resp = rbuf;
apdu.resplen = sizeof(rbuf);
apdu.le = 256;
r = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
if(apdu.sw1 == 0x6A && apdu.sw2 == 0x81)
{
sc_log(card->ctx, "Key generation not supported by the card with that particular key type. "
"Your card may not support the specified algorithm used by the applet / specified by you. "
"In most cases, this happens when trying to generate EC keys not supported by your java card. "
"In this case, look for supported field lengths and whether FP and/or F2M are supported.");
}
LOG_TEST_RET(card->ctx, r, "Card returned error");
/* Parse the public key / response. */
outer_tag_value = apdu.resp;
r = sc_asn1_read_tag(&outer_tag_value, apdu.resplen, &cla, &tag, &outer_tag_len);
LOG_TEST_RET(card->ctx, r, "Error in ASN1 handling.");
/* Interindustry template for nesting one set of public key data objects */
if((tag != 0x1F49) || (cla != 0x60))
{
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA,
"The data returned by the card is unexpected.");
}
switch(args->algorithm_ref)
{
case SC_ISOAPPLET_ALG_REF_RSA_GEN_2048:
/* Search for the modulus tag (81). */
inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x81, &inner_tag_len);
if(inner_tag_value == NULL || inner_tag_len != 256)
{
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid modulus.");
}
if(inner_tag_len > args->pubkey.rsa.modulus.len)
{
LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL);
}
memcpy(args->pubkey.rsa.modulus.value, inner_tag_value, inner_tag_len);
args->pubkey.rsa.modulus.len = inner_tag_len;
/* Exponent tag (82) */
inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x82, &inner_tag_len);
if(inner_tag_value == NULL || inner_tag_len != 3)
{
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid exponent.");
}
if(inner_tag_len > args->pubkey.rsa.exponent.len)
{
LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL);
}
if(memcmp(inner_tag_value, "\x01\x00\x01", 3) != 0)
{
LOG_TEST_RET(card->ctx, SC_ERROR_INCOMPATIBLE_KEY,
"Key generation error: Unexpected public key exponent.");
}
memcpy(args->pubkey.rsa.exponent.value, inner_tag_value, inner_tag_len);
args->pubkey.rsa.exponent.len = inner_tag_len;
p = NULL;
break;
case SC_ISOAPPLET_ALG_REF_EC_GEN:
/* Compare the parameters received from the card to the ones sent to the card. */
inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x81, &inner_tag_len);
if(inner_tag_value == NULL || inner_tag_len != args->pubkey.ec.params.prime.len
|| memcmp(inner_tag_value, args->pubkey.ec.params.prime.value, inner_tag_len) != 0)
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid prime.");
inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x82, &inner_tag_len);
if(inner_tag_value == NULL || inner_tag_len != args->pubkey.ec.params.coefficientA.len
|| memcmp(inner_tag_value, args->pubkey.ec.params.coefficientA.value, inner_tag_len) != 0)
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid coefficient A.");
inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x83, &inner_tag_len);
if(inner_tag_value == NULL || inner_tag_len != args->pubkey.ec.params.coefficientB.len
|| memcmp(inner_tag_value, args->pubkey.ec.params.coefficientB.value, inner_tag_len) != 0)
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid coefficient B.");
inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x84, &inner_tag_len);
if(inner_tag_value == NULL || inner_tag_len != args->pubkey.ec.params.basePointG.len
|| memcmp(inner_tag_value, args->pubkey.ec.params.basePointG.value, inner_tag_len) != 0)
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid base point G.");
inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x85, &inner_tag_len);
if(inner_tag_value == NULL || inner_tag_len != args->pubkey.ec.params.order.len
|| memcmp(inner_tag_value, args->pubkey.ec.params.order.value, inner_tag_len) != 0)
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid order.");
inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x87, &inner_tag_len);
if(inner_tag_value == NULL || inner_tag_len != args->pubkey.ec.params.coFactor.len
|| memcmp(inner_tag_value, args->pubkey.ec.params.coFactor.value, inner_tag_len) != 0)
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid cofactor.");
/* Extract public key */
inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x86, &inner_tag_len);
if(inner_tag_value == NULL || inner_tag_len != args->pubkey.ec.ecPointQ.len)
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid EC point Q.");
memcpy(args->pubkey.ec.ecPointQ.value, inner_tag_value, inner_tag_len);
break;
default:
LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "Unable to parse public key: Unsupported algorithm.");
}/* switch */
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
/*
* @brief Use PUT DATA to import a private RSA key.
*
* For simplicity, command chaining has to be used. One chunk (apdu) must contain
* one RSA field (P, Q, etc.). The first apdu must contain the outer tag (7F48).
*
* @param card
* @param rsa The RSA private key to import.
*
* @return SC_ERROR_INVALID_ARGUMENTS: The RSA key does not contain CRT fields.
* other errors: Transmit errors / errors returned by card.
*/
static int
isoApplet_put_data_prkey_rsa(sc_card_t *card, sc_cardctl_isoApplet_import_key_t *args)
{
sc_apdu_t apdu;
u8 sbuf[SC_MAX_EXT_APDU_BUFFER_SIZE];
u8 *p = NULL;
int r;
size_t tags_len;
LOG_FUNC_CALLED(card->ctx);
if(!args->privkey.rsa.p.value
|| !args->privkey.rsa.q.value
|| !args->privkey.rsa.iqmp.value
|| !args->privkey.rsa.dmp1.value
|| !args->privkey.rsa.dmq1.value)
{
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "RSA key is missing information.");
}
/* Note: The format is according to ISO 2-byte tag 7F48
* "T-L pair to indicate a private key data object" */
/* Calculate the length of all inner tag-length-value entries, but do not write anything yet. */
tags_len = 0;
r = sc_asn1_put_tag(0x92, NULL, args->privkey.rsa.p.len, NULL, 0, NULL);
LOG_TEST_RET(card->ctx, r, "Error handling TLV.");
tags_len += r;
r = sc_asn1_put_tag(0x93, NULL, args->privkey.rsa.q.len, NULL, 0, NULL);
LOG_TEST_RET(card->ctx, r, "Error handling TLV.");
tags_len += r;
r = sc_asn1_put_tag(0x94, NULL, args->privkey.rsa.iqmp.len, NULL, 0, NULL);
LOG_TEST_RET(card->ctx, r, "Error handling TLV.");
tags_len += r;
r = sc_asn1_put_tag(0x95, NULL, args->privkey.rsa.dmp1.len, NULL, 0, NULL);
LOG_TEST_RET(card->ctx, r, "Error handling TLV.");
tags_len += r;
r = sc_asn1_put_tag(0x96, NULL, args->privkey.rsa.dmq1.len, NULL, 0, NULL);
LOG_TEST_RET(card->ctx, r, "Error handling TLV.");
tags_len += r;
/* Write the outer tag and length information. */
p = sbuf;
r = sc_asn1_put_tag(0x7F48, NULL, tags_len, p, sizeof(sbuf), &p);
LOG_TEST_RET(card->ctx, r, "Error handling TLV.");
/* Write inner tags. */
/* p */
r = sc_asn1_put_tag(0x92, args->privkey.rsa.p.value, args->privkey.rsa.p.len, p, sizeof(sbuf) - (p - sbuf), &p);
if(r < 0)
goto out;
/* q */
r = sc_asn1_put_tag(0x93, args->privkey.rsa.q.value, args->privkey.rsa.q.len, p, sizeof(sbuf) - (p - sbuf), &p);
if(r < 0)
goto out;
/* 1/q mod p */
r = sc_asn1_put_tag(0x94, args->privkey.rsa.iqmp.value, args->privkey.rsa.iqmp.len, p, sizeof(sbuf) - (p - sbuf), &p);
if(r < 0)
goto out;
/* d mod (p-1) */
r = sc_asn1_put_tag(0x95, args->privkey.rsa.dmp1.value, args->privkey.rsa.dmp1.len, p, sizeof(sbuf) - (p - sbuf), &p);
if(r < 0)
goto out;
/* d mod (q-1) */
r = sc_asn1_put_tag(0x96, args->privkey.rsa.dmq1.value, args->privkey.rsa.dmq1.len, p, sizeof(sbuf) - (p - sbuf), &p);
if(r < 0)
goto out;
/* Send to card, using chaining or extended APDUs. */
sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xDB, 0x3F, 0xFF);
apdu.data = sbuf;
apdu.datalen = p - sbuf;
apdu.lc = p - sbuf;
if ((card->caps & SC_CARD_CAP_APDU_EXT) == 0)
{
/* The lower layers will automatically do chaining */
apdu.flags |= SC_APDU_FLAGS_CHAINING;
}
r = sc_transmit_apdu(card, &apdu);
if(r < 0)
goto out;
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
if(apdu.sw1 == 0x6A && apdu.sw2 == 0x81)
{
sc_log(card->ctx, "Key import not supported by the card with that particular key type. "
"Your card may not support the specified algorithm used by the applet / specified by you. "
"In most cases, this happens when trying to import EC keys not supported by your java card. "
"In this case, look for supported field lengths and whether FP and/or F2M are supported. "
"If you tried to import a private RSA key, check the key length.");
}
if(apdu.sw1 == 0x69 && apdu.sw2 == 0x00)
{
sc_log(card->ctx, "Key import not allowed by the applet's security policy. "
"If you want to allow key import, set DEF_PRIVATE_KEY_IMPORT_ALLOWED in the IsoApplet,"
" rebuild and reinstall the applet.");
}
if(r < 0)
goto out;
r = SC_SUCCESS;
out:
sc_mem_clear(sbuf, sizeof(sbuf));
LOG_FUNC_RETURN(card->ctx, r);
}
/*
* @brief Use PUT DATA to import a private EC key.
*
* Format of transmitted data:
* 0xE0 - Private class, constructed encoding, number one.
* 0x81 - prime
* 0x82 - coefficient A
* 0x83 - coefficient B
* 0x84 - base point G
* 0x85 - order
* 0x87 - cofactor
* 0x88 - private D (private key)
*
* @param card
* @param ec The EC private key to import.
*
* @return SC_ERROR_INVALID_ARGUMENTS: Curve parameters or private component is missing.
* other errors: Transmit errors / errors returned by card.
* ASN1 errors.
*/
static int
isoApplet_put_data_prkey_ec(sc_card_t *card, sc_cardctl_isoApplet_import_key_t *args)
{
sc_apdu_t apdu;
u8 sbuf[SC_MAX_EXT_APDU_BUFFER_SIZE];
int r;
u8 *p;
size_t tags_len;
LOG_FUNC_CALLED(card->ctx);
if(!args->privkey.ec.privateD.value
|| !args->privkey.ec.params.prime.value
|| !args->privkey.ec.params.coefficientA.value
|| !args->privkey.ec.params.coefficientB.value
|| !args->privkey.ec.params.basePointG.value
|| !args->privkey.ec.params.order.value
|| !args->privkey.ec.params.coFactor.value
)
{
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Missing information about EC private key.");
}
/* Calculate the length of all inner tag-length-value entries, but do not write anything yet. */
tags_len = 0;
r = sc_asn1_put_tag(0x81, NULL, args->privkey.ec.params.prime.len, NULL, 0, NULL);
LOG_TEST_RET(card->ctx, r, "Error handling TLV.");
tags_len += r;
r = sc_asn1_put_tag(0x82, NULL, args->privkey.ec.params.coefficientA.len, NULL, 0, NULL);
LOG_TEST_RET(card->ctx, r, "Error handling TLV.");
tags_len += r;
r = sc_asn1_put_tag(0x83, NULL, args->privkey.ec.params.coefficientB.len, NULL, 0, NULL);
LOG_TEST_RET(card->ctx, r, "Error handling TLV.");
tags_len += r;
r = sc_asn1_put_tag(0x84, NULL, args->privkey.ec.params.basePointG.len, NULL, 0, NULL);
LOG_TEST_RET(card->ctx, r, "Error handling TLV.");
tags_len += r;
r = sc_asn1_put_tag(0x85, NULL, args->privkey.ec.params.order.len, NULL, 0, NULL);
LOG_TEST_RET(card->ctx, r, "Error handling TLV.");
tags_len += r;
r = sc_asn1_put_tag(0x87, NULL, args->privkey.ec.params.coFactor.len, NULL, 0, NULL);
LOG_TEST_RET(card->ctx, r, "Error handling TLV.");
tags_len += r;
r = sc_asn1_put_tag(0x88, NULL, args->privkey.ec.privateD.len, NULL, 0, NULL);
LOG_TEST_RET(card->ctx, r, "Error handling TLV.");
tags_len += r;
/* Write the outer tag and length information. */
p = sbuf;
r = sc_asn1_put_tag(0xE0, NULL, tags_len, p, sizeof(sbuf), &p);
LOG_TEST_RET(card->ctx, r, "Error handling TLV.");
/* Write inner tags. */
r = isoApplet_put_ec_params(card, &args->privkey.ec.params, p, sizeof(sbuf) - (p - sbuf), &p);
if(r < 0)
{
sc_log(card->ctx, "Error composing EC params.");
goto out;
}
r = sc_asn1_put_tag(0x88, args->privkey.ec.privateD.value, args->privkey.ec.privateD.len, p, sizeof(sbuf) - (p - sbuf), &p);
if(r < 0)
goto out;
/* Send to card. */
sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xDB, 0x3F, 0xFF);
apdu.lc = p - sbuf;
apdu.datalen = p - sbuf;
apdu.data = sbuf;
if ((apdu.datalen > 255) && !(card->caps & SC_CARD_CAP_APDU_EXT))
{
apdu.flags |= SC_APDU_FLAGS_CHAINING;
}
r = sc_transmit_apdu(card, &apdu);
if(r < 0)
{
sc_log(card->ctx, "APDU transmit failed");
goto out;
}
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
if(apdu.sw1 == 0x6D && apdu.sw2 == 0x00)
{
sc_log(card->ctx, "The applet returned that the PUT DATA instruction byte is not supported. "
"If you are using an older applet version and are trying to import keys, please update your applet first.");
}
else if(apdu.sw1 == 0x6A && apdu.sw2 == 0x81)
{
sc_log(card->ctx, "Key import not supported by the card with that particular key type. "
"Your card may not support the specified algorithm used by the applet / specified by you. "
"In most cases, this happens when trying to import EC keys not supported by your java card. "
"In this case, look for supported field lengths and whether FP and/or F2M are supported. "
"If you tried to import a private RSA key, check the key length.");
}
else if(apdu.sw1 == 0x69 && apdu.sw2 == 0x00)
{
sc_log(card->ctx, "Key import not allowed by the applet's security policy. "
"If you want to allow key import, set DEF_PRIVATE_KEY_IMPORT_ALLOWED in the IsoApplet,"
" rebuild and reinstall the applet.");
}
if(r < 0)
{
sc_log(card->ctx, "Card returned error");
goto out;
}
r = SC_SUCCESS;
out:
sc_mem_clear(sbuf, sizeof(sbuf));
LOG_FUNC_RETURN(card->ctx, r);
}
/*
* @brief Import a private key.
*/
static int
isoApplet_ctl_import_key(sc_card_t *card, sc_cardctl_isoApplet_import_key_t *args)
{
int r;
sc_apdu_t apdu;
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
u8 *p;
LOG_FUNC_CALLED(card->ctx);
/*
* Private keys are not stored in the filesystem.
* ISO 7816-8 - section C.2 describes:
* "Usage of the PUT DATA command for private key import"
* The applet uses this PUT DATA to import private keys, if private key import is allowed.
*
* The first step is to perform a MANAGE SECURITY ENVIRONMENT as it would be done
* with on-card key generation. The second step is PUT DATA (instead of
* GENERATE ASYMMETRIC KEY PAIR).
*/
/* MANAGE SECURITY ENVIRONMENT (SET). Set the algorithm and key references. */
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0x00);
p = sbuf;
*p++ = 0x80; /* algorithm reference */
*p++ = 0x01;
*p++ = args->algorithm_ref;
*p++ = 0x84; /* Private key reference */
*p++ = 0x01;
*p++ = args->priv_key_ref;
r = p - sbuf;
p = NULL;
apdu.lc = r;
apdu.datalen = r;
apdu.data = sbuf;
r = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, r, "%s: APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_TEST_RET(card->ctx, r, "Card returned error");
/* PUT DATA */
switch(args->algorithm_ref)
{
case SC_ISOAPPLET_ALG_REF_RSA_GEN_2048:
r = isoApplet_put_data_prkey_rsa(card, args);
LOG_TEST_RET(card->ctx, r, "Error in PUT DATA.");
break;
case SC_ISOAPPLET_ALG_REF_EC_GEN:
r = isoApplet_put_data_prkey_ec(card, args);
LOG_TEST_RET(card->ctx, r, "Error in PUT DATA.");
break;
default:
LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "Unknown algorithm reference.");
}
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
static int
isoApplet_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr)
{
int r = 0;
LOG_FUNC_CALLED(card->ctx);
switch (cmd)
{
case SC_CARDCTL_ISOAPPLET_GENERATE_KEY:
r = isoApplet_ctl_generate_key(card,
(sc_cardctl_isoApplet_genkey_t *) ptr);
break;
case SC_CARDCTL_ISOAPPLET_IMPORT_KEY:
r = isoApplet_ctl_import_key(card,
(sc_cardctl_isoApplet_import_key_t *) ptr);
break;
default:
r = SC_ERROR_NOT_SUPPORTED;
}
LOG_FUNC_RETURN(card->ctx, r);
}
static int
isoApplet_set_security_env(sc_card_t *card,
const sc_security_env_t *env, int se_num)
{
sc_apdu_t apdu;
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
u8 *p;
int r;
struct isoApplet_drv_data *drvdata = DRVDATA(card);
LOG_FUNC_CALLED(card->ctx);
if(se_num != 0)
{
LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED,
"IsoApplet does not support storing of security environments.");
}
assert(card != NULL && env != NULL);
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0);
switch (env->operation)
{
case SC_SEC_OPERATION_DECIPHER:
apdu.p2 = 0xB8;
break;
case SC_SEC_OPERATION_SIGN:
apdu.p2 = 0xB6;
break;
default:
return SC_ERROR_INVALID_ARGUMENTS;
}
p = sbuf;
if (env->flags & SC_SEC_ENV_ALG_PRESENT)
{
switch(env->algorithm)
{
case SC_ALGORITHM_RSA:
if( env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1 )
{
drvdata->sec_env_alg_ref = ISOAPPLET_ALG_REF_RSA_PAD_PKCS1;
}
else
{
LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "IsoApplet only supports RSA with PKCS1 padding.");
}
break;
case SC_ALGORITHM_EC:
if( env->algorithm_flags & SC_ALGORITHM_ECDSA_RAW )
{
drvdata->sec_env_alg_ref = ISOAPPLET_ALG_REF_ECDSA;
drvdata->sec_env_ec_field_length = env->algorithm_ref;
}
else
{
LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "IsoApplet only supports raw ECDSA.");
}
break;
default:
LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "Unsupported algorithm.");
}
*p++ = 0x80; /* algorithm reference */
*p++ = 0x01;
*p++ = drvdata->sec_env_alg_ref;
}
if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT)
{
*p++ = 0x81;
*p++ = env->file_ref.len;
assert(sizeof(sbuf) - (p - sbuf) >= env->file_ref.len);
memcpy(p, env->file_ref.value, env->file_ref.len);
p += env->file_ref.len;
}
if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT)
{
if (env->flags & SC_SEC_ENV_KEY_REF_SYMMETRIC)
*p++ = 0x83;
else
*p++ = 0x84;
*p++ = 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 = p - sbuf;
apdu.lc = r;
apdu.datalen = r;
apdu.data = sbuf;
if (apdu.datalen != 0)
{
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");
}
LOG_FUNC_RETURN(card->ctx, r);
}
static int
isoApplet_compute_signature(struct sc_card *card,
const u8 *data, size_t datalen,
u8 *out, size_t outlen)
{
struct sc_context *ctx = card->ctx;
struct isoApplet_drv_data *drvdata = DRVDATA(card);
int r;
LOG_FUNC_CALLED(ctx);
r = iso_ops->compute_signature(card, data, datalen, out, outlen);
if(r < 0)
{
LOG_FUNC_RETURN(ctx, r);
}
/* If ECDSA was used, the ASN.1 sequence of integers R,S returned by the
* card needs to be converted to the raw concatenation of R,S for PKCS#11. */
if(drvdata->sec_env_alg_ref == ISOAPPLET_ALG_REF_ECDSA)
{
u8* p = NULL;
size_t len = (drvdata->sec_env_ec_field_length + 7) / 8 * 2;
if (len > outlen)
LOG_FUNC_RETURN(ctx, SC_ERROR_BUFFER_TOO_SMALL);
p = calloc(1,len);
if (!p)
LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY);
r = sc_asn1_sig_value_sequence_to_rs(ctx, out, r, p, len);
if (!r) {
memcpy(out, p, len);
r = len;
}
free(p);
}
LOG_FUNC_RETURN(ctx, r);
}
static int
isoApplet_get_challenge(struct sc_card *card, u8 *rnd, size_t len)
{
int r;
LOG_FUNC_CALLED(card->ctx);
if(card->caps & SC_CARD_CAP_RNG) {
r = iso_ops->get_challenge(card, rnd, len);
} else {
r = SC_ERROR_NOT_SUPPORTED;
}
LOG_FUNC_RETURN(card->ctx, r);
}
static int isoApplet_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) {
size_t rlen = SC_MAX_APDU_BUFFER_SIZE;
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
r = isoApplet_select_applet(card, isoApplet_aid, ISOAPPLET_AID_LEN, rbuf, &rlen);
}
LOG_FUNC_RETURN(card->ctx, r);
}
static struct sc_card_driver *sc_get_driver(void)
{
sc_card_driver_t *iso_drv = sc_get_iso7816_driver();
if(iso_ops == NULL)
{
iso_ops = iso_drv->ops;
}
isoApplet_ops = *iso_drv->ops;
isoApplet_ops.match_card = isoApplet_match_card;
isoApplet_ops.init = isoApplet_init;
isoApplet_ops.finish = isoApplet_finish;
isoApplet_ops.card_ctl = isoApplet_card_ctl;
isoApplet_ops.create_file = isoApplet_create_file;
isoApplet_ops.process_fci = isoApplet_process_fci;
isoApplet_ops.set_security_env = isoApplet_set_security_env;
isoApplet_ops.compute_signature = isoApplet_compute_signature;
isoApplet_ops.get_challenge = isoApplet_get_challenge;
isoApplet_ops.card_reader_lock_obtained = isoApplet_card_reader_lock_obtained;
/* unsupported functions */
isoApplet_ops.write_binary = NULL;
isoApplet_ops.read_record = NULL;
isoApplet_ops.write_record = NULL;
isoApplet_ops.append_record = NULL;
isoApplet_ops.update_record = NULL;
isoApplet_ops.restore_security_env = NULL;
return &isoApplet_drv;
}
struct sc_card_driver * sc_get_isoApplet_driver(void)
{
return sc_get_driver();
}