Add support for the IsoApplet (Java Card applet)

The IsoApplet can be found here:
https://github.com/philipWendland/IsoApplet
Add read/write support for this applet, including RSA and
ECC support.
This commit is contained in:
Philip Wendland 2014-08-28 14:29:16 +02:00
parent 9f3dbaa39d
commit 48bd6b0964
No known key found for this signature in database
GPG Key ID: 4424DC9CFB68DAAF
12 changed files with 2307 additions and 6 deletions

View File

@ -41,6 +41,7 @@ libopensc_la_SOURCES = \
card-itacns.c card-authentic.c \
card-iasecc.c iasecc-sdo.c iasecc-sm.c card-sc-hsm.c \
card-dnie.c cwa14890.c cwa-dnie.c user-interface.c \
card-isoApplet.c \
\
pkcs15-openpgp.c pkcs15-infocamere.c pkcs15-starcert.c \
pkcs15-tcos.c pkcs15-esteid.c pkcs15-postecert.c pkcs15-gemsafeGPK.c \

View File

@ -23,7 +23,7 @@ OBJECTS = \
card-rtecp.obj card-westcos.obj card-myeid.obj card-ias.obj \
card-itacns.obj card-authentic.obj \
card-iasecc.obj iasecc-sdo.obj iasecc-sm.obj cwa-dnie.obj cwa14890.obj \
card-sc-hsm.obj card-dnie.obj user-interface.obj \
card-sc-hsm.obj card-dnie.obj user-interface.obj card-isoApplet.obj \
\
pkcs15-openpgp.obj pkcs15-infocamere.obj pkcs15-starcert.obj \
pkcs15-tcos.obj pkcs15-esteid.obj pkcs15-postecert.obj pkcs15-gemsafeGPK.obj \

File diff suppressed because it is too large Load Diff

View File

@ -255,7 +255,14 @@ enum {
*/
SC_CARDCTL_DNIE_BASE = _CTL_PREFIX('D', 'N', 'I'),
SC_CARDCTL_DNIE_GENERATE_KEY,
SC_CARDCTL_DNIE_GET_INFO
SC_CARDCTL_DNIE_GET_INFO,
/*
* isoApplet Java Card Applet
*/
SC_CARDCTL_ISOAPPLET_BASE = _CTL_PREFIX('I','S','O'),
SC_CARDCTL_ISOAPPLET_GENERATE_KEY,
SC_CARDCTL_ISOAPPLET_IMPORT_KEY
};
enum {
@ -957,6 +964,30 @@ typedef struct sc_cardctl_sc_hsm_wrapped_key {
size_t wrapped_key_length; /* Length of key blob */
} sc_cardctl_sc_hsm_wrapped_key_t;
/*
* isoApplet
*/
#define SC_ISOAPPLET_ALG_REF_RSA_GEN_2048 0xF3
#define SC_ISOAPPLET_ALG_REF_EC_GEN_BRAINPOOLP192R1 0xE0
#define SC_ISOAPPLET_ALG_REF_EC_GEN_PRIME256V1 0xE1
typedef struct sc_cardctl_isoApplet_genkey {
u8 algorithm_ref; /* Algorithm reference sent to card */
unsigned char *exponent; /* RSA public key exponent */
unsigned int exponent_len;
unsigned int priv_key_ref; /* Private key refernce sent to card */
unsigned char *pubkey; /* RSA public key modulus (or EC tlv-encoded public key) */
unsigned int pubkey_len;
} sc_cardctl_isoApplet_genkey_t;
typedef struct sc_cardctl_isoApplet_import_key {
u8 algorithm_ref; /*Algorithm reference sent to card */
unsigned int priv_key_ref; /* Private key refernce sent to card */
struct sc_pkcs15_prkey *prkey;
} sc_cardctl_isoApplet_import_key_t;
#ifdef __cplusplus
}
#endif

View File

@ -199,7 +199,11 @@ enum {
SC_CARD_TYPE_DNIE_BLANK, /* ATR LC byte: 00 */
SC_CARD_TYPE_DNIE_ADMIN, /* ATR LC byte: 01 */
SC_CARD_TYPE_DNIE_USER, /* ATR LC byte: 03 */
SC_CARD_TYPE_DNIE_TERMINATED /* ATR LC byte: 0F */
SC_CARD_TYPE_DNIE_TERMINATED, /* ATR LC byte: 0F */
/* JavaCards with isoApplet */
SC_CARD_TYPE_ISO_APPLET_BASE = 28000,
SC_CARD_TYPE_ISO_APPLET_GENERIC
};
extern sc_card_driver_t *sc_get_default_driver(void);
@ -236,6 +240,7 @@ extern sc_card_driver_t *sc_get_authentic_driver(void);
extern sc_card_driver_t *sc_get_iasecc_driver(void);
extern sc_card_driver_t *sc_get_epass2003_driver(void);
extern sc_card_driver_t *sc_get_dnie_driver(void);
extern sc_card_driver_t *sc_get_isoApplet_driver(void);
#ifdef __cplusplus
}

View File

@ -108,6 +108,7 @@ static const struct _sc_driver_entry internal_card_drivers[] = {
{ "atrust-acos",(void *(*)(void)) sc_get_atrust_acos_driver },
{ "PIV-II", (void *(*)(void)) sc_get_piv_driver },
{ "itacns", (void *(*)(void)) sc_get_itacns_driver },
{ "isoApplet", (void *(*)(void)) sc_get_isoApplet_driver },
/* The default driver should be last, as it handles all the
* unrecognized cards. */
{ "default", (void *(*)(void)) sc_get_default_driver },

View File

@ -29,7 +29,8 @@ dist_pkgdata_DATA = \
iasecc.profile \
ias_adele_admin1.profile ias_adele_admin2.profile ias_adele_common.profile \
iasecc_generic_pki.profile iasecc_admin_eid.profile iasecc_generic_oberthur.profile \
openpgp.profile sc-hsm.profile
openpgp.profile sc-hsm.profile \
isoApplet.profile
AM_CPPFLAGS = -DSC_PKCS15_PROFILE_DIRECTORY=\"$(pkgdatadir)\" \
-I$(top_srcdir)/src
@ -46,4 +47,5 @@ libpkcs15init_la_SOURCES = \
pkcs15-rtecp.c pkcs15-myeid.c \
pkcs15-oberthur.c pkcs15-oberthur-awp.c \
pkcs15-authentic.c pkcs15-iasecc.c pkcs15-openpgp.c \
pkcs15-sc-hsm.c
pkcs15-sc-hsm.c \
pkcs15-isoApplet.c

View File

@ -9,7 +9,8 @@ OBJECTS = pkcs15-lib.obj profile.obj \
pkcs15-muscle.obj pkcs15-asepcos.obj pkcs15-rutoken.obj \
pkcs15-entersafe.obj pkcs15-rtecp.obj pkcs15-westcos.obj \
pkcs15-myeid.obj pkcs15-authentic.obj pkcs15-iasecc.obj \
pkcs15-epass2003.obj pkcs15-openpgp.obj pkcs15-sc-hsm.obj
pkcs15-epass2003.obj pkcs15-openpgp.obj pkcs15-sc-hsm.obj \
pkcs15-isoApplet.obj
all: $(TARGET)

View File

@ -0,0 +1,164 @@
#
# PKCS15 profile for the isoApplet JavaCard Applet.
# - init driver: pkcs15-isoApplet.c
# - card driver: card-isoApplet.c
#
cardinfo {
label = "JavaCard isoApplet";
manufacturer = "unknown";
min-pin-length = 4;
max-pin-length = 16;
pin-pad-char = 0x00;
}
pkcs15 {
# Method to calculate ID of the crypto objects
# mozilla: SHA1(modulus) for RSA, SHA1(pub) for DSA
# rfc2459: SHA1(SequenceASN1 of public key components as ASN1 integers)
# native: 'E' + number_of_present_objects_of_the_same_type
# default value: 'native'
pkcs15-id-style = native;
}
option default {
macros {
unusedspace-size = 128;
odf-size = 256;
aodf-size = 256;
cdf-size = 512;
prkdf-size = 512;
pukdf-size = 512;
dodf-size = 256;
}
}
PIN so-pin {
attempts = 3;
max-length = 16;
min-length = 4;
reference = 1;
flags = case-sensitive, needs-padding;
}
PIN so-puk {
attempts = 3;
max-length = 16;
min-length = 16;
reference = 2;
flags = unblockingPin, unblock-disabled, case-sensitive, change-disabled;
}
filesystem {
DF MF {
path = 3F00;
type = DF;
# This is the DIR file
EF DIR {
type = EF;
file-id = 2F00;
size = 128;
acl = *=NONE;
}
# Here comes the application DF
DF PKCS15-AppDF {
type = DF;
file-id = 5015;
aid = A0:00:00:00:63:50:4B:43:53:2D:31:35;
acl = *=NONE, DELETE=$PIN;
size = 5000;
EF PKCS15-ODF {
file-id = 5031;
size = $odf-size;
ACL = *=NONE;
}
EF PKCS15-TokenInfo {
file-id = 5032;
ACL = *=NONE;
}
EF PKCS15-UnusedSpace {
file-id = 5033;
size = $unusedspace-size;
ACL = *=NONE;
}
EF PKCS15-AODF {
file-id = 4401;
size = $aodf-size;
ACL = *=$PIN, READ=NONE;
}
EF PKCS15-PrKDF {
file-id = 4402;
size = $prkdf-size;
acl = *=$PIN, READ=NONE;
}
EF PKCS15-PuKDF {
file-id = 4403;
size = $pukdf-size;
acl = *=$PIN, READ=NONE;
}
EF PKCS15-CDF {
file-id = 4404;
size = $cdf-size;
acl = *=$PIN, READ=NONE;
}
EF PKCS15-DODF {
file-id = 4405;
size = $dodf-size;
ACL = *=$PIN, READ=NONE;
}
template key-domain {
BSO private-key {
ACL = *=$PIN, READ=NEVER;
}
# EF private-key {
# file-id = 3000;
# acl = *=NEVER, UPDATE=$PIN, ERASE=$PIN;
# }
# EF extractable-key {
# file-id = 3100;
# acl = *=NEVER, READ=$PIN, UPDATE=$PIN,
# ERASE=$PIN;
# }
EF data {
file-id = 3200;
acl = *=NEVER, UPDATE=$PIN, READ=NONE,
DELETE-SELF=$PIN, ERASE=$PIN;
}
EF privdata {
file-id = 3500;
acl = *=NEVER, UPDATE=$PIN, READ=$PIN,
DELETE-SELF=$PIN, ERASE=$PIN;
}
EF public-key {
file-id = 3300;
acl = *=NEVER, UPDATE=$PIN, READ=NONE,
DELETE-SELF=$PIN, ERASE=$PIN;
}
EF certificate {
file-id = 3400;
acl = *=NEVER, UPDATE=$PIN, READ=NONE,
DELETE-SELF=$PIN, ERASE=$PIN;
}
}
}
}
}

View File

@ -420,6 +420,7 @@ extern struct sc_pkcs15init_operations *sc_pkcs15init_get_iasecc_ops(void);
extern struct sc_pkcs15init_operations *sc_pkcs15init_get_piv_ops(void);
extern struct sc_pkcs15init_operations *sc_pkcs15init_get_openpgp_ops(void);
extern struct sc_pkcs15init_operations *sc_pkcs15init_get_sc_hsm_ops(void);
extern struct sc_pkcs15init_operations *sc_pkcs15init_get_isoApplet_ops(void);
#ifdef __cplusplus
}

View File

@ -0,0 +1,871 @@
/*
* pkcs15-init driver for JavaCards with IsoApplet installed.
*
* 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 "config.h"
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdarg.h>
#include "../libopensc/log.h"
#include "../libopensc/internal.h"
#include "../libopensc/opensc.h"
#include "../libopensc/cardctl.h"
#include "../libopensc/asn1.h"
#include "pkcs15-init.h"
#include "profile.h"
#define ISOAPPLET_KEY_ID_MIN 0
#define ISOAPPLET_KEY_ID_MAX 15
struct ec_curve
{
const struct sc_lv_data oid;
const struct sc_lv_data prime;
const struct sc_lv_data coefficientA;
const struct sc_lv_data coefficientB;
const struct sc_lv_data basePointG;
const struct sc_lv_data order;
const struct sc_lv_data coFactor;
};
static struct ec_curve curves[] =
{
{
{ (unsigned char *) "\x2B\x24\x03\x03\x02\x08\x01\x01\x03", 9}, /* brainpoolP192r1 */
{ (unsigned char *) "\xC3\x02\xF4\x1D\x93\x2A\x36\xCD\xA7\xA3\x46\x30\x93\xD1\x8D\xB7\x8F\xCE\x47\x6D\xE1\xA8\x62\x97", 24},
{ (unsigned char *) "\x6A\x91\x17\x40\x76\xB1\xE0\xE1\x9C\x39\xC0\x31\xFE\x86\x85\xC1\xCA\xE0\x40\xE5\xC6\x9A\x28\xEF", 24},
{ (unsigned char *) "\x46\x9A\x28\xEF\x7C\x28\xCC\xA3\xDC\x72\x1D\x04\x4F\x44\x96\xBC\xCA\x7E\xF4\x14\x6F\xBF\x25\xC9", 24},
{ (unsigned char *) "\xC0\xA0\x64\x7E\xAA\xB6\xA4\x87\x53\xB0\x33\xC5\x6C\xB0\xF0\x90\x0A\x2F\x5C\x48\x53\x37\x5F\xD6\x14\xB6\x90\x86\x6A\xBD\x5B\xB8\x8B\x5F\x48\x28\xC1\x49\x00\x02\xE6\x77\x3F\xA2\xFA\x29\x9B\x8F", 48},
{ (unsigned char *) "\xC3\x02\xF4\x1D\x93\x2A\x36\xCD\xA7\xA3\x46\x2F\x9E\x9E\x91\x6B\x5B\xE8\xF1\x02\x9A\xC4\xAC\xC1", 24},
{ (unsigned char *) "\x00\x01", 2}
},
{
{ (unsigned char *) "\x2A\x86\x48\xCE\x3D\x03\x01\x07", 8}, /* secp256r1 aka prime256v1 */
{ (unsigned char *) "\xFF\xFF\xFF\xFF\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 32},
{ (unsigned char *) "\xFF\xFF\xFF\xFF\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFC", 32},
{ (unsigned char *) "\x5A\xC6\x35\xD8\xAA\x3A\x93\xE7\xB3\xEB\xBD\x55\x76\x98\x86\xBC\x65\x1D\x06\xB0\xCC\x53\xB0\xF6\x3B\xCE\x3C\x3E\x27\xD2\x60\x4B", 32},
{ (unsigned char *) "\x6B\x17\xD1\xF2\xE1\x2C\x42\x47\xF8\xBC\xE6\xE5\x63\xA4\x40\xF2\x77\x03\x7D\x81\x2D\xEB\x33\xA0\xF4\xA1\x39\x45\xD8\x98\xC2\x96\x4F\xE3\x42\xE2\xFE\x1A\x7F\x9B\x8E\xE7\xEB\x4A\x7C\x0F\x9E\x16\x2B\xCE\x33\x57\x6B\x31\x5E\xCE\xCB\xB6\x40\x68\x37\xBF\x51\xF5", 64},
{ (unsigned char *) "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xBC\xE6\xFA\xAD\xA7\x17\x9E\x84\xF3\xB9\xCA\xC2\xFC\x63\x25\x51", 32},
{ (unsigned char *) "\x00\x01", 2}
},
{
{ NULL, 0},
{ NULL, 0},
{ NULL, 0},
{ NULL, 0},
{ NULL, 0},
{ NULL, 0},
{ NULL, 0}
}
};
/*
* Create DF, using default pkcs15init functions.
*/
static int
isoApplet_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df)
{
sc_card_t *card = p15card->card;
int r = SC_SUCCESS;
LOG_FUNC_CALLED(card->ctx);
if(!profile || !p15card || !df || !p15card->card || !p15card->card->ctx)
{
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
}
r = sc_pkcs15init_create_file(profile, p15card, df);
LOG_FUNC_RETURN(card->ctx, r);
}
/*
* Select a PIN reference.
*
* Basically (as I understand it) the caller passes an auth_info object and the
* auth_info->attrs.pin.reference is supposed to be set accordingly and return.
*
* The IsoApplet only supports a PIN and a PUK at the moment.
* The reference for the PIN is 1, for the PUK 2.
*/
static int
isoApplet_select_pin_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card,
sc_pkcs15_auth_info_t *auth_info)
{
sc_card_t *card = p15card->card;
int preferred, current;
LOG_FUNC_CALLED(card->ctx);
if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN)
{
LOG_FUNC_RETURN(card->ctx, SC_ERROR_OBJECT_NOT_VALID);
}
current = auth_info->attrs.pin.reference;
if (current < 0)
{
current = 0;
}
if(current > 2)
{
/* Only two PINs supported: User PIN and PUK. */
LOG_FUNC_RETURN(card->ctx, SC_ERROR_TOO_MANY_OBJECTS);
}
else
{
if(auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN)
{
/* PUK */
preferred = 2;
}
else
{
/* PIN */
preferred = 1;
}
}
auth_info->attrs.pin.reference = preferred;
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
/*
* Create a PIN and store it on the card using CHANGE REFERENCE DATA for PIN transmission.
* First, the PUK is transmitted, then the PIN. Now, the IsoApplet is in the
* "STATE_OPERATIONAL_ACTIVATED" lifecycle state.
*/
static int
isoApplet_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df,
sc_pkcs15_object_t *pin_obj,
const u8 *pin, size_t pin_len,
const u8 *puk, size_t puk_len)
{
sc_card_t *card = p15card->card;
sc_pkcs15_auth_info_t *auth_info = (sc_pkcs15_auth_info_t *) pin_obj->data;
struct sc_pkcs15_pin_attributes *pin_attrs = &auth_info->attrs.pin;
int r;
LOG_FUNC_CALLED(card->ctx);
if(!pin || !pin_len || !p15card || !p15card->card || !df || !&df->path)
{
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
}
if(pin_attrs->reference != 1 && pin_attrs->reference != 2)
{
/* Reject PIN reference. */
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_PIN_REFERENCE);
}
/* If we have a PUK, set it first. */
if(puk && puk_len)
{
/* The PUK has a incremented reference, i.e. pins are odd, puks are equal (+1). */
r = sc_change_reference_data(p15card->card, SC_AC_CHV,
pin_attrs->reference+1,
NULL, 0,
puk, puk_len, NULL);
if(r < 0)
{
LOG_FUNC_RETURN(card->ctx, r);
}
}
/* Store PIN: (use CHANGE REFERENCE DATA). */
r = sc_change_reference_data(p15card->card, SC_AC_CHV,
pin_attrs->reference,
NULL, 0,
pin, pin_len, NULL);
LOG_FUNC_RETURN(card->ctx, r);
}
/*
* @brief Get the OID of the curve specified by a curve name.
*
* @param[in] named_curve The name of the curve to search the OID of.
* Supported values are: brainpoolP192r1, prime256v1.
* @param[out] oid The OID of the curve.
*
* @returns SC_SUCCESS: If the curve was found.
* SC_ERROR_INVALID_ARGUMENTS: If named_curve was null or the curve
* was not found
*/
static int
isoApplet_get_curve_oid(const char* named_curve, const struct sc_lv_data **oid)
{
if(!named_curve)
return SC_ERROR_INVALID_ARGUMENTS;
if(strncmp(named_curve, "brainpoolP192r1", 15) == 0)
{
*oid = &curves[0].oid;
return SC_SUCCESS;
}
else if(strncmp(named_curve, "prime256v1", 10) == 0)
{
*oid = &curves[1].oid;
return SC_SUCCESS;
}
return SC_ERROR_INVALID_ARGUMENTS;
}
/*
* @brief Check the consistency of TLV-encoded EC curve parameters.
*
* Check the EC params in buf (length: len) that are structured according
* to ISO 7816-8 table 3 - Public key data objects.
* The params are compared with the ones given in the curve struct.
*
* @param[in] ctx
* @param[in] buf The buffer containing the TLV-encoded (ISO 7816-8 table 3)
* EC parameters.
* @param[in] len The length of buf.
* @param[in] curve An ec_curve struct that should be used to check the
* parameters in buf.
*
* @return SC_SUCCESS: If the EC parameters are consistent.
* SC_ERROR_INCOMPATIBLE_KEY: If the curve is unknown or the EC
* parameters are not consistent.
*/
static int
checkEcParams(sc_context_t* ctx, const u8* buf, size_t len, const struct ec_curve curve)
{
const u8 *curr_pos = NULL;
size_t tag_len;
int r = SC_SUCCESS;
LOG_FUNC_CALLED(ctx);
/* Check the field. */
curr_pos = sc_asn1_find_tag(ctx, buf, len, (unsigned int) 0x81, &tag_len);
if(curr_pos == NULL || tag_len != curve.prime.len)
{
r = SC_ERROR_INCOMPATIBLE_KEY;
LOG_TEST_RET(ctx, r,
"Could not find any EC field tag in the response template or the length was unexpected.");
}
if(memcmp(curr_pos, curve.prime.value, curve.prime.len) != 0)
{
r = SC_ERROR_INCOMPATIBLE_KEY;
LOG_TEST_RET(ctx, r,
"The EC field by the smartcard was unexpected.");
}
/* Check the coefficient A. */
curr_pos = sc_asn1_find_tag(ctx, buf, len, (unsigned int) 0x82, &tag_len);
if(curr_pos == NULL || tag_len != curve.coefficientA.len)
{
r = SC_ERROR_INCOMPATIBLE_KEY;
LOG_TEST_RET(ctx, r,
"Could not find any EC coefficient A tag in the response template or the length was unexpected.");
}
if(memcmp(curr_pos, curve.coefficientA.value, curve.coefficientA.len) != 0)
{
r = SC_ERROR_INCOMPATIBLE_KEY;
LOG_TEST_RET(ctx, r,
"The EC coefficient A returned by the smartcard was unexpected.");
}
/* Check the coefficient B. */
curr_pos = sc_asn1_find_tag(ctx, buf, len, (unsigned int) 0x83, &tag_len);
if(curr_pos == NULL || tag_len != curve.coefficientB.len)
{
r = SC_ERROR_INCOMPATIBLE_KEY;
LOG_TEST_RET(ctx, r,
"Could not find any EC coefficient B tag in the response template or the length was unexpected.");
}
if(memcmp(curr_pos, curve.coefficientB.value, curve.coefficientB.len) != 0)
{
r = SC_ERROR_INCOMPATIBLE_KEY;
LOG_TEST_RET(ctx, r,
"The EC coefficient B returned by the smartcard was unexpected.");
}
/* Check the basepoint G.
* Note: The IsoApplet omits the 0x04 (uncompressed) tag. */
curr_pos = sc_asn1_find_tag(ctx, buf, len, (unsigned int) 0x84, &tag_len);
if(curr_pos == NULL || tag_len != curve.basePointG.len)
{
r = SC_ERROR_INCOMPATIBLE_KEY;
LOG_TEST_RET(ctx, r,
"Could not find any EC basepoint G tag in the response template or the length was unexpected.");
}
if(memcmp(curr_pos, curve.basePointG.value, curve.basePointG.len) != 0)
{
r = SC_ERROR_INCOMPATIBLE_KEY;
LOG_TEST_RET(ctx, r,
"The EC basepoint G returned by the smartcard was unexpected.");
}
/* Check the order. */
curr_pos = sc_asn1_find_tag(ctx, buf, len, (unsigned int) 0x85, &tag_len);
if(curr_pos == NULL || tag_len != curve.order.len)
{
r = SC_ERROR_INCOMPATIBLE_KEY;
LOG_TEST_RET(ctx, r,
"Could not find any EC order tag in the response template or the length was unexpected.");
}
if(memcmp(curr_pos, curve.order.value, curve.order.len) != 0)
{
r = SC_ERROR_INCOMPATIBLE_KEY;
LOG_TEST_RET(ctx, r,
"The EC order returned by the smartcard was unexpected.");
}
/* Check the coFactor. */
curr_pos = sc_asn1_find_tag(ctx, buf, len, (unsigned int) 0x87, &tag_len);
if(curr_pos == NULL || tag_len != curve.coFactor.len)
{
r = SC_ERROR_INCOMPATIBLE_KEY;
LOG_TEST_RET(ctx, r,
"Could not find any EC cofactor tag in the response template or the length was unexpected.");
}
if(memcmp(curr_pos, curve.coFactor.value, curve.coFactor.len) != 0)
{
r = SC_ERROR_INCOMPATIBLE_KEY;
LOG_TEST_RET(ctx, r,
"The EC cofactor returned by the smartcards was unexpected.");
}
LOG_FUNC_RETURN(ctx, r);
}
/*
* @brief Generate a RSA private key on the card.
*
* A MANAGE SECURITY ENVIRONMENT apdu must have been sent before.
* This function uses card_ctl to access the card-isoApplet driver.
*
* @param[in] key_info
* @param[in] card
* @param[in] pubkey The public key of the generated key pair
* returned by the card.
*
* @return SC_ERROR_INVALID_ARGURMENTS: Invalid key length.
* SC_ERROR_OUT_OF_MEMORY
*/
static int
generate_key_rsa(sc_pkcs15_prkey_info_t *key_info, sc_card_t *card,
sc_pkcs15_pubkey_t *pubkey)
{
int rv;
size_t keybits;
struct sc_cardctl_isoApplet_genkey args;
LOG_FUNC_CALLED(card->ctx);
/* Check key size: */
keybits = key_info->modulus_length;
if (keybits != 2048)
{
rv = SC_ERROR_INVALID_ARGUMENTS;
sc_log(card->ctx, "%s: RSA private key length is unsupported, correct length is 2048", sc_strerror(rv));
goto err;
}
/* Generate the key.
* Note: keysize is not explicitly passed to the card. It assumes 2048 along with the algorithm reference. */
memset(&args, 0, sizeof(args));
args.algorithm_ref = SC_ISOAPPLET_ALG_REF_RSA_GEN_2048;
args.priv_key_ref = key_info->key_reference;
args.pubkey_len = keybits / 8;
args.pubkey = malloc(args.pubkey_len);
if (!args.pubkey)
{
rv = SC_ERROR_OUT_OF_MEMORY;
sc_log(card->ctx, "%s: Unable to allocate public key buffer.", sc_strerror(rv));
goto err;
}
args.exponent_len = 3;
args.exponent = malloc(args.exponent_len);
if(!args.exponent)
{
rv = SC_ERROR_OUT_OF_MEMORY;
sc_log(card->ctx, "%s: Unable to allocate public key exponent buffer.", sc_strerror(rv));
goto err;
}
rv = sc_card_ctl(card, SC_CARDCTL_ISOAPPLET_GENERATE_KEY, &args);
if (rv < 0)
{
sc_log(card->ctx, "%s: Error in card_ctl", sc_strerror(rv));
goto err;
}
/* extract public key */
pubkey->algorithm = SC_ALGORITHM_RSA;
pubkey->u.rsa.modulus.len = args.pubkey_len;
pubkey->u.rsa.modulus.data = args.pubkey;
pubkey->u.rsa.exponent.len = args.exponent_len;
pubkey->u.rsa.exponent.data = args.exponent;
rv = SC_SUCCESS;
LOG_FUNC_RETURN(card->ctx, rv);
err:
if (args.pubkey)
{
free(args.pubkey);
pubkey->u.rsa.modulus.data = NULL;
pubkey->u.rsa.modulus.len = 0;
}
if (args.exponent)
{
free(args.exponent);
pubkey->u.rsa.exponent.data = NULL;
pubkey->u.rsa.exponent.len = 0;
}
LOG_FUNC_RETURN(card->ctx, rv);
}
/*
* @brief Generate a EC private key on the card.
*
* A MANAGE SECURITY ENVIRONMENT apdu must have been sent before.
* This function uses card_ctl to access the card-isoApplet driver.
*
* @param[in] key_info
* @param[in] card
* @param[in] pubkey The public key of the generated key pair
* returned by the card.
*
* @return SC_ERROR_INVALID_ARGURMENTS: Invalid key length or curve.
* SC_ERROR_OUT_OF_MEMORY
* SC_ERROR_INCOMPATIBLE_KEY: The data returned by the card
* was unexpected and can not be
* handled.
*/
static int
generate_key_ec(const sc_pkcs15_prkey_info_t *key_info, sc_card_t *card,
sc_pkcs15_pubkey_t *pubkey)
{
int r;
u8* p = NULL;
u8* ecPubKeyPoint = NULL;
size_t tag_len;
size_t all_tags_len;
const u8* curr_pos = NULL;
struct sc_ec_params* ecp = NULL;
const struct sc_lv_data* oid = NULL;
sc_cardctl_isoApplet_genkey_t args;
const struct sc_pkcs15_ec_parameters* info_ecp =
(struct sc_pkcs15_ec_parameters *) key_info->params.data;
LOG_FUNC_CALLED(card->ctx);
/* Check key size: */
if(key_info->field_length != 192
&& key_info->field_length != 256)
{
sc_log(card->ctx, "EC field length is unsupported, length provided was: %d.", key_info->field_length);
r = SC_ERROR_INVALID_ARGUMENTS;
goto err;
}
if(info_ecp->named_curve && strncmp(info_ecp->named_curve, "brainpoolP192r1", 15) != 0
&& strncmp(info_ecp->named_curve, "prime256v1", 10) != 0)
{
sc_log(card->ctx, "EC key generation failed: Unsupported curve: [%s].", info_ecp->named_curve);
r = SC_ERROR_INVALID_ARGUMENTS;
goto err;
}
/* Generate the key.
* Note: THe field size is not explicitly passed to the card.
* It assumes it along with the algorithm reference. */
memset(&args, 0, sizeof(args));
args.pubkey_len = 512;
args.pubkey = malloc(args.pubkey_len);
if(!args.pubkey)
{
r = SC_ERROR_OUT_OF_MEMORY;
sc_log(card->ctx, "%s: Unable to allocate public key buffer.", sc_strerror(r));
goto err;
}
if(strncmp(info_ecp->named_curve, "brainpoolP192r1", 15) == 0)
{
args.algorithm_ref = SC_ISOAPPLET_ALG_REF_EC_GEN_BRAINPOOLP192R1;
}
else if(strncmp(info_ecp->named_curve, "prime256v1", 10) == 0)
{
args.algorithm_ref = SC_ISOAPPLET_ALG_REF_EC_GEN_PRIME256V1;
}
args.priv_key_ref = key_info->key_reference;
r = sc_card_ctl(card, SC_CARDCTL_ISOAPPLET_GENERATE_KEY, &args);
if (r < 0)
{
sc_log(card->ctx, "%s: Error in card_ctl.", sc_strerror(r));
goto err;
}
/* Extract the public key. */
pubkey->algorithm = SC_ALGORITHM_EC;
/* Get the curves OID. */
r = isoApplet_get_curve_oid(info_ecp->named_curve, &oid);
if(r < 0)
{
sc_log(card->ctx, "Error obtaining the curve OID.", sc_strerror(r));
goto err;
}
/* der-encoded parameters */
ecp = calloc(1, sizeof(struct sc_ec_params));
if(!ecp)
{
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
ecp->der_len = oid->len + 2;
ecp->der = calloc(ecp->der_len, 1);
if(!ecp->der)
{
r = SC_ERROR_OUT_OF_MEMORY;
sc_log(card->ctx, "%s: Unable to allocate public key buffer.", sc_strerror(r));
goto err;
}
ecp->der[0] = 0x06;
ecp->der[1] = (u8)oid->len;
memcpy(ecp->der + 2, oid->value, oid->len);
ecp->type = 1; /* named curve */
pubkey->alg_id = (struct sc_algorithm_id *)calloc(1, sizeof(struct sc_algorithm_id));
if(!pubkey->alg_id)
{
r = SC_ERROR_OUT_OF_MEMORY;
sc_log(card->ctx, "%s: Unable to allocate public key sc_algorithm_id.", sc_strerror(r));
goto err;
}
pubkey->alg_id->algorithm = SC_ALGORITHM_EC;
pubkey->alg_id->params = ecp;
p = args.pubkey;
if(memcmp(info_ecp->named_curve, "brainpoolP192r1", 15) == 0)
{
/* The applet returns the public key encoded according to
* ISO 7816-8 table 3 - Public key data objects. This is a
* 2-byte tag. A length of 0xD0 = 208 is expected for BrainpoolP192r1. */
if(memcmp(p, "\x7F\x49\x81\xD0", 4) != 0)
{
r = SC_ERROR_INCOMPATIBLE_KEY;
sc_log(card->ctx, "%s: Key generation error: Unexpected EC public key received length.", sc_strerror(r));
goto err;
}
else
{
p += 4; /* p points to the value field of the outer (7F 49) tag.
* This value field is a TLV-structure again. */
all_tags_len = 208; /* 0xD0 bytes */
}
/* Check EC params. */
r = checkEcParams(card->ctx, p, all_tags_len, curves[0]);
if(r != SC_SUCCESS)
{
goto err;
}
}
else if(memcmp(info_ecp->named_curve, "prime256v1", 10) == 0)
{
/* The applet returns the public key encoded according to
* ISO 7816-8 table 3 - Public key data objects. This is a
* 2-byte tag. A length of 0x011A = 282 is expected for Prime256v1. */
if(memcmp(p, "\x7F\x49\x82\x01\x1A", 5) != 0)
{
r = SC_ERROR_INCOMPATIBLE_KEY;
sc_log(card->ctx, "%s: Key generation error: Unexpected EC public key parameters.", sc_strerror(r));
goto err;
}
else
{
p += 5; /* p points to the value field of the outer (7F 49) tag.
* This value field is a TLV-structure again. */
all_tags_len = 282; /* 0x011A bytes */
}
/* Check EC params. */
r = checkEcParams(card->ctx, p, all_tags_len, curves[1]);
if(r != SC_SUCCESS)
{
goto err;
}
}
/* Extract ecpointQ */
curr_pos = sc_asn1_find_tag(card->ctx, p, all_tags_len, (unsigned int) 0x86, &tag_len);
if(curr_pos == NULL || tag_len == 0)
{
r = SC_ERROR_INCOMPATIBLE_KEY;
sc_log(card->ctx, "%s: Could not find any EC pointQ tag in the response template.", sc_strerror(r));
goto err;
}
ecPubKeyPoint = malloc(tag_len+1);
if(!ecPubKeyPoint)
{
r = SC_ERROR_OUT_OF_MEMORY;
sc_log(card->ctx, "%s: Unable to allocate public key ecpointQ buffer.", sc_strerror(r));
goto err;
}
*ecPubKeyPoint = 0x04; /* uncompressed */
memcpy(ecPubKeyPoint+1, curr_pos, tag_len);
pubkey->u.ec.ecpointQ.value = ecPubKeyPoint;
pubkey->u.ec.ecpointQ.len = tag_len+1;
/* OID for the public key */
pubkey->u.ec.params.der.value = malloc(ecp->der_len);
if(!pubkey->u.ec.params.der.value)
{
r = SC_ERROR_OUT_OF_MEMORY;
sc_log(card->ctx, "%s: Unable to allocate public key ec params buffer.", sc_strerror(r));
goto err;
}
memcpy(pubkey->u.ec.params.der.value, ecp->der, ecp->der_len);
pubkey->u.ec.params.der.len = ecp->der_len;
r = sc_pkcs15_fix_ec_parameters(card->ctx, &pubkey->u.ec.params);
LOG_FUNC_RETURN(card->ctx, r);
err:
if(pubkey)
{
if(pubkey->alg_id)
{
free(pubkey->alg_id);
pubkey->alg_id = NULL;
}
if(pubkey->u.ec.params.der.value)
{
free(pubkey->u.ec.params.der.value);
pubkey->u.ec.params.der.value = NULL;
pubkey->u.ec.params.der.len = 0;
}
memset(pubkey, 0, sizeof(sc_pkcs15_pubkey_t));
}
if(args.pubkey)
{
free(args.pubkey);
args.pubkey = NULL;
args.pubkey_len = 0;
}
if(ecPubKeyPoint)
{
free(ecPubKeyPoint);
ecPubKeyPoint = NULL;
}
if(ecp)
{
if(ecp->der)
{
free(ecp->der);
ecp->der = NULL;
}
free(ecp);
ecp = NULL;
}
LOG_FUNC_RETURN(card->ctx, r);
}
static int
isoApplet_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card,
sc_pkcs15_object_t *obj,
sc_pkcs15_pubkey_t *pubkey)
{
int r;
sc_pkcs15_prkey_info_t* key_info = (sc_pkcs15_prkey_info_t *) obj->data;
sc_file_t* privKeyFile=NULL;
sc_card_t* card = p15card->card;
LOG_FUNC_CALLED(card->ctx);
/* Authentication stuff. */
r = sc_profile_get_file_by_path(profile, &key_info->path, &privKeyFile);
if(!privKeyFile)
{
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED);
}
r = sc_pkcs15init_authenticate(profile, p15card, privKeyFile, SC_AC_OP_CREATE_EF);
if(r < 0)
{
sc_file_free(privKeyFile);
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED);
}
sc_file_free(privKeyFile);
/* Generate the key. */
switch(obj->type)
{
case SC_PKCS15_TYPE_PRKEY_RSA:
r = generate_key_rsa(key_info, card, pubkey);
break;
case SC_PKCS15_TYPE_PRKEY_EC:
r = generate_key_ec(key_info, card, pubkey);
break;
default:
r = SC_ERROR_NOT_SUPPORTED;
sc_log(card->ctx, "%s: Key generation failed: Unknown/unsupported key type.", strerror(r));
}
LOG_FUNC_RETURN(card->ctx, r);
}
/*
* Create a new key file. This is a no-op, because private keys are stored as key objects on the javacard.
*/
static int
isoApplet_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj)
{
sc_card_t *card = p15card->card;
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
/*
* Select a key reference.
*/
static int
isoApplet_select_key_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card,
sc_pkcs15_prkey_info_t *key_info)
{
int rv = SC_SUCCESS;
sc_card_t *card = p15card->card;
LOG_FUNC_CALLED(card->ctx);
if(key_info->key_reference < ISOAPPLET_KEY_ID_MIN)
{
key_info->key_reference = ISOAPPLET_KEY_ID_MIN;
rv = SC_SUCCESS;
}
if(key_info->key_reference > ISOAPPLET_KEY_ID_MAX)
{
rv = SC_ERROR_TOO_MANY_OBJECTS;
}
LOG_FUNC_RETURN(card->ctx, rv);
}
/*
* Store a usable private key on the card.
*/
static int
isoApplet_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *object,
sc_pkcs15_prkey_t *key)
{
sc_card_t *card = p15card->card;
sc_pkcs15_prkey_info_t* key_info = (sc_pkcs15_prkey_info_t *) object->data;
sc_file_t* privKeyFile=NULL;
sc_cardctl_isoApplet_import_key_t args;
int r;
char *p = NULL;
LOG_FUNC_CALLED(card->ctx);
/* Authentication stuff. */
r = sc_profile_get_file_by_path(profile, &key_info->path, &privKeyFile);
if(!privKeyFile)
{
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED);
}
r = sc_pkcs15init_authenticate(profile, p15card, privKeyFile, SC_AC_OP_CREATE_EF);
if(r < 0)
{
sc_file_free(privKeyFile);
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED);
}
sc_file_free(privKeyFile);
/* Key import. */
switch(object->type)
{
case SC_PKCS15_TYPE_PRKEY_RSA:
args.algorithm_ref = SC_ISOAPPLET_ALG_REF_RSA_GEN_2048;
break;
case SC_PKCS15_TYPE_PRKEY_EC:
p = key->u.ec.params.named_curve;
if(!p)
{
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
}
if(strncmp(p, "brainpoolP192r1", 15) == 0)
{
args.algorithm_ref = SC_ISOAPPLET_ALG_REF_EC_GEN_BRAINPOOLP192R1;
}
else if(strncmp(p, "prime256v1", 10) == 0)
{
args.algorithm_ref = SC_ISOAPPLET_ALG_REF_EC_GEN_PRIME256V1;
}
break;
default:
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
}
args.priv_key_ref = key_info->key_reference;
args.prkey = key;
r = sc_card_ctl(card, SC_CARDCTL_ISOAPPLET_IMPORT_KEY, &args);
if (r < 0)
{
sc_log(card->ctx, "%s: Error in card_ctl", sc_strerror(r));
LOG_FUNC_RETURN(card->ctx, r);
}
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
static struct sc_pkcs15init_operations sc_pkcs15init_isoApplet_operations =
{
NULL, /* erase_card */
NULL, /* init_card */
isoApplet_create_dir, /* create_dir */
NULL, /* create_domain */
isoApplet_select_pin_reference, /* pin_reference*/
isoApplet_create_pin, /* create_pin */
isoApplet_select_key_reference, /* key_reference */
isoApplet_create_key, /* create_key */
isoApplet_store_key, /* store_key */
isoApplet_generate_key, /* generate_key */
NULL, NULL, /* encode private/public key */
NULL, /* finalize */
NULL, /* delete_object */
NULL, NULL, NULL, NULL, NULL, /* pkcs15init emulation */
NULL, /* sanity_check*/
};
struct
sc_pkcs15init_operations *sc_pkcs15init_get_isoApplet_ops(void)
{
return &sc_pkcs15init_isoApplet_operations;
}

View File

@ -154,6 +154,7 @@ static struct profile_operations {
{ "westcos", (void *) sc_pkcs15init_get_westcos_ops },
{ "myeid", (void *) sc_pkcs15init_get_myeid_ops },
{ "sc-hsm", (void *) sc_pkcs15init_get_sc_hsm_ops },
{ "isoApplet", (void *) sc_pkcs15init_get_isoApplet_ops },
#ifdef ENABLE_OPENSSL
{ "authentic", (void *) sc_pkcs15init_get_authentic_ops },
{ "iasecc", (void *) sc_pkcs15init_get_iasecc_ops },