2014-08-28 12:29:16 +00:00
|
|
|
/*
|
|
|
|
* 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>
|
|
|
|
|
2015-02-22 22:08:18 +00:00
|
|
|
#include "asn1.h"
|
2014-08-28 12:29:16 +00:00
|
|
|
#include "cardctl.h"
|
2015-02-22 22:08:18 +00:00
|
|
|
#include "internal.h"
|
2014-08-28 12:29:16 +00:00
|
|
|
#include "log.h"
|
2015-02-22 22:08:18 +00:00
|
|
|
#include "opensc.h"
|
2014-08-28 12:29:16 +00:00
|
|
|
#include "pkcs15.h"
|
2015-02-22 22:08:18 +00:00
|
|
|
#include "types.h"
|
2014-08-28 12:29:16 +00:00
|
|
|
|
|
|
|
#define ISOAPPLET_ALG_REF_ECDSA 0x21
|
|
|
|
#define ISOAPPLET_ALG_REF_RSA_PAD_PKCS1 0x11
|
|
|
|
|
|
|
|
#define ISOAPPLET_API_VERSION_MAJOR 0x00
|
2015-03-25 10:21:29 +00:00
|
|
|
#define ISOAPPLET_API_VERSION_MINOR 0x06
|
2015-03-22 23:08:11 +00:00
|
|
|
|
2014-08-28 12:29:16 +00:00
|
|
|
#define ISOAPPLET_API_FEATURE_EXT_APDU 0x01
|
2015-03-22 23:08:11 +00:00
|
|
|
#define ISOAPPLET_API_FEATURE_SECURE_RANDOM 0x02
|
2015-03-22 23:09:30 +00:00
|
|
|
#define ISOAPPLET_API_FEATURE_ECC 0x04
|
2014-08-28 12:29:16 +00:00
|
|
|
|
|
|
|
#define ISOAPPLET_AID_LEN 12
|
2014-10-28 07:58:30 +00:00
|
|
|
static const u8 isoApplet_aid[] = {0xf2,0x76,0xa2,0x88,0xbc,0xfb,0xa6,0x9d,0x34,0xf3,0x10,0x01};
|
2014-08-28 12:29:16 +00:00
|
|
|
|
|
|
|
struct isoApplet_drv_data
|
|
|
|
{
|
2014-10-28 07:58:30 +00:00
|
|
|
/* Save the current algorithm reference
|
|
|
|
* (ISOAPPLET_ALG_REF_ECDSA, ISOAPPLET_ALG_REF_RSA_PAD_PKCS1)
|
2018-04-14 17:38:34 +00:00
|
|
|
* to be able to distinguish between RSA and ECC operations.
|
2014-10-28 07:58:30 +00:00
|
|
|
* If ECC is being used, the signatures generated by the card
|
|
|
|
* have to be modified. */
|
2014-08-28 12:29:16 +00:00
|
|
|
unsigned int sec_env_alg_ref;
|
2015-03-08 10:26:16 +00:00
|
|
|
unsigned int sec_env_ec_field_length;
|
2015-03-22 13:03:02 +00:00
|
|
|
unsigned int isoapplet_version;
|
2014-08-28 12:29:16 +00:00
|
|
|
};
|
|
|
|
#define DRVDATA(card) ((struct isoApplet_drv_data *) ((card)->drv_data))
|
|
|
|
|
2014-10-28 07:58:30 +00:00
|
|
|
/* Operations supported by the applet. */
|
2014-08-28 12:29:16 +00:00
|
|
|
static struct sc_card_operations isoApplet_ops;
|
2014-10-28 07:58:30 +00:00
|
|
|
|
|
|
|
/* A reference to the iso7816_* functions.
|
|
|
|
* Initialized in sc_get_driver. */
|
2014-08-28 12:29:16 +00:00
|
|
|
static const struct sc_card_operations *iso_ops = NULL;
|
2014-10-28 07:58:30 +00:00
|
|
|
|
|
|
|
/* The description of the driver. */
|
2014-08-28 12:29:16 +00:00
|
|
|
static struct sc_card_driver isoApplet_drv =
|
|
|
|
{
|
|
|
|
"Javacard with IsoApplet",
|
|
|
|
"isoApplet",
|
|
|
|
&isoApplet_ops,
|
|
|
|
NULL, 0, NULL
|
|
|
|
};
|
|
|
|
|
2015-03-22 13:49:59 +00:00
|
|
|
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. */
|
|
|
|
};
|
2014-08-28 12:29:16 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* SELECT an applet on the smartcard. (Not in the emulated filesystem.)
|
|
|
|
* The response will be written to resp.
|
|
|
|
*
|
2014-11-28 18:06:21 +00:00
|
|
|
* @param[in] card
|
|
|
|
* @param[in] aid The applet ID.
|
2018-04-14 17:38:34 +00:00
|
|
|
* @param[in] aid_len The length of aid.
|
2014-11-28 18:06:21 +00:00
|
|
|
* @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.
|
2014-08-28 12:29:16 +00:00
|
|
|
*
|
2014-11-28 18:06:21 +00:00
|
|
|
* @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.
|
2014-08-28 12:29:16 +00:00
|
|
|
*/
|
|
|
|
static int
|
2014-11-13 12:51:10 +00:00
|
|
|
isoApplet_select_applet(sc_card_t *card, const u8 *aid, const size_t aid_len, u8 *resp, size_t *resp_len)
|
2014-08-28 12:29:16 +00:00
|
|
|
{
|
|
|
|
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;
|
2014-11-13 12:51:10 +00:00
|
|
|
apdu.data = aid;
|
2014-08-28 12:29:16 +00:00
|
|
|
apdu.datalen = aid_len;
|
|
|
|
apdu.resp = resp;
|
|
|
|
apdu.resplen = *resp_len;
|
|
|
|
apdu.le = 0;
|
|
|
|
|
|
|
|
rv = sc_transmit_apdu(card, &apdu);
|
2018-04-14 17:38:34 +00:00
|
|
|
LOG_TEST_RET(ctx, rv, "APDU transmit failure.");
|
2014-08-28 12:29:16 +00:00
|
|
|
|
|
|
|
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
|
2014-10-28 07:58:30 +00:00
|
|
|
isoApplet_finish(sc_card_t *card)
|
2014-08-28 12:29:16 +00:00
|
|
|
{
|
|
|
|
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
|
2014-10-28 07:58:30 +00:00
|
|
|
isoApplet_match_card(sc_card_t *card)
|
2014-08-28 12:29:16 +00:00
|
|
|
{
|
|
|
|
size_t rlen = SC_MAX_APDU_BUFFER_SIZE;
|
|
|
|
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
|
|
|
|
int rv;
|
|
|
|
|
2014-10-28 07:58:30 +00:00
|
|
|
rv = isoApplet_select_applet(card, isoApplet_aid, ISOAPPLET_AID_LEN, rbuf, &rlen);
|
2014-08-28 12:29:16 +00:00
|
|
|
|
|
|
|
if(rv != SC_SUCCESS)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-10-28 07:58:30 +00:00
|
|
|
/* The IsoApplet should return an API version (major and minor) and a feature bitmap.
|
2015-03-22 13:03:02 +00:00
|
|
|
* We expect 3 bytes: MAJOR API version - MINOR API version - API feature bitmap.
|
2014-10-28 07:58:30 +00:00
|
|
|
* If applet does not return API version, versions 0x00 will match */
|
2015-03-22 13:03:02 +00:00
|
|
|
if(rlen < 3)
|
2014-08-28 12:29:16 +00:00
|
|
|
{
|
2015-03-22 13:03:02 +00:00
|
|
|
assert(sizeof(rbuf) >= 3);
|
|
|
|
memset(rbuf, 0x00, 3);
|
2014-08-28 12:29:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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. "
|
2014-11-28 17:14:24 +00:00
|
|
|
"API versions: Driver (%02X-%02X), applet (%02X-%02X). "
|
2014-08-28 12:29:16 +00:00
|
|
|
"Please update accordingly whenever possible.",
|
|
|
|
ISOAPPLET_API_VERSION_MAJOR, ISOAPPLET_API_VERSION_MINOR, rbuf[0], rbuf[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2014-10-28 07:58:30 +00:00
|
|
|
isoApplet_init(sc_card_t *card)
|
2014-08-28 12:29:16 +00:00
|
|
|
{
|
2015-03-22 13:49:59 +00:00
|
|
|
int i;
|
2014-08-28 12:29:16 +00:00
|
|
|
unsigned long flags = 0;
|
|
|
|
unsigned long ext_flags = 0;
|
2015-03-22 13:03:02 +00:00
|
|
|
size_t rlen = SC_MAX_APDU_BUFFER_SIZE;
|
|
|
|
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
|
2014-08-28 12:29:16 +00:00
|
|
|
struct isoApplet_drv_data *drvdata;
|
|
|
|
|
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
|
2015-03-08 10:26:16 +00:00
|
|
|
drvdata=calloc(1, sizeof(*drvdata));
|
2014-08-28 12:29:16 +00:00
|
|
|
if (!drvdata)
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY);
|
|
|
|
|
|
|
|
card->drv_data = drvdata;
|
|
|
|
card->cla = 0x00;
|
|
|
|
|
2015-03-22 13:03:02 +00:00
|
|
|
/* Obtain applet version and specific features */
|
2018-05-18 21:49:29 +00:00
|
|
|
if (0 > isoApplet_select_applet(card, isoApplet_aid, ISOAPPLET_AID_LEN, rbuf, &rlen)) {
|
2019-08-27 13:59:46 +00:00
|
|
|
free(card->drv_data);
|
|
|
|
card->drv_data = NULL;
|
2018-05-18 21:49:29 +00:00
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_CARD, "Error obtaining applet version.");
|
|
|
|
}
|
2015-03-22 13:03:02 +00:00
|
|
|
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;
|
2015-03-22 23:08:11 +00:00
|
|
|
if(rbuf[2] & ISOAPPLET_API_FEATURE_SECURE_RANDOM)
|
|
|
|
card->caps |= SC_CARD_CAP_RNG;
|
2015-03-22 23:09:30 +00:00
|
|
|
if(drvdata->isoapplet_version <= 0x0005 || rbuf[2] & ISOAPPLET_API_FEATURE_ECC)
|
2015-03-22 13:49:59 +00:00
|
|
|
{
|
2015-03-22 23:09:30 +00:00
|
|
|
/* 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;
|
2015-05-08 18:41:47 +00:00
|
|
|
flags |= SC_ALGORITHM_ECDSA_HASH_SHA1;
|
2015-03-22 23:09:30 +00:00
|
|
|
flags |= SC_ALGORITHM_ONBOARD_KEY_GEN;
|
2015-04-01 21:26:40 +00:00
|
|
|
ext_flags = SC_ALGORITHM_EXT_EC_UNCOMPRESES;
|
|
|
|
ext_flags |= SC_ALGORITHM_EXT_EC_NAMEDCURVE;
|
2015-03-22 23:09:30 +00:00
|
|
|
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);
|
|
|
|
}
|
2015-03-22 13:49:59 +00:00
|
|
|
}
|
2014-08-28 12:29:16 +00:00
|
|
|
|
|
|
|
/* RSA */
|
|
|
|
flags = 0;
|
|
|
|
/* Padding schemes: */
|
|
|
|
flags |= SC_ALGORITHM_RSA_PAD_PKCS1;
|
2014-10-28 10:17:45 +00:00
|
|
|
/* Hashes are to be done by the host for RSA */
|
2014-08-28 12:29:16 +00:00
|
|
|
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.
|
|
|
|
*
|
2014-11-28 18:06:21 +00:00
|
|
|
* @return The security condition byte. No restriction (0x00)
|
|
|
|
* if unknown operation.
|
2014-08-28 12:29:16 +00:00
|
|
|
*/
|
|
|
|
static u8
|
2014-10-28 07:58:30 +00:00
|
|
|
isoApplet_acl_to_security_condition_byte(const sc_acl_entry_t *entry)
|
2014-08-28 12:29:16 +00:00
|
|
|
{
|
|
|
|
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:
|
2014-11-28 18:06:21 +00:00
|
|
|
* 7 - ISO 7816-4 table 16 or 17
|
|
|
|
* 6 to 0 - ISO 7816-4 table 20
|
2014-08-28 12:29:16 +00:00
|
|
|
*/
|
|
|
|
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)
|
|
|
|
{
|
2014-11-28 18:06:21 +00:00
|
|
|
const int df_idx[8] = /* These are the SC operations. */
|
2014-08-28 12:29:16 +00:00
|
|
|
{
|
|
|
|
0, /* Reserved. */
|
2014-11-28 18:06:21 +00:00
|
|
|
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 */
|
2014-08-28 12:29:16 +00:00
|
|
|
};
|
|
|
|
for(i=0; i<8; i++)
|
|
|
|
{
|
|
|
|
idx[i] = df_idx[i];
|
|
|
|
}
|
|
|
|
}
|
2014-11-28 18:06:21 +00:00
|
|
|
else /* EF */
|
2014-08-28 12:29:16 +00:00
|
|
|
{
|
|
|
|
const int ef_idx[8] =
|
|
|
|
{
|
|
|
|
0, /* Reserved. */
|
2014-11-28 18:06:21 +00:00
|
|
|
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 */
|
2014-08-28 12:29:16 +00:00
|
|
|
};
|
|
|
|
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]);
|
2014-10-28 07:58:30 +00:00
|
|
|
access_buf[i] = isoApplet_acl_to_security_condition_byte(entry);
|
2014-08-28 12:29:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2014-10-28 07:58:30 +00:00
|
|
|
* Add an ACL entry to the OpenSC file struct, according to the operation
|
2014-08-28 12:29:16 +00:00
|
|
|
* and the saByte (Encoded according to IsoApplet FCI proprietary security
|
|
|
|
* information, see also ISO 7816-4 table 20).
|
|
|
|
*
|
2014-11-28 18:06:21 +00:00
|
|
|
* @param[in,out] file
|
|
|
|
* @param[in] operation The OpenSC operation.
|
|
|
|
* @param[in] saByte The security condition byte returned by the applet.
|
2014-08-28 12:29:16 +00:00
|
|
|
*/
|
|
|
|
static int
|
2014-10-28 07:58:30 +00:00
|
|
|
isoApplet_add_sa_to_acl(sc_file_t *file, unsigned int operation, u8 saByte)
|
2014-08-28 12:29:16 +00:00
|
|
|
{
|
|
|
|
int r;
|
2014-10-28 07:58:30 +00:00
|
|
|
|
2014-08-28 12:29:16 +00:00
|
|
|
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)
|
|
|
|
{
|
2014-10-28 07:58:30 +00:00
|
|
|
r = isoApplet_add_sa_to_acl(file, SC_AC_OP_DELETE_SELF, sa[1]);
|
2014-08-28 12:29:16 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Error adding ACL entry.");
|
2014-10-28 07:58:30 +00:00
|
|
|
r = isoApplet_add_sa_to_acl(file, SC_AC_OP_LOCK, sa[2]);
|
2014-08-28 12:29:16 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Error adding ACL entry.");
|
2014-10-28 07:58:30 +00:00
|
|
|
r = isoApplet_add_sa_to_acl(file, SC_AC_OP_ACTIVATE, sa[3]);
|
2014-08-28 12:29:16 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Error adding ACL entry.");
|
2014-10-28 07:58:30 +00:00
|
|
|
r = isoApplet_add_sa_to_acl(file, SC_AC_OP_DEACTIVATE, sa[4]);
|
2014-08-28 12:29:16 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Error adding ACL entry.");
|
2014-10-28 07:58:30 +00:00
|
|
|
r = isoApplet_add_sa_to_acl(file, SC_AC_OP_CREATE_DF, sa[5]);
|
2014-08-28 12:29:16 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Error adding ACL entry.");
|
2014-10-28 07:58:30 +00:00
|
|
|
r = isoApplet_add_sa_to_acl(file, SC_AC_OP_CREATE_EF, sa[6]);
|
2014-08-28 12:29:16 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Error adding ACL entry.");
|
2014-10-28 07:58:30 +00:00
|
|
|
r = isoApplet_add_sa_to_acl(file, SC_AC_OP_DELETE, sa[7]);
|
2014-08-28 12:29:16 +00:00
|
|
|
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)
|
|
|
|
{
|
2014-10-28 07:58:30 +00:00
|
|
|
r = isoApplet_add_sa_to_acl(file, SC_AC_OP_DELETE_SELF, sa[1]);
|
2014-08-28 12:29:16 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Error adding ACL entry.");
|
2014-10-28 07:58:30 +00:00
|
|
|
r = isoApplet_add_sa_to_acl(file, SC_AC_OP_LOCK, sa[2]);
|
2014-08-28 12:29:16 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Error adding ACL entry.");
|
2014-10-28 07:58:30 +00:00
|
|
|
r = isoApplet_add_sa_to_acl(file, SC_AC_OP_ACTIVATE, sa[3]);
|
2014-08-28 12:29:16 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Error adding ACL entry.");
|
2014-10-28 07:58:30 +00:00
|
|
|
r = isoApplet_add_sa_to_acl(file, SC_AC_OP_DEACTIVATE, sa[4]);
|
2014-08-28 12:29:16 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Error adding ACL entry.");
|
2014-10-28 07:58:30 +00:00
|
|
|
r = isoApplet_add_sa_to_acl(file, SC_AC_OP_WRITE, sa[5]);
|
2014-08-28 12:29:16 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Error adding ACL entry.");
|
2014-10-28 07:58:30 +00:00
|
|
|
r = isoApplet_add_sa_to_acl(file, SC_AC_OP_UPDATE, sa[6]);
|
2014-08-28 12:29:16 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Error adding ACL entry.");
|
2014-10-28 07:58:30 +00:00
|
|
|
r = isoApplet_add_sa_to_acl(file, SC_AC_OP_READ, sa[7]);
|
2014-08-28 12:29:16 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Error adding ACL entry.");
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
|
|
|
|
}
|
|
|
|
|
2014-10-28 07:58:30 +00:00
|
|
|
/*
|
2018-04-14 17:38:34 +00:00
|
|
|
* @brief Encode the EC parameters as a concatenation of TLV entries.
|
2014-10-28 07:58:30 +00:00
|
|
|
*
|
|
|
|
* The format is:
|
2014-11-28 18:06:21 +00:00
|
|
|
* 81 - prime
|
|
|
|
* 82 - coefficient A
|
|
|
|
* 83 - coefficient B
|
|
|
|
* 84 - base point G
|
|
|
|
* 85 - order
|
|
|
|
* 87 - cofactor
|
2014-10-28 07:58:30 +00:00
|
|
|
*
|
2014-11-28 18:06:21 +00:00
|
|
|
* @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).
|
2014-10-28 07:58:30 +00:00
|
|
|
*/
|
2014-08-28 12:29:16 +00:00
|
|
|
static int
|
2014-10-28 07:58:30 +00:00
|
|
|
isoApplet_put_ec_params(sc_card_t *card, sc_cardctl_isoApplet_ec_parameters_t *params, u8 *out, size_t out_len, u8 **ptr)
|
2014-08-28 12:29:16 +00:00
|
|
|
{
|
2014-10-28 07:58:30 +00:00
|
|
|
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.");
|
|
|
|
}
|
|
|
|
|
2014-11-19 13:04:40 +00:00
|
|
|
if(out == NULL || out_len == 0)
|
|
|
|
{
|
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Error: Parameter out is NULL or outlen is zero.");
|
|
|
|
}
|
|
|
|
|
2014-10-28 07:58:30 +00:00
|
|
|
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;
|
2014-08-28 12:29:16 +00:00
|
|
|
sc_apdu_t apdu;
|
|
|
|
u8 rbuf[SC_MAX_EXT_APDU_BUFFER_SIZE];
|
2015-02-19 20:10:38 +00:00
|
|
|
u8 sbuf[SC_MAX_EXT_APDU_BUFFER_SIZE];
|
2014-08-28 12:29:16 +00:00
|
|
|
u8 *p;
|
2014-10-28 07:58:30 +00:00
|
|
|
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;
|
2014-08-28 12:29:16 +00:00
|
|
|
|
|
|
|
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:
|
2014-11-28 18:06:21 +00:00
|
|
|
* 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
|
2018-04-14 17:38:34 +00:00
|
|
|
* card will send SW_BYTES_REMAINING, OpenSC will automatically do a
|
2014-11-28 18:06:21 +00:00
|
|
|
* GET RESPONSE to get the remaining data, and will append it to the data
|
|
|
|
* buffer. */
|
2014-10-28 07:58:30 +00:00
|
|
|
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);
|
|
|
|
}
|
2014-08-28 12:29:16 +00:00
|
|
|
|
|
|
|
apdu.resp = rbuf;
|
2014-10-28 07:58:30 +00:00
|
|
|
apdu.resplen = sizeof(rbuf);
|
2014-08-28 12:29:16 +00:00
|
|
|
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);
|
2014-11-28 17:14:24 +00:00
|
|
|
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.");
|
|
|
|
}
|
2014-08-28 12:29:16 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Card returned error");
|
|
|
|
|
|
|
|
/* Parse the public key / response. */
|
2014-10-28 07:58:30 +00:00
|
|
|
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.");
|
|
|
|
}
|
|
|
|
|
2014-08-28 12:29:16 +00:00
|
|
|
switch(args->algorithm_ref)
|
|
|
|
{
|
|
|
|
|
|
|
|
case SC_ISOAPPLET_ALG_REF_RSA_GEN_2048:
|
|
|
|
/* Search for the modulus tag (81). */
|
2014-10-28 07:58:30 +00:00
|
|
|
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)
|
2014-08-28 12:29:16 +00:00
|
|
|
{
|
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid modulus.");
|
|
|
|
}
|
2014-10-28 07:58:30 +00:00
|
|
|
if(inner_tag_len > args->pubkey.rsa.modulus.len)
|
2014-08-28 12:29:16 +00:00
|
|
|
{
|
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL);
|
|
|
|
}
|
2014-10-28 07:58:30 +00:00
|
|
|
memcpy(args->pubkey.rsa.modulus.value, inner_tag_value, inner_tag_len);
|
|
|
|
args->pubkey.rsa.modulus.len = inner_tag_len;
|
2014-08-28 12:29:16 +00:00
|
|
|
|
|
|
|
/* Exponent tag (82) */
|
2014-10-28 07:58:30 +00:00
|
|
|
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)
|
2014-08-28 12:29:16 +00:00
|
|
|
{
|
2014-10-28 07:58:30 +00:00
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid exponent.");
|
2014-08-28 12:29:16 +00:00
|
|
|
}
|
2014-10-28 07:58:30 +00:00
|
|
|
if(inner_tag_len > args->pubkey.rsa.exponent.len)
|
2014-08-28 12:29:16 +00:00
|
|
|
{
|
2014-10-28 07:58:30 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL);
|
2014-08-28 12:29:16 +00:00
|
|
|
}
|
2014-10-28 07:58:30 +00:00
|
|
|
if(memcmp(inner_tag_value, "\x01\x00\x01", 3) != 0)
|
2014-08-28 12:29:16 +00:00
|
|
|
{
|
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_INCOMPATIBLE_KEY,
|
|
|
|
"Key generation error: Unexpected public key exponent.");
|
|
|
|
}
|
2014-10-28 07:58:30 +00:00
|
|
|
memcpy(args->pubkey.rsa.exponent.value, inner_tag_value, inner_tag_len);
|
|
|
|
args->pubkey.rsa.exponent.len = inner_tag_len;
|
2014-08-28 12:29:16 +00:00
|
|
|
p = NULL;
|
|
|
|
break;
|
|
|
|
|
2014-10-28 07:58:30 +00:00
|
|
|
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);
|
2014-08-28 12:29:16 +00:00
|
|
|
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "Unable to parse public key: Unsupported algorithm.");
|
2014-10-28 07:58:30 +00:00
|
|
|
}/* switch */
|
2014-08-28 12:29:16 +00:00
|
|
|
|
|
|
|
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
|
2014-11-28 18:06:21 +00:00
|
|
|
* @param rsa The RSA private key to import.
|
2014-08-28 12:29:16 +00:00
|
|
|
*
|
2014-11-28 18:06:21 +00:00
|
|
|
* @return SC_ERROR_INVALID_ARGUMENTS: The RSA key does not contain CRT fields.
|
|
|
|
* other errors: Transmit errors / errors returned by card.
|
2014-08-28 12:29:16 +00:00
|
|
|
*/
|
|
|
|
static int
|
2014-10-28 07:58:30 +00:00
|
|
|
isoApplet_put_data_prkey_rsa(sc_card_t *card, sc_cardctl_isoApplet_import_key_t *args)
|
2014-08-28 12:29:16 +00:00
|
|
|
{
|
|
|
|
sc_apdu_t apdu;
|
2014-11-19 13:04:40 +00:00
|
|
|
u8 sbuf[SC_MAX_EXT_APDU_BUFFER_SIZE];
|
2014-08-28 12:29:16 +00:00
|
|
|
u8 *p = NULL;
|
|
|
|
int r;
|
|
|
|
size_t tags_len;
|
|
|
|
|
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
|
2014-10-28 07:58:30 +00:00
|
|
|
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)
|
2014-08-28 12:29:16 +00:00
|
|
|
{
|
2014-10-28 07:58:30 +00:00
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "RSA key is missing information.");
|
2014-08-28 12:29:16 +00:00
|
|
|
}
|
|
|
|
|
2014-11-19 13:04:40 +00:00
|
|
|
/* 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. */
|
2014-08-28 12:29:16 +00:00
|
|
|
p = sbuf;
|
2014-11-19 13:04:40 +00:00
|
|
|
r = sc_asn1_put_tag(0x7F48, NULL, tags_len, p, sizeof(sbuf), &p);
|
|
|
|
LOG_TEST_RET(card->ctx, r, "Error handling TLV.");
|
2014-08-28 12:29:16 +00:00
|
|
|
|
2014-11-19 13:04:40 +00:00
|
|
|
/* Write inner tags. */
|
2014-08-28 12:29:16 +00:00
|
|
|
/* p */
|
2014-10-28 07:58:30 +00:00
|
|
|
r = sc_asn1_put_tag(0x92, args->privkey.rsa.p.value, args->privkey.rsa.p.len, p, sizeof(sbuf) - (p - sbuf), &p);
|
2014-11-28 17:06:13 +00:00
|
|
|
if(r < 0)
|
|
|
|
goto out;
|
2014-08-28 12:29:16 +00:00
|
|
|
/* q */
|
2014-10-28 07:58:30 +00:00
|
|
|
r = sc_asn1_put_tag(0x93, args->privkey.rsa.q.value, args->privkey.rsa.q.len, p, sizeof(sbuf) - (p - sbuf), &p);
|
2014-11-28 17:06:13 +00:00
|
|
|
if(r < 0)
|
|
|
|
goto out;
|
2014-08-28 12:29:16 +00:00
|
|
|
/* 1/q mod p */
|
2014-10-28 07:58:30 +00:00
|
|
|
r = sc_asn1_put_tag(0x94, args->privkey.rsa.iqmp.value, args->privkey.rsa.iqmp.len, p, sizeof(sbuf) - (p - sbuf), &p);
|
2014-11-28 17:06:13 +00:00
|
|
|
if(r < 0)
|
|
|
|
goto out;
|
2014-08-28 12:29:16 +00:00
|
|
|
/* d mod (p-1) */
|
2014-10-28 07:58:30 +00:00
|
|
|
r = sc_asn1_put_tag(0x95, args->privkey.rsa.dmp1.value, args->privkey.rsa.dmp1.len, p, sizeof(sbuf) - (p - sbuf), &p);
|
2014-11-28 17:06:13 +00:00
|
|
|
if(r < 0)
|
|
|
|
goto out;
|
2014-08-28 12:29:16 +00:00
|
|
|
/* d mod (q-1) */
|
2014-10-28 07:58:30 +00:00
|
|
|
r = sc_asn1_put_tag(0x96, args->privkey.rsa.dmq1.value, args->privkey.rsa.dmq1.len, p, sizeof(sbuf) - (p - sbuf), &p);
|
2014-11-28 17:06:13 +00:00
|
|
|
if(r < 0)
|
|
|
|
goto out;
|
2014-11-19 13:04:40 +00:00
|
|
|
|
|
|
|
/* Send to card, using chaining or extended APDUs. */
|
|
|
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xDB, 0x3F, 0xFF);
|
2014-08-28 12:29:16 +00:00
|
|
|
apdu.data = sbuf;
|
|
|
|
apdu.datalen = p - sbuf;
|
|
|
|
apdu.lc = p - sbuf;
|
2014-11-19 13:04:40 +00:00
|
|
|
if ((card->caps & SC_CARD_CAP_APDU_EXT) == 0)
|
2014-10-21 14:05:06 +00:00
|
|
|
{
|
2014-11-19 13:04:40 +00:00
|
|
|
/* The lower layers will automatically do chaining */
|
|
|
|
apdu.flags |= SC_APDU_FLAGS_CHAINING;
|
2014-10-21 14:05:06 +00:00
|
|
|
}
|
2014-11-19 13:04:40 +00:00
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2014-11-28 17:06:13 +00:00
|
|
|
if(r < 0)
|
|
|
|
goto out;
|
2014-08-28 12:29:16 +00:00
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2014-11-28 17:14:24 +00:00
|
|
|
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.");
|
|
|
|
}
|
2014-11-28 17:06:13 +00:00
|
|
|
if(r < 0)
|
|
|
|
goto out;
|
2014-08-28 12:29:16 +00:00
|
|
|
|
2014-11-28 17:06:13 +00:00
|
|
|
r = SC_SUCCESS;
|
|
|
|
out:
|
|
|
|
sc_mem_clear(sbuf, sizeof(sbuf));
|
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
2014-08-28 12:29:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @brief Use PUT DATA to import a private EC key.
|
|
|
|
*
|
2014-10-28 07:58:30 +00:00
|
|
|
* Format of transmitted data:
|
2014-11-28 18:06:21 +00:00
|
|
|
* 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)
|
2014-08-28 12:29:16 +00:00
|
|
|
*
|
|
|
|
* @param card
|
2014-11-28 18:06:21 +00:00
|
|
|
* @param ec The EC private key to import.
|
2014-08-28 12:29:16 +00:00
|
|
|
*
|
2014-11-28 18:06:21 +00:00
|
|
|
* @return SC_ERROR_INVALID_ARGUMENTS: Curve parameters or private component is missing.
|
|
|
|
* other errors: Transmit errors / errors returned by card.
|
|
|
|
* ASN1 errors.
|
2014-08-28 12:29:16 +00:00
|
|
|
*/
|
|
|
|
static int
|
2014-10-28 07:58:30 +00:00
|
|
|
isoApplet_put_data_prkey_ec(sc_card_t *card, sc_cardctl_isoApplet_import_key_t *args)
|
2014-08-28 12:29:16 +00:00
|
|
|
{
|
|
|
|
sc_apdu_t apdu;
|
|
|
|
u8 sbuf[SC_MAX_EXT_APDU_BUFFER_SIZE];
|
|
|
|
int r;
|
2014-10-28 07:58:30 +00:00
|
|
|
u8 *p;
|
|
|
|
size_t tags_len;
|
2014-08-28 12:29:16 +00:00
|
|
|
|
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
|
2014-10-28 07:58:30 +00:00
|
|
|
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
|
|
|
|
)
|
2014-08-28 12:29:16 +00:00
|
|
|
{
|
2014-10-28 07:58:30 +00:00
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Missing information about EC private key.");
|
2014-08-28 12:29:16 +00:00
|
|
|
}
|
|
|
|
|
2014-11-19 13:04:40 +00:00
|
|
|
/* 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. */
|
2014-10-28 07:58:30 +00:00
|
|
|
p = sbuf;
|
2014-11-19 13:04:40 +00:00
|
|
|
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. */
|
2014-10-28 07:58:30 +00:00
|
|
|
r = isoApplet_put_ec_params(card, &args->privkey.ec.params, p, sizeof(sbuf) - (p - sbuf), &p);
|
2014-11-28 17:06:13 +00:00
|
|
|
if(r < 0)
|
|
|
|
{
|
|
|
|
sc_log(card->ctx, "Error composing EC params.");
|
|
|
|
goto out;
|
|
|
|
}
|
2014-10-28 07:58:30 +00:00
|
|
|
r = sc_asn1_put_tag(0x88, args->privkey.ec.privateD.value, args->privkey.ec.privateD.len, p, sizeof(sbuf) - (p - sbuf), &p);
|
2014-11-28 17:06:13 +00:00
|
|
|
if(r < 0)
|
|
|
|
goto out;
|
2014-08-28 12:29:16 +00:00
|
|
|
|
|
|
|
/* Send to card. */
|
2014-10-28 07:58:30 +00:00
|
|
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xDB, 0x3F, 0xFF);
|
|
|
|
apdu.lc = p - sbuf;
|
|
|
|
apdu.datalen = p - sbuf;
|
2014-08-28 12:29:16 +00:00
|
|
|
apdu.data = sbuf;
|
2019-07-20 21:04:57 +00:00
|
|
|
if ((apdu.datalen > 255) && !(card->caps & SC_CARD_CAP_APDU_EXT))
|
|
|
|
{
|
|
|
|
apdu.flags |= SC_APDU_FLAGS_CHAINING;
|
|
|
|
}
|
2014-08-28 12:29:16 +00:00
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2014-11-28 17:06:13 +00:00
|
|
|
if(r < 0)
|
|
|
|
{
|
|
|
|
sc_log(card->ctx, "APDU transmit failed");
|
|
|
|
goto out;
|
|
|
|
}
|
2014-08-28 12:29:16 +00:00
|
|
|
|
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
|
|
|
if(apdu.sw1 == 0x6D && apdu.sw2 == 0x00)
|
|
|
|
{
|
2014-11-28 17:14:24 +00:00
|
|
|
sc_log(card->ctx, "The applet returned that the PUT DATA instruction byte is not supported. "
|
2014-08-28 12:29:16 +00:00
|
|
|
"If you are using an older applet version and are trying to import keys, please update your applet first.");
|
|
|
|
}
|
2014-11-28 17:14:24 +00:00
|
|
|
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.");
|
|
|
|
}
|
2014-11-28 17:06:13 +00:00
|
|
|
if(r < 0)
|
|
|
|
{
|
|
|
|
sc_log(card->ctx, "Card returned error");
|
|
|
|
goto out;
|
|
|
|
}
|
2014-08-28 12:29:16 +00:00
|
|
|
|
2014-11-28 17:06:13 +00:00
|
|
|
r = SC_SUCCESS;
|
|
|
|
out:
|
|
|
|
sc_mem_clear(sbuf, sizeof(sbuf));
|
2014-08-28 12:29:16 +00:00
|
|
|
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
|
2020-08-29 08:34:51 +00:00
|
|
|
* GENERATE ASYMMETRIC KEY PAIR).
|
2014-08-28 12:29:16 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* 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:
|
2014-10-28 07:58:30 +00:00
|
|
|
r = isoApplet_put_data_prkey_rsa(card, args);
|
2014-08-28 12:29:16 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Error in PUT DATA.");
|
|
|
|
break;
|
|
|
|
|
2014-10-28 07:58:30 +00:00
|
|
|
case SC_ISOAPPLET_ALG_REF_EC_GEN:
|
|
|
|
r = isoApplet_put_data_prkey_ec(card, args);
|
2014-08-28 12:29:16 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Error in PUT DATA.");
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2018-04-14 17:38:34 +00:00
|
|
|
LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "Unknown algorithm reference.");
|
2014-08-28 12:29:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2015-09-18 14:34:31 +00:00
|
|
|
int r;
|
2014-08-28 12:29:16 +00:00
|
|
|
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;
|
2015-03-08 10:26:16 +00:00
|
|
|
drvdata->sec_env_ec_field_length = env->algorithm_ref;
|
2014-08-28 12:29:16 +00:00
|
|
|
}
|
|
|
|
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.");
|
|
|
|
}
|
|
|
|
|
2014-11-28 18:06:21 +00:00
|
|
|
*p++ = 0x80; /* algorithm reference */
|
2014-08-28 12:29:16 +00:00
|
|
|
*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)
|
|
|
|
{
|
2018-02-21 13:07:53 +00:00
|
|
|
if (env->flags & SC_SEC_ENV_KEY_REF_SYMMETRIC)
|
2014-08-28 12:29:16 +00:00
|
|
|
*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);
|
2015-09-18 14:34:31 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
|
2014-08-28 12:29:16 +00:00
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2015-09-18 14:34:31 +00:00
|
|
|
LOG_TEST_RET(card->ctx, r, "Card returned error");
|
2014-08-28 12:29:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
isoApplet_compute_signature(struct sc_card *card,
|
2014-10-28 07:58:30 +00:00
|
|
|
const u8 *data, size_t datalen,
|
|
|
|
u8 *out, size_t outlen)
|
2014-08-28 12:29:16 +00:00
|
|
|
{
|
2015-03-08 10:26:16 +00:00
|
|
|
struct sc_context *ctx = card->ctx;
|
2014-08-28 12:29:16 +00:00
|
|
|
struct isoApplet_drv_data *drvdata = DRVDATA(card);
|
|
|
|
int r;
|
|
|
|
|
2015-03-08 10:26:16 +00:00
|
|
|
LOG_FUNC_CALLED(ctx);
|
2014-08-28 12:29:16 +00:00
|
|
|
|
|
|
|
r = iso_ops->compute_signature(card, data, datalen, out, outlen);
|
|
|
|
if(r < 0)
|
|
|
|
{
|
2015-03-08 10:26:16 +00:00
|
|
|
LOG_FUNC_RETURN(ctx, r);
|
2014-08-28 12:29:16 +00:00
|
|
|
}
|
|
|
|
|
2015-03-08 10:26:16 +00:00
|
|
|
/* 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. */
|
2014-08-28 12:29:16 +00:00
|
|
|
if(drvdata->sec_env_alg_ref == ISOAPPLET_ALG_REF_ECDSA)
|
|
|
|
{
|
2015-02-17 19:23:21 +00:00
|
|
|
u8* p = NULL;
|
2015-03-29 11:08:01 +00:00
|
|
|
size_t len = (drvdata->sec_env_ec_field_length + 7) / 8 * 2;
|
2015-03-08 10:26:16 +00:00
|
|
|
|
|
|
|
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);
|
2015-02-17 19:23:21 +00:00
|
|
|
|
2015-03-08 10:26:16 +00:00
|
|
|
r = sc_asn1_sig_value_sequence_to_rs(ctx, out, r, p, len);
|
|
|
|
if (!r) {
|
|
|
|
memcpy(out, p, len);
|
|
|
|
r = len;
|
2014-08-28 12:29:16 +00:00
|
|
|
}
|
2015-03-08 10:26:16 +00:00
|
|
|
|
2015-02-17 19:23:21 +00:00
|
|
|
free(p);
|
2014-08-28 12:29:16 +00:00
|
|
|
}
|
2015-03-08 10:26:16 +00:00
|
|
|
LOG_FUNC_RETURN(ctx, r);
|
2014-08-28 12:29:16 +00:00
|
|
|
}
|
|
|
|
|
2015-03-22 23:08:11 +00:00
|
|
|
static int
|
|
|
|
isoApplet_get_challenge(struct sc_card *card, u8 *rnd, size_t len)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
|
2018-01-23 12:53:23 +00:00
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
2015-03-22 23:08:11 +00:00
|
|
|
|
2018-01-23 12:53:23 +00:00
|
|
|
if(card->caps & SC_CARD_CAP_RNG) {
|
2015-03-22 23:08:11 +00:00
|
|
|
r = iso_ops->get_challenge(card, rnd, len);
|
|
|
|
} else {
|
|
|
|
r = SC_ERROR_NOT_SUPPORTED;
|
|
|
|
}
|
2018-01-23 12:53:23 +00:00
|
|
|
|
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
2015-03-22 23:08:11 +00:00
|
|
|
}
|
|
|
|
|
2018-01-16 20:03:30 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2014-08-28 12:29:16 +00:00
|
|
|
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;
|
2015-03-22 23:08:11 +00:00
|
|
|
isoApplet_ops.get_challenge = isoApplet_get_challenge;
|
2018-01-16 20:03:30 +00:00
|
|
|
isoApplet_ops.card_reader_lock_obtained = isoApplet_card_reader_lock_obtained;
|
2014-08-28 12:29:16 +00:00
|
|
|
|
|
|
|
/* 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();
|
|
|
|
}
|