Added initial support for SetCOS 4.4 cards
git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@2291 c6295689-39f2-0310-b995-f0e70906c6a9
This commit is contained in:
parent
abf3bf9549
commit
3439f9cdd7
|
@ -3,6 +3,7 @@
|
||||||
*
|
*
|
||||||
* Copyright (C) 2001, 2002 Juha Yrjölä <juha.yrjola@iki.fi>
|
* Copyright (C) 2001, 2002 Juha Yrjölä <juha.yrjola@iki.fi>
|
||||||
* Copyright (C) 2005 Antti Tapaninen <aet@cc.hut.fi>
|
* Copyright (C) 2005 Antti Tapaninen <aet@cc.hut.fi>
|
||||||
|
* Copyright (C) 2005 Zetes
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
@ -20,6 +21,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
#include "cardctl.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
@ -41,9 +43,19 @@ static struct sc_atr_table setcos_atrs[] = {
|
||||||
{ "3b:64:00:00:80:62:00:51", "ff:ff:ff:ff:ff:ff:f0:ff", NULL, SC_CARD_TYPE_SETCOS_FINEID_V2, 0 },
|
{ "3b:64:00:00:80:62:00:51", "ff:ff:ff:ff:ff:ff:f0:ff", NULL, SC_CARD_TYPE_SETCOS_FINEID_V2, 0 },
|
||||||
/* FINEID 2264 (EIDApplet/7816-15, OPK/EMV/AVANT) */
|
/* FINEID 2264 (EIDApplet/7816-15, OPK/EMV/AVANT) */
|
||||||
{ "3b:6e:00:00:00:62:00:00:57:41:56:41:4e:54:10:81:90:00", NULL, NULL, SC_CARD_TYPE_SETCOS_FINEID_V2, 0 },
|
{ "3b:6e:00:00:00:62:00:00:57:41:56:41:4e:54:10:81:90:00", NULL, NULL, SC_CARD_TYPE_SETCOS_FINEID_V2, 0 },
|
||||||
|
|
||||||
|
/* Setcos 4.4.1 */
|
||||||
|
{ "3b:9f:94:80:1f:c3:00:68:11:44:05:01:46:49:53:45:31:c8:00:00:00:00", "ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00", NULL, SC_CARD_TYPE_SETCOS_44, 0 },
|
||||||
{ NULL }
|
{ NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Setcos 4.4 Life Cycle Status Integer */
|
||||||
|
#define SETEC_LCSI_CREATE 0x01
|
||||||
|
#define SETEC_LCSI_INIT 0x03
|
||||||
|
#define SETEC_LCSI_ACTIVATED 0x07
|
||||||
|
#define SETEC_LCSI_DEACTIVATE 0x06
|
||||||
|
#define SETEC_LCSI_TEMINATE 0x0F /* MF only */
|
||||||
|
|
||||||
static struct sc_card_operations setcos_ops;
|
static struct sc_card_operations setcos_ops;
|
||||||
static struct sc_card_driver setcos_drv = {
|
static struct sc_card_driver setcos_drv = {
|
||||||
"Setec cards",
|
"Setec cards",
|
||||||
|
@ -132,6 +144,11 @@ static int setcos_init(sc_card_t *card)
|
||||||
if (card->flags & SC_CARD_FLAG_RNG)
|
if (card->flags & SC_CARD_FLAG_RNG)
|
||||||
card->caps |= SC_CARD_CAP_RNG;
|
card->caps |= SC_CARD_CAP_RNG;
|
||||||
break;
|
break;
|
||||||
|
case SC_CARD_TYPE_SETCOS_44:
|
||||||
|
card->cla = 0x00;
|
||||||
|
card->caps |= SC_CARD_CAP_USE_FCI_AC;
|
||||||
|
card->caps |= SC_CARD_CAP_RNG;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
/* XXX: Get SetCOS version */
|
/* XXX: Get SetCOS version */
|
||||||
card->cla = 0x80; /* SetCOS 4.3.x */
|
card->cla = 0x80; /* SetCOS 4.3.x */
|
||||||
|
@ -153,12 +170,125 @@ static int setcos_init(sc_card_t *card)
|
||||||
_sc_card_add_rsa_alg(card, 1024, flags, 0);
|
_sc_card_add_rsa_alg(card, 1024, flags, 0);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case SC_CARD_TYPE_SETCOS_44:
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_PAD_PKCS1;
|
||||||
|
flags |= SC_ALGORITHM_RSA_HASH_NONE | SC_ALGORITHM_RSA_HASH_SHA1;
|
||||||
|
|
||||||
|
_sc_card_add_rsa_alg(card, 1024, flags, 0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct sc_card_operations *iso_ops = NULL;
|
static const struct sc_card_operations *iso_ops = NULL;
|
||||||
|
|
||||||
|
static int setcos_construct_fci_44(sc_card_t *card, const sc_file_t *file, u8 *out, size_t *outlen)
|
||||||
|
{
|
||||||
|
u8 *p = out;
|
||||||
|
u8 buf[64];
|
||||||
|
u8 *pin_key_info;
|
||||||
|
|
||||||
|
/* Command */
|
||||||
|
*p++ = 0x6F;
|
||||||
|
p++;
|
||||||
|
|
||||||
|
/* Length */
|
||||||
|
buf[0] = (file->size >> 8) & 0xFF;
|
||||||
|
buf[1] = file->size & 0xFF;
|
||||||
|
sc_asn1_put_tag(0x81, buf, 2, p, 16, &p);
|
||||||
|
|
||||||
|
/* Type */
|
||||||
|
if (file->type_attr_len) {
|
||||||
|
memcpy(buf, file->type_attr, file->type_attr_len);
|
||||||
|
sc_asn1_put_tag(0x82, buf, file->type_attr_len, p, 16, &p);
|
||||||
|
} else {
|
||||||
|
u8 bLen = 1;
|
||||||
|
|
||||||
|
buf[0] = file->shareable ? 0x40 : 0;
|
||||||
|
switch (file->type) {
|
||||||
|
case SC_FILE_TYPE_INTERNAL_EF: /* RSA keyfile */
|
||||||
|
buf[0] = 0x11;
|
||||||
|
break;
|
||||||
|
case SC_FILE_TYPE_WORKING_EF:
|
||||||
|
if (file->ef_structure == 0x22) { /* pin-file */
|
||||||
|
/* ISF key-file, Setcos V4.4 specific */
|
||||||
|
bLen = 5;
|
||||||
|
buf[0] = 0x0A; /* EF linear fixed EF for ISF keys */
|
||||||
|
buf[1] = 0x41; /* fixed */
|
||||||
|
buf[2] = file->record_length >> 8; /* 2 byte record length */
|
||||||
|
buf[3] = file->record_length & 0xFF;
|
||||||
|
buf[4] = file->size / file->record_length; /* record count */
|
||||||
|
} else {
|
||||||
|
buf[0] |= file->ef_structure & 7; /* set file-type, only for EF, not for DF objects */
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SC_FILE_TYPE_DF:
|
||||||
|
buf[0] = 0x38;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SC_ERROR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
sc_asn1_put_tag(0x82, buf, bLen, p, 16, &p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* File-id */
|
||||||
|
buf[0] = (file->id >> 8) & 0xFF;
|
||||||
|
buf[1] = file->id & 0xFF;
|
||||||
|
sc_asn1_put_tag(0x83, buf, 2, p, 16, &p);
|
||||||
|
|
||||||
|
/* file state */
|
||||||
|
if (file->prop_attr_len) {
|
||||||
|
memcpy(buf, file->prop_attr, file->prop_attr_len);
|
||||||
|
sc_asn1_put_tag(0x8A, buf, file->prop_attr_len, p, 18, &p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Access control */
|
||||||
|
memcpy(buf, file->sec_attr, file->sec_attr_len);
|
||||||
|
sc_asn1_put_tag(0x86, buf, file->sec_attr_len, p, 18, &p);
|
||||||
|
|
||||||
|
switch (file->type) {
|
||||||
|
case SC_FILE_TYPE_DF:
|
||||||
|
if (file->name[0] != 0)
|
||||||
|
sc_asn1_put_tag(0x84, (u8 *) file->name, file->namelen, p, 18, &p);
|
||||||
|
else { /* Name required -> take the FID */
|
||||||
|
buf[0] = (file->id >> 8) & 0xFF;
|
||||||
|
buf[1] = file->id & 0xFF;
|
||||||
|
sc_asn1_put_tag(0x84, buf, 2, p, 16, &p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pin/key info: define 4 pins, no keys */
|
||||||
|
if(file->path.len == 2)
|
||||||
|
pin_key_info = "\xC1\x04\x81\x82\x83\x84\xC2\x00"; /* root-MF: use local pin-file */
|
||||||
|
else
|
||||||
|
pin_key_info = "\xC1\x04\x01\x02\x03\x04\xC2\x00"; /* sub-DF: use parent pin-file in root-MF */
|
||||||
|
sc_asn1_put_tag(0xA5, pin_key_info, 8, p, 18, &p);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SC_FILE_TYPE_INTERNAL_EF: /* RSA keyfiles */
|
||||||
|
/* Get pin-number */
|
||||||
|
/* Define AC for RSA-files */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Length */
|
||||||
|
out[1] = p - out - 2;
|
||||||
|
|
||||||
|
*outlen = p - out;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int setcos_construct_fci(sc_card_t *card, const sc_file_t *file, u8 *out, size_t *outlen)
|
||||||
|
{
|
||||||
|
if (card->type == SC_CARD_TYPE_SETCOS_44)
|
||||||
|
return setcos_construct_fci_44(card, file, out, outlen);
|
||||||
|
else
|
||||||
|
return iso_ops->construct_fci(card, file, out, outlen);
|
||||||
|
}
|
||||||
|
|
||||||
static u8 acl_to_byte(const sc_acl_entry_t *e)
|
static u8 acl_to_byte(const sc_acl_entry_t *e)
|
||||||
{
|
{
|
||||||
switch (e->method) {
|
switch (e->method) {
|
||||||
|
@ -184,8 +314,160 @@ static u8 acl_to_byte(const sc_acl_entry_t *e)
|
||||||
return 0x00;
|
return 0x00;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u8 acl_to_byte_44(const struct sc_acl_entry *e, u8* p_bNumber)
|
||||||
|
{
|
||||||
|
/* Handle special fixed values */
|
||||||
|
if (e == (sc_acl_entry_t *) 1) /* SC_AC_NEVER */
|
||||||
|
return SC_AC_NEVER;
|
||||||
|
else if ((e == (sc_acl_entry_t *) 2) || /* SC_AC_NONE */
|
||||||
|
(e == (sc_acl_entry_t *) 3) || /* SC_AC_UNKNOWN */
|
||||||
|
(e == (sc_acl_entry_t *) 0))
|
||||||
|
return SC_AC_NONE;
|
||||||
|
|
||||||
|
/* Handle standard values */
|
||||||
|
*p_bNumber = e->key_ref;
|
||||||
|
return(e->method);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If pin is present in the pins list, return it's index.
|
||||||
|
* If it's not yet present, add it to the list and return the index. */
|
||||||
|
static int setcos_pin_index_44(int *pins, int len, int pin)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
if (pins[i] == pin)
|
||||||
|
return i;
|
||||||
|
if (pins[i] == -1) {
|
||||||
|
pins[i] = pin;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(i != len); /* Too much PINs, shouldn't happen */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The ACs are allways for the SETEC_LCSI_ACTIVATED state, even if
|
||||||
|
* we have to create the file in the SC_FILE_STATUS_INITIALISATION state. */
|
||||||
|
static int setcos_create_file_44(sc_card_t *card, sc_file_t *file)
|
||||||
|
{
|
||||||
|
const u8 bFileStatus = file->status == SC_FILE_STATUS_CREATION ?
|
||||||
|
SETEC_LCSI_CREATE : SETEC_LCSI_ACTIVATED;
|
||||||
|
u8 bCommands_always = 0;
|
||||||
|
int pins[] = {-1, -1, -1, -1, -1, -1, -1};
|
||||||
|
u8 bCommands_pin[sizeof(pins)];
|
||||||
|
u8 bCommands_key = 0;
|
||||||
|
u8 bNumber = 0;
|
||||||
|
u8 bPinNumber = 0;
|
||||||
|
u8 bKeyNumber = 0;
|
||||||
|
u8 bMethod = 0;
|
||||||
|
|
||||||
|
/* -1 means RFU */
|
||||||
|
const int df_idx[8] = { /* byte 1 = OpenSC type of AC Bit0, byte 2 = OpenSC type of AC Bit1 ...*/
|
||||||
|
SC_AC_OP_DELETE, SC_AC_OP_CREATE, SC_AC_OP_CREATE,
|
||||||
|
SC_AC_OP_INVALIDATE, SC_AC_OP_REHABILITATE,
|
||||||
|
SC_AC_OP_LOCK, SC_AC_OP_DELETE, -1};
|
||||||
|
const int ef_idx[8] = { /* note: SC_AC_OP_SELECT to be ignored, actually RFU */
|
||||||
|
SC_AC_OP_READ, SC_AC_OP_UPDATE, SC_AC_OP_WRITE,
|
||||||
|
SC_AC_OP_INVALIDATE, SC_AC_OP_REHABILITATE,
|
||||||
|
-1, SC_AC_OP_ERASE, -1};
|
||||||
|
const int efi_idx[8] = { /* internal EF used for RSA keys */
|
||||||
|
SC_AC_OP_READ, SC_AC_OP_ERASE, SC_AC_OP_UPDATE,
|
||||||
|
SC_AC_OP_INVALIDATE, SC_AC_OP_REHABILITATE,
|
||||||
|
-1, SC_AC_OP_ERASE, -1};
|
||||||
|
|
||||||
|
/* Set file creation status */
|
||||||
|
sc_file_set_prop_attr(file, &bFileStatus, 1);
|
||||||
|
|
||||||
|
/* Build ACI from local structure = get AC for each operation group */
|
||||||
|
if (file->sec_attr_len == 0) {
|
||||||
|
const int* p_idx;
|
||||||
|
int i;
|
||||||
|
int len = 0;
|
||||||
|
u8 bBuf[32];
|
||||||
|
|
||||||
|
/* Get specific operation groups for specified file-type */
|
||||||
|
switch (file->type){
|
||||||
|
case SC_FILE_TYPE_DF: /* DF */
|
||||||
|
p_idx = df_idx;
|
||||||
|
break;
|
||||||
|
case SC_FILE_TYPE_INTERNAL_EF: /* EF for RSA keys */
|
||||||
|
p_idx = efi_idx;
|
||||||
|
break;
|
||||||
|
default: /* SC_FILE_TYPE_WORKING_EF */
|
||||||
|
p_idx = ef_idx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get enabled commands + required Keys/Pins */
|
||||||
|
memset(bCommands_pin, 0, sizeof(bCommands_pin));
|
||||||
|
for (i = 7; i >= 0; i--) { /* for each AC Setcos operation */
|
||||||
|
bCommands_always <<= 1;
|
||||||
|
bCommands_key <<= 1;
|
||||||
|
|
||||||
|
if (p_idx[i] == -1) /* -1 means that bit is RFU -> set to 0 */
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bMethod = acl_to_byte_44(file->acl[ p_idx[i] ], &bNumber);
|
||||||
|
/* Convert to OpenSc-index, convert to pin/key number */
|
||||||
|
switch(bMethod){
|
||||||
|
case SC_AC_NONE: /* always allowed */
|
||||||
|
bCommands_always |= 1;
|
||||||
|
break;
|
||||||
|
case SC_AC_CHV: /* pin */
|
||||||
|
if (bNumber == 0 || bNumber > 7) {
|
||||||
|
sc_error(card->ctx, "SetCOS 4.4 PIN refs can only be 1..7\n");
|
||||||
|
return SC_ERROR_INVALID_ARGUMENTS;
|
||||||
|
}
|
||||||
|
bPinNumber = bNumber;
|
||||||
|
bCommands_pin[setcos_pin_index_44(pins, sizeof(pins), (int) bNumber)] |= 1 << i;
|
||||||
|
break;
|
||||||
|
case SC_AC_TERM: /* key */
|
||||||
|
bKeyNumber = bNumber; /* There should be only 1 key */
|
||||||
|
bCommands_key |= 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add the commands that are allways allowed */
|
||||||
|
if (bCommands_always) {
|
||||||
|
bBuf[len++] = 1;
|
||||||
|
bBuf[len++] = bCommands_always;
|
||||||
|
}
|
||||||
|
/* Add commands that require pins */
|
||||||
|
for (i = 0; i < sizeof(bCommands_pin) && pins[i] != -1; i++) {
|
||||||
|
bBuf[len++] = 2;
|
||||||
|
bBuf[len++] = bCommands_pin[i];
|
||||||
|
bBuf[len++] = pins[i] & 0x07; /* pin ref */
|
||||||
|
}
|
||||||
|
/* Add ommands that require the key */
|
||||||
|
if (bCommands_key) {
|
||||||
|
bBuf[len++] = 2 | 0x20; /* indicate keyNumber present */
|
||||||
|
bBuf[len++] = bCommands_key;
|
||||||
|
bBuf[len++] = bKeyNumber;
|
||||||
|
}
|
||||||
|
/* RSA signing/decryption requires AC adaptive coding, can't be put
|
||||||
|
in AC simple coding. Only implemented for pins, not for a key. */
|
||||||
|
bPinNumber = 0;
|
||||||
|
bKeyNumber = 0;
|
||||||
|
if ( (file->type == SC_FILE_TYPE_INTERNAL_EF) &&
|
||||||
|
(acl_to_byte_44(file->acl[SC_AC_OP_CRYPTO], &bNumber) == SC_AC_CHV) ) {
|
||||||
|
bBuf[len++] = 0x83;
|
||||||
|
bBuf[len++] = 0x01;
|
||||||
|
bBuf[len++] = 0x2A; /* INS byte for the sign/decrypt APDU */
|
||||||
|
bBuf[len++] = bNumber & 0x07; /* pin ref */
|
||||||
|
}
|
||||||
|
|
||||||
|
sc_file_set_sec_attr(file, bBuf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
return iso_ops->create_file(card, file);
|
||||||
|
}
|
||||||
|
|
||||||
static int setcos_create_file(sc_card_t *card, sc_file_t *file)
|
static int setcos_create_file(sc_card_t *card, sc_file_t *file)
|
||||||
{
|
{
|
||||||
|
if (card->type == SC_CARD_TYPE_SETCOS_44)
|
||||||
|
return setcos_create_file_44(card, file);
|
||||||
|
|
||||||
if (file->prop_attr_len == 0)
|
if (file->prop_attr_len == 0)
|
||||||
sc_file_set_prop_attr(file, (const u8 *) "\x03\x00\x00", 3);
|
sc_file_set_prop_attr(file, (const u8 *) "\x03\x00\x00", 3);
|
||||||
if (file->sec_attr_len == 0) {
|
if (file->sec_attr_len == 0) {
|
||||||
|
@ -227,6 +509,18 @@ static int setcos_set_security_env2(sc_card_t *card,
|
||||||
int r, locked = 0;
|
int r, locked = 0;
|
||||||
|
|
||||||
assert(card != NULL && env != NULL);
|
assert(card != NULL && env != NULL);
|
||||||
|
|
||||||
|
if (card->type == SC_CARD_TYPE_SETCOS_44) {
|
||||||
|
if (env->flags & SC_SEC_ENV_KEY_REF_ASYMMETRIC) {
|
||||||
|
sc_error(card->ctx, "asymmetric keyref not supported.\n");
|
||||||
|
return SC_ERROR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
if (se_num > 0) {
|
||||||
|
sc_error(card->ctx, "restore security environment not supported.\n");
|
||||||
|
return SC_ERROR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0, 0);
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0, 0);
|
||||||
switch (env->operation) {
|
switch (env->operation) {
|
||||||
case SC_SEC_OPERATION_DECIPHER:
|
case SC_SEC_OPERATION_DECIPHER:
|
||||||
|
@ -236,7 +530,8 @@ static int setcos_set_security_env2(sc_card_t *card,
|
||||||
break;
|
break;
|
||||||
case SC_SEC_OPERATION_SIGN:
|
case SC_SEC_OPERATION_SIGN:
|
||||||
/* Should be 0x41 */
|
/* Should be 0x41 */
|
||||||
apdu.p1 = (card->type == SC_CARD_TYPE_SETCOS_FINEID_V2) ? 0x41 : 0x81;
|
apdu.p1 = ((card->type == SC_CARD_TYPE_SETCOS_FINEID_V2) ||
|
||||||
|
(card->type == SC_CARD_TYPE_SETCOS_44)) ? 0x41 : 0x81;
|
||||||
apdu.p2 = 0xB6;
|
apdu.p2 = 0xB6;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -316,6 +611,7 @@ static int setcos_set_security_env(sc_card_t *card,
|
||||||
case SC_CARD_TYPE_SETCOS_PKI:
|
case SC_CARD_TYPE_SETCOS_PKI:
|
||||||
case SC_CARD_TYPE_SETCOS_FINEID:
|
case SC_CARD_TYPE_SETCOS_FINEID:
|
||||||
case SC_CARD_TYPE_SETCOS_FINEID_V2:
|
case SC_CARD_TYPE_SETCOS_FINEID_V2:
|
||||||
|
case SC_CARD_TYPE_SETCOS_44:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
sc_error(card->ctx, "Card does not support RSA.\n");
|
sc_error(card->ctx, "Card does not support RSA.\n");
|
||||||
|
@ -392,6 +688,147 @@ static void parse_sec_attr(sc_file_t *file, const u8 * buf, size_t len)
|
||||||
add_acl_entry(file, idx[i], buf[i]);
|
add_acl_entry(file, idx[i], buf[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void parse_sec_attr_44(sc_file_t *file, const u8 *buf, size_t len)
|
||||||
|
{
|
||||||
|
/* OpenSc Operation values for each command operation-type */
|
||||||
|
const int df_idx[8] = { /* byte 1 = OpenSC type of AC Bit0, byte 2 = OpenSC type of AC Bit1 ...*/
|
||||||
|
SC_AC_OP_DELETE, SC_AC_OP_CREATE, SC_AC_OP_CREATE,
|
||||||
|
SC_AC_OP_INVALIDATE, SC_AC_OP_REHABILITATE,
|
||||||
|
SC_AC_OP_LOCK, SC_AC_OP_DELETE, -1};
|
||||||
|
const int ef_idx[8] = {
|
||||||
|
SC_AC_OP_READ, SC_AC_OP_UPDATE, SC_AC_OP_WRITE,
|
||||||
|
SC_AC_OP_INVALIDATE, SC_AC_OP_REHABILITATE,
|
||||||
|
-1, SC_AC_OP_ERASE, -1};
|
||||||
|
const int efi_idx[8] = { /* internal EF used for RSA keys */
|
||||||
|
SC_AC_OP_READ, SC_AC_OP_ERASE, SC_AC_OP_UPDATE,
|
||||||
|
SC_AC_OP_INVALIDATE, SC_AC_OP_REHABILITATE,
|
||||||
|
-1, SC_AC_OP_ERASE, -1};
|
||||||
|
|
||||||
|
u8 bValue;
|
||||||
|
int i;
|
||||||
|
int iKeyRef;
|
||||||
|
int iMethod;
|
||||||
|
int iPinCount;
|
||||||
|
int iOffset = 0;
|
||||||
|
int iOperation;
|
||||||
|
const int* p_idx;
|
||||||
|
|
||||||
|
/* Check all sub-AC definitions whitin the total AC */
|
||||||
|
while (len > 1) { /* minimum length = 2 */
|
||||||
|
int iACLen = buf[iOffset] & 0x0F;
|
||||||
|
|
||||||
|
iPinCount = -1; /* default no pin required */
|
||||||
|
iMethod = SC_AC_NONE; /* default no authentication required */
|
||||||
|
|
||||||
|
if (buf[iOffset] & 0X80) { /* AC in adaptive coding */
|
||||||
|
/* Evaluates only the command-byte, not the optional P1/P2/Option bytes */
|
||||||
|
int iParmLen = 1; /* command-byte is always present */
|
||||||
|
int iKeyLen = 0; /* Encryption key is optional */
|
||||||
|
|
||||||
|
if (buf[iOffset] & 0x20) iKeyLen++;
|
||||||
|
if (buf[iOffset+1] & 0x40) iParmLen++;
|
||||||
|
if (buf[iOffset+1] & 0x20) iParmLen++;
|
||||||
|
if (buf[iOffset+1] & 0x10) iParmLen++;
|
||||||
|
if (buf[iOffset+1] & 0x08) iParmLen++;
|
||||||
|
|
||||||
|
/* Get KeyNumber if available */
|
||||||
|
if(iKeyLen) {
|
||||||
|
int iSC = buf[iOffset+iACLen];
|
||||||
|
|
||||||
|
switch( (iSC>>5) & 0x03 ){
|
||||||
|
case 0:
|
||||||
|
iMethod = SC_AC_TERM; /* key authentication */
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
iMethod = SC_AC_AUT; /* key authentication */
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
iMethod = SC_AC_PRO; /* secure messaging */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
iKeyRef = iSC & 0x1F; /* get key number */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get PinNumber if available */
|
||||||
|
if (iACLen > (1+iParmLen+iKeyLen)) { /* check via total length if pin is present */
|
||||||
|
iKeyRef = buf[iOffset+1+1+iParmLen]; /* PTL + AM-header + parameter-bytes */
|
||||||
|
iMethod = SC_AC_CHV;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert SETCOS command to OpenSC command group */
|
||||||
|
switch(buf[iOffset+2]){
|
||||||
|
case 0x2A: /* crypto operation */
|
||||||
|
iOperation = SC_AC_OP_CRYPTO;
|
||||||
|
break;
|
||||||
|
case 0x46: /* key-generation operation */
|
||||||
|
iOperation = SC_AC_OP_UPDATE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
iOperation = SC_AC_OP_SELECT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sc_file_add_acl_entry(file, iOperation, iMethod, iKeyRef);
|
||||||
|
}
|
||||||
|
else { /* AC in simple coding */
|
||||||
|
/* Initial AC is treated as an operational AC */
|
||||||
|
|
||||||
|
/* Get specific Cmd groups for specified file-type */
|
||||||
|
switch (file->type) {
|
||||||
|
case SC_FILE_TYPE_DF: /* DF */
|
||||||
|
p_idx = df_idx;
|
||||||
|
break;
|
||||||
|
case SC_FILE_TYPE_INTERNAL_EF: /* EF for RSA keys */
|
||||||
|
p_idx = efi_idx;
|
||||||
|
break;
|
||||||
|
default: /* EF */
|
||||||
|
p_idx = ef_idx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encryption key present ? */
|
||||||
|
iPinCount = iACLen - 1;
|
||||||
|
|
||||||
|
if (buf[iOffset] & 0x20) {
|
||||||
|
int iSC = buf[iOffset + iACLen];
|
||||||
|
|
||||||
|
switch( (iSC>>5) & 0x03 ) {
|
||||||
|
case 0:
|
||||||
|
iMethod = SC_AC_TERM; /* key authentication */
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
iMethod = SC_AC_AUT; /* key authentication */
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
iMethod = SC_AC_PRO; /* secure messaging */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
iKeyRef = iSC & 0x1F; /* get key number */
|
||||||
|
|
||||||
|
iPinCount--; /* one byte used for keyReference */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pin present ? */
|
||||||
|
if ( iPinCount > 0 ) {
|
||||||
|
iKeyRef = (buf[iOffset + 2] & 0x7); /* pin ref */
|
||||||
|
iMethod = SC_AC_CHV;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add AC for each command-operationType into OpenSc structure */
|
||||||
|
bValue = buf[iOffset + 1];
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
if((bValue & 1) && (p_idx[i] >= 0))
|
||||||
|
sc_file_add_acl_entry(file, p_idx[i], iMethod, iKeyRef);
|
||||||
|
bValue >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Current field treated, get next AC sub-field */
|
||||||
|
iOffset += iACLen +1; /* AC + PTL-byte */
|
||||||
|
len -= iACLen +1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int setcos_select_file(sc_card_t *card,
|
static int setcos_select_file(sc_card_t *card,
|
||||||
const sc_path_t *in_path, sc_file_t **file)
|
const sc_path_t *in_path, sc_file_t **file)
|
||||||
{
|
{
|
||||||
|
@ -400,8 +837,12 @@ static int setcos_select_file(sc_card_t *card,
|
||||||
r = iso_ops->select_file(card, in_path, file);
|
r = iso_ops->select_file(card, in_path, file);
|
||||||
if (r)
|
if (r)
|
||||||
return r;
|
return r;
|
||||||
if (file != NULL)
|
if (file != NULL) {
|
||||||
parse_sec_attr(*file, (*file)->sec_attr, (*file)->sec_attr_len);
|
if (card->type == SC_CARD_TYPE_SETCOS_44)
|
||||||
|
parse_sec_attr_44(*file, (*file)->sec_attr, (*file)->sec_attr_len);
|
||||||
|
else
|
||||||
|
parse_sec_attr(*file, (*file)->sec_attr, (*file)->sec_attr_len);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,16 +852,194 @@ static int setcos_list_files(sc_card_t *card, u8 * buf, size_t buflen)
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xAA, 0, 0);
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xAA, 0, 0);
|
||||||
|
if (card->type == SC_CARD_TYPE_SETCOS_44)
|
||||||
|
apdu.cla = 0x80;
|
||||||
apdu.resp = buf;
|
apdu.resp = buf;
|
||||||
apdu.resplen = buflen;
|
apdu.resplen = buflen;
|
||||||
apdu.le = buflen > 256 ? 256 : buflen;
|
apdu.le = buflen > 256 ? 256 : buflen;
|
||||||
r = sc_transmit_apdu(card, &apdu);
|
r = sc_transmit_apdu(card, &apdu);
|
||||||
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||||
|
if (card->type == SC_CARD_TYPE_SETCOS_44 && apdu.sw1 == 0x6A && apdu.sw2 == 0x82)
|
||||||
|
return 0; /* no files found */
|
||||||
if (apdu.resplen == 0)
|
if (apdu.resplen == 0)
|
||||||
return sc_check_sw(card, apdu.sw1, apdu.sw2);
|
return sc_check_sw(card, apdu.sw1, apdu.sw2);
|
||||||
return apdu.resplen;
|
return apdu.resplen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int setcos_process_fci(sc_card_t *card, sc_file_t *file,
|
||||||
|
const u8 *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
int r = iso_ops->process_fci(card, file, buf, buflen);
|
||||||
|
|
||||||
|
/* SetCOS 4.4: RSA key file is an internal EF but it's
|
||||||
|
* file descriptor doesn't seem to follow ISO7816. */
|
||||||
|
if (r >= 0 && card->type == SC_CARD_TYPE_SETCOS_44) {
|
||||||
|
const u8 *tag;
|
||||||
|
size_t taglen = 1;
|
||||||
|
tag = (u8 *) sc_asn1_find_tag(card->ctx, buf, buflen, 0x82, &taglen);
|
||||||
|
if (tag != NULL && taglen == 1 && *tag == 0x11)
|
||||||
|
file->type = SC_FILE_TYPE_INTERNAL_EF;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write internal data, e.g. add default pin-records to pin-file */
|
||||||
|
setcos_putdata(struct sc_card *card, struct sc_cardctl_setcos_data_obj* data_obj)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
struct sc_apdu apdu;
|
||||||
|
|
||||||
|
SC_FUNC_CALLED(card->ctx, 1);
|
||||||
|
|
||||||
|
memset(&apdu, 0, sizeof(apdu));
|
||||||
|
apdu.cse = SC_APDU_CASE_3_SHORT;
|
||||||
|
apdu.cla = 0x00;
|
||||||
|
apdu.ins = 0xDA;
|
||||||
|
apdu.p1 = data_obj->P1;
|
||||||
|
apdu.p2 = data_obj->P2;
|
||||||
|
apdu.lc = data_obj->DataLen;
|
||||||
|
apdu.datalen = data_obj->DataLen;
|
||||||
|
apdu.data = data_obj->Data;
|
||||||
|
|
||||||
|
r = sc_transmit_apdu(card, &apdu);
|
||||||
|
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||||
|
|
||||||
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
||||||
|
SC_TEST_RET(card->ctx, r, "PUT_DATA returned error");
|
||||||
|
|
||||||
|
SC_FUNC_RETURN(card->ctx, 1, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read internal data, e.g. get RSA public key */
|
||||||
|
setcos_getdata(struct sc_card *card, struct sc_cardctl_setcos_data_obj* data_obj)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
struct sc_apdu apdu;
|
||||||
|
|
||||||
|
SC_FUNC_CALLED(card->ctx, 1);
|
||||||
|
|
||||||
|
memset(&apdu, 0, sizeof(apdu));
|
||||||
|
apdu.cse = SC_APDU_CASE_2_SHORT;
|
||||||
|
apdu.cla = 0x00;
|
||||||
|
apdu.ins = 0xCA; /* GET DATA */
|
||||||
|
apdu.p1 = data_obj->P1;
|
||||||
|
apdu.p2 = data_obj->P2;
|
||||||
|
apdu.lc = 0;
|
||||||
|
apdu.datalen = 0;
|
||||||
|
apdu.data = data_obj->Data;
|
||||||
|
|
||||||
|
apdu.le = 256;
|
||||||
|
apdu.resp = data_obj->Data;
|
||||||
|
apdu.resplen = data_obj->DataLen;
|
||||||
|
|
||||||
|
r = sc_transmit_apdu(card, &apdu);
|
||||||
|
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||||
|
|
||||||
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
||||||
|
SC_TEST_RET(card->ctx, r, "GET_DATA returned error");
|
||||||
|
|
||||||
|
if (apdu.resplen > data_obj->DataLen)
|
||||||
|
r = SC_ERROR_WRONG_LENGTH;
|
||||||
|
else
|
||||||
|
data_obj->DataLen = apdu.resplen;
|
||||||
|
|
||||||
|
SC_FUNC_RETURN(card->ctx, 1, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generate or store a key */
|
||||||
|
static int setcos_generate_store_key(sc_card_t *card,
|
||||||
|
struct sc_cardctl_setcos_gen_store_key_info *data)
|
||||||
|
{
|
||||||
|
struct sc_apdu apdu;
|
||||||
|
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
|
||||||
|
int r, p2, len;
|
||||||
|
|
||||||
|
SC_FUNC_CALLED(card->ctx, 1);
|
||||||
|
|
||||||
|
/* Setup key-generation paramters */
|
||||||
|
len = 0;
|
||||||
|
if (data->op_type == OP_TYPE_GENERATE)
|
||||||
|
sbuf[len++] = 0x92; /* algo ID: RSA CRT */
|
||||||
|
else
|
||||||
|
sbuf[len++] = 0x9A; /* algo ID: EXTERNALLY GENERATED RSA CRT */
|
||||||
|
sbuf[len++] = 0x00;
|
||||||
|
sbuf[len++] = data->mod_len / 256; /* 2 bytes for modulus bitlength */
|
||||||
|
sbuf[len++] = data->mod_len % 256;
|
||||||
|
|
||||||
|
sbuf[len++] = data->pubexp_len / 256; /* 2 bytes for pubexp bitlength */
|
||||||
|
sbuf[len++] = data->pubexp_len % 256;
|
||||||
|
memcpy(sbuf + len, data->pubexp, (data->pubexp_len + 7) / 8);
|
||||||
|
len += (data->pubexp_len + 7) / 8;
|
||||||
|
|
||||||
|
if (data->op_type == OP_TYPE_STORE) {
|
||||||
|
sbuf[len++] = data->primep_len / 256;
|
||||||
|
sbuf[len++] = data->primep_len % 256;
|
||||||
|
memcpy(sbuf + len, data->primep, (data->primep_len + 7) / 8);
|
||||||
|
len += (data->primep_len + 7) / 8;
|
||||||
|
sbuf[len++] = data->primeq_len / 256;
|
||||||
|
sbuf[len++] = data->primeq_len % 256;
|
||||||
|
memcpy(sbuf + len, data->primeq, (data->primeq_len + 7) / 8);
|
||||||
|
len += (data->primeq_len + 7) / 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x46, 0x00, 0x00);
|
||||||
|
apdu.cla = 0x00;
|
||||||
|
apdu.data = sbuf;
|
||||||
|
apdu.datalen = len;
|
||||||
|
apdu.lc = len;
|
||||||
|
|
||||||
|
r = sc_transmit_apdu(card, &apdu);
|
||||||
|
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||||
|
|
||||||
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
||||||
|
SC_TEST_RET(card->ctx, r, "STORE/GENERATE_KEY returned error");
|
||||||
|
|
||||||
|
SC_FUNC_RETURN(card->ctx, 1, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int setcos_activate_file(sc_card_t *card)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
u8 sbuf[2];
|
||||||
|
sc_apdu_t apdu;
|
||||||
|
|
||||||
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x44, 0x00, 0x00);
|
||||||
|
apdu.data = sbuf;
|
||||||
|
|
||||||
|
r = sc_transmit_apdu(card, &apdu);
|
||||||
|
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||||
|
|
||||||
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
||||||
|
SC_TEST_RET(card->ctx, r, "ACTIVATE_FILE returned error");
|
||||||
|
|
||||||
|
SC_FUNC_RETURN(card->ctx, 1, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int setcos_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr)
|
||||||
|
{
|
||||||
|
if (card->type != SC_CARD_TYPE_SETCOS_44)
|
||||||
|
return SC_ERROR_NOT_SUPPORTED;
|
||||||
|
|
||||||
|
switch(cmd) {
|
||||||
|
case SC_CARDCTL_SETCOS_PUTDATA:
|
||||||
|
return setcos_putdata(card,
|
||||||
|
(struct sc_cardctl_setcos_data_obj*) ptr);
|
||||||
|
break;
|
||||||
|
case SC_CARDCTL_SETCOS_GETDATA:
|
||||||
|
return setcos_getdata(card,
|
||||||
|
(struct sc_cardctl_setcos_data_obj*) ptr);
|
||||||
|
break;
|
||||||
|
case SC_CARDCTL_SETCOS_GENERATE_STORE_KEY:
|
||||||
|
return setcos_generate_store_key(card,
|
||||||
|
(struct sc_cardctl_setcos_gen_store_key_info *) ptr);
|
||||||
|
case SC_CARDCTL_SETCOS_ACTIVATE_FILE:
|
||||||
|
return setcos_activate_file(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SC_ERROR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
static int setcos_logout(sc_card_t *card)
|
static int setcos_logout(sc_card_t *card)
|
||||||
{
|
{
|
||||||
|
@ -442,6 +1061,9 @@ static struct sc_card_driver *sc_get_driver(void)
|
||||||
setcos_ops.set_security_env = setcos_set_security_env;
|
setcos_ops.set_security_env = setcos_set_security_env;
|
||||||
setcos_ops.select_file = setcos_select_file;
|
setcos_ops.select_file = setcos_select_file;
|
||||||
setcos_ops.list_files = setcos_list_files;
|
setcos_ops.list_files = setcos_list_files;
|
||||||
|
setcos_ops.process_fci = setcos_process_fci;
|
||||||
|
setcos_ops.construct_fci = setcos_construct_fci;
|
||||||
|
setcos_ops.card_ctl = setcos_card_ctl;
|
||||||
#if 0
|
#if 0
|
||||||
setcos_ops.logout = setcos_logout;
|
setcos_ops.logout = setcos_logout;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -101,6 +101,15 @@ enum {
|
||||||
SC_CARDCTL_OBERTHUR_UPDATE_KEY,
|
SC_CARDCTL_OBERTHUR_UPDATE_KEY,
|
||||||
SC_CARDCTL_OBERTHUR_GENERATE_KEY,
|
SC_CARDCTL_OBERTHUR_GENERATE_KEY,
|
||||||
SC_CARDCTL_OBERTHUR_CREATE_PIN,
|
SC_CARDCTL_OBERTHUR_CREATE_PIN,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setcos specific calls
|
||||||
|
*/
|
||||||
|
SC_CARDCTL_SETCOS_BASE = _CTL_PREFIX('S', 'E', 'T'),
|
||||||
|
SC_CARDCTL_SETCOS_PUTDATA,
|
||||||
|
SC_CARDCTL_SETCOS_GETDATA,
|
||||||
|
SC_CARDCTL_SETCOS_GENERATE_STORE_KEY,
|
||||||
|
SC_CARDCTL_SETCOS_ACTIVATE_FILE,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
@ -287,6 +296,31 @@ struct sc_cardctl_oberthur_createpin_info {
|
||||||
unsigned int puk_tries;
|
unsigned int puk_tries;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setcos stuff
|
||||||
|
*/
|
||||||
|
struct sc_cardctl_setcos_data_obj {
|
||||||
|
int P1;
|
||||||
|
int P2;
|
||||||
|
u8 * Data;
|
||||||
|
size_t DataLen;
|
||||||
|
int LengthMax;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define OP_TYPE_GENERATE 0
|
||||||
|
#define OP_TYPE_STORE 1
|
||||||
|
|
||||||
|
struct sc_cardctl_setcos_gen_store_key_info {
|
||||||
|
int op_type;
|
||||||
|
unsigned int mod_len; /* in bits */
|
||||||
|
unsigned int pubexp_len; /* in bits */
|
||||||
|
unsigned char *pubexp;
|
||||||
|
unsigned int primep_len; /* in bits */
|
||||||
|
unsigned char *primep;
|
||||||
|
unsigned int primeq_len; /* in bits */
|
||||||
|
unsigned char *primeq;
|
||||||
|
};
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -74,6 +74,7 @@ enum {
|
||||||
SC_CARD_TYPE_SETCOS_PKI,
|
SC_CARD_TYPE_SETCOS_PKI,
|
||||||
SC_CARD_TYPE_SETCOS_FINEID,
|
SC_CARD_TYPE_SETCOS_FINEID,
|
||||||
SC_CARD_TYPE_SETCOS_FINEID_V2,
|
SC_CARD_TYPE_SETCOS_FINEID_V2,
|
||||||
|
SC_CARD_TYPE_SETCOS_44 = 6100,
|
||||||
|
|
||||||
/* starcos driver */
|
/* starcos driver */
|
||||||
SC_CARD_TYPE_STARCOS_BASE = 7000,
|
SC_CARD_TYPE_STARCOS_BASE = 7000,
|
||||||
|
|
|
@ -26,7 +26,7 @@ libpkcs15init_la_SOURCES = \
|
||||||
pkcs15-lib.c profile.c keycache.c \
|
pkcs15-lib.c profile.c keycache.c \
|
||||||
pkcs15-gpk.c pkcs15-miocos.c pkcs15-cflex.c \
|
pkcs15-gpk.c pkcs15-miocos.c pkcs15-cflex.c \
|
||||||
pkcs15-etoken.c pkcs15-jcop.c pkcs15-starcos.c \
|
pkcs15-etoken.c pkcs15-jcop.c pkcs15-starcos.c \
|
||||||
pkcs15-oberthur.c
|
pkcs15-oberthur.c pkcs15-setcos.c
|
||||||
|
|
||||||
libpkcs15init_la_LDFLAGS = -version-info @OPENSC_LT_CURRENT@:@OPENSC_LT_REVISION@:@OPENSC_LT_AGE@
|
libpkcs15init_la_LDFLAGS = -version-info @OPENSC_LT_CURRENT@:@OPENSC_LT_REVISION@:@OPENSC_LT_AGE@
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ HEADERSDIR = $(TOPDIR)\src\include\opensc
|
||||||
OBJECTS = profile.obj pkcs15-lib.obj keycache.obj \
|
OBJECTS = profile.obj pkcs15-lib.obj keycache.obj \
|
||||||
pkcs15-miocos.obj pkcs15-gpk.obj pkcs15-cflex.obj \
|
pkcs15-miocos.obj pkcs15-gpk.obj pkcs15-cflex.obj \
|
||||||
pkcs15-etoken.obj pkcs15-jcop.obj pkcs15-starcos.obj \
|
pkcs15-etoken.obj pkcs15-jcop.obj pkcs15-starcos.obj \
|
||||||
pkcs15-oberthur.obj
|
pkcs15-oberthur.obj pkcs15-setcos.obj
|
||||||
|
|
||||||
all: install-headers $(TARGET)
|
all: install-headers $(TARGET)
|
||||||
|
|
||||||
|
|
|
@ -368,6 +368,7 @@ extern struct sc_pkcs15init_operations *sc_pkcs15init_get_etoken_ops(void);
|
||||||
extern struct sc_pkcs15init_operations *sc_pkcs15init_get_jcop_ops(void);
|
extern struct sc_pkcs15init_operations *sc_pkcs15init_get_jcop_ops(void);
|
||||||
extern struct sc_pkcs15init_operations *sc_pkcs15init_get_starcos_ops(void);
|
extern struct sc_pkcs15init_operations *sc_pkcs15init_get_starcos_ops(void);
|
||||||
extern struct sc_pkcs15init_operations *sc_pkcs15init_get_oberthur_ops(void);
|
extern struct sc_pkcs15init_operations *sc_pkcs15init_get_oberthur_ops(void);
|
||||||
|
extern struct sc_pkcs15init_operations *sc_pkcs15init_get_setcos_ops(void);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,6 +142,7 @@ static struct profile_operations {
|
||||||
{ "jcop", (void *) sc_pkcs15init_get_jcop_ops },
|
{ "jcop", (void *) sc_pkcs15init_get_jcop_ops },
|
||||||
{ "starcos", (void *) sc_pkcs15init_get_starcos_ops },
|
{ "starcos", (void *) sc_pkcs15init_get_starcos_ops },
|
||||||
{ "oberthur", (void *) sc_pkcs15init_get_oberthur_ops },
|
{ "oberthur", (void *) sc_pkcs15init_get_oberthur_ops },
|
||||||
|
{ "setcos", (void *) sc_pkcs15init_get_setcos_ops },
|
||||||
{ NULL, NULL },
|
{ NULL, NULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,582 @@
|
||||||
|
/*
|
||||||
|
* SetOCS 4.4 specific operations for PKCS15 initialization
|
||||||
|
*
|
||||||
|
* Copyright (C) 2003, 2005 Zetes
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <opensc/opensc.h>
|
||||||
|
#include <opensc/cardctl.h>
|
||||||
|
#include <opensc/log.h>
|
||||||
|
#include "pkcs15-init.h"
|
||||||
|
#include "keycache.h"
|
||||||
|
#include "profile.h"
|
||||||
|
|
||||||
|
#define SETCOS_MAX_PINS 7
|
||||||
|
|
||||||
|
unsigned char SETCOS_DEFAULT_PUBKEY[] = {0x01, 0x00, 0x01};
|
||||||
|
#define SETCOS_DEFAULT_PUBKEY_LEN sizeof(SETCOS_DEFAULT_PUBKEY)
|
||||||
|
|
||||||
|
static int setcos_generate_store_key( sc_profile_t *, sc_card_t *,
|
||||||
|
unsigned int, unsigned int, sc_pkcs15_pubkey_t *, sc_pkcs15_prkey_t *,
|
||||||
|
sc_pkcs15_prkey_info_t *);
|
||||||
|
|
||||||
|
static int setcos_create_pin_internal(sc_profile_t *, sc_card_t *,
|
||||||
|
int, sc_pkcs15_pin_info_t *, const u8 *, size_t, const u8 *, size_t);
|
||||||
|
|
||||||
|
static int setcos_puk_retries(sc_profile_t *, int);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Erase the card.
|
||||||
|
*/
|
||||||
|
static int setcos_erase_card(sc_profile_t *profile, sc_card_t *card)
|
||||||
|
{
|
||||||
|
sc_pkcs15_pin_info_t pin_info;
|
||||||
|
sc_path_t path;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
/* Just delete the entire MF */
|
||||||
|
|
||||||
|
/* The SO pin has pin reference 1 -- not that it matters much
|
||||||
|
* because pkcs15-init will ask to enter all pins, even if we
|
||||||
|
* did a --so-pin on the command line. */
|
||||||
|
sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &pin_info);
|
||||||
|
sc_keycache_set_pin_name(NULL, pin_info.reference, SC_PKCS15INIT_SO_PIN);
|
||||||
|
|
||||||
|
/* Select parent DF and verify PINs/key as necessary */
|
||||||
|
r = sc_pkcs15init_authenticate(profile, card,
|
||||||
|
profile->mf_info->file, SC_AC_OP_DELETE);
|
||||||
|
if (r < 0)
|
||||||
|
return r == SC_ERROR_FILE_NOT_FOUND ? 0 : r;
|
||||||
|
|
||||||
|
/* Empty path -> we have to to delete the current DF (= the MF) */
|
||||||
|
memset(&path, 0, sizeof(sc_path_t));
|
||||||
|
r = sc_delete_file(card, &path) ;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0 /* New API, turned out to be more work wrt setting the
|
||||||
|
life cycle state to SC_FILE_STATUS_ACTIVATED. */
|
||||||
|
/*
|
||||||
|
* Create the MF and global pin file if they don't exist.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
setcos_init_card(sc_profile_t *profile, sc_card_t *card)
|
||||||
|
{
|
||||||
|
sc_file_t *mf = profile->mf_info->file;
|
||||||
|
sc_file_t *pinfile;
|
||||||
|
int pin_ref;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
/* The SO pin in the keycache is only linked to the pkcs15 DF,
|
||||||
|
* we'll re-ink it to the MF. */
|
||||||
|
pin_ref = sc_keycache_find_named_pin(&profile->df_info->file->path,
|
||||||
|
SC_PKCS15INIT_SO_PIN);
|
||||||
|
if (pin_ref >= 0)
|
||||||
|
sc_keycache_set_pin_name(&profile->mf_info->file->path,
|
||||||
|
pin_ref, SC_PKCS15INIT_SO_PIN);
|
||||||
|
|
||||||
|
/* Create the MF if it doesn't exist yet */
|
||||||
|
card->ctx->suppress_errors++;
|
||||||
|
r = sc_select_file(card, &mf->path, NULL);
|
||||||
|
card->ctx->suppress_errors--;
|
||||||
|
if (r == SC_ERROR_FILE_NOT_FOUND) {
|
||||||
|
sc_debug(card->ctx, "MF doesn't exist, creating now");
|
||||||
|
|
||||||
|
/* Fix up the file's ACLs */
|
||||||
|
r = sc_pkcs15init_fixup_file(profile, mf);
|
||||||
|
if (r >= 0)
|
||||||
|
r = sc_create_file(card, mf);
|
||||||
|
}
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
/* Create the global pin file if it doesn't exist yet */
|
||||||
|
r = sc_profile_get_file(profile, "pinfile", &pinfile);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
card->ctx->suppress_errors++;
|
||||||
|
r = sc_select_file(card, &pinfile->path, NULL);
|
||||||
|
card->ctx->suppress_errors--;
|
||||||
|
if (r == SC_ERROR_FILE_NOT_FOUND) {
|
||||||
|
sc_debug(card->ctx, "Global pin file doesn't exist, creating now");
|
||||||
|
|
||||||
|
/* Fix up the file's ACLs */
|
||||||
|
r = sc_pkcs15init_fixup_file(profile, pinfile);
|
||||||
|
/* Set life cycle state to SC_FILE_STATUS_CREATION,
|
||||||
|
* which means that all ACs are ignored. */
|
||||||
|
if (r >= 0)
|
||||||
|
r = sc_create_file(card, pinfile);
|
||||||
|
}
|
||||||
|
sc_file_free(pinfile);
|
||||||
|
|
||||||
|
/* Re-link the SO-PIN back to the original DF (= the pkcs15 DF) */
|
||||||
|
sc_keycache_set_pin_name(&profile->df_info->file->path,
|
||||||
|
pin_ref, SC_PKCS15INIT_SO_PIN);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a DF
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
setcos_create_dir(sc_profile_t *profile, sc_card_t *card, sc_file_t *df)
|
||||||
|
{
|
||||||
|
return sc_pkcs15init_create_file(profile, card, df);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create the MF and global pin file if they don't exist.
|
||||||
|
*/
|
||||||
|
static int setcos_init_app(sc_profile_t *profile, sc_card_t *card,
|
||||||
|
sc_pkcs15_pin_info_t *pin_info,
|
||||||
|
const u8 *pin, size_t pin_len,
|
||||||
|
const u8 *puk, size_t puk_len)
|
||||||
|
{
|
||||||
|
sc_file_t *mf = profile->mf_info->file;
|
||||||
|
sc_file_t *pinfile = NULL;
|
||||||
|
int pin_ref;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
/* The SO pin in the keycache is only linked to the pkcs15 DF,
|
||||||
|
* we'll re-link it to the MF. */
|
||||||
|
pin_ref = sc_keycache_find_named_pin(&profile->df_info->file->path,
|
||||||
|
SC_PKCS15INIT_SO_PIN);
|
||||||
|
if (pin_ref >= 0)
|
||||||
|
sc_keycache_set_pin_name(&profile->mf_info->file->path,
|
||||||
|
pin_ref, SC_PKCS15INIT_SO_PIN);
|
||||||
|
|
||||||
|
/* Create the MF if it doesn't exist yet */
|
||||||
|
card->ctx->suppress_errors++;
|
||||||
|
r = sc_select_file(card, &mf->path, NULL);
|
||||||
|
card->ctx->suppress_errors--;
|
||||||
|
if (r == SC_ERROR_FILE_NOT_FOUND) {
|
||||||
|
sc_debug(card->ctx, "MF doesn't exist, creating now");
|
||||||
|
/* Fix up the file's ACLs */
|
||||||
|
if ((r = sc_pkcs15init_fixup_file(profile, mf)) >= 0) {
|
||||||
|
/* Set life cycle state to SC_FILE_STATUS_CREATION,
|
||||||
|
* which means that all ACs are ignored. */
|
||||||
|
mf->status = SC_FILE_STATUS_CREATION;
|
||||||
|
r = sc_create_file(card, mf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
/* Create the global pin file if it doesn't exist yet */
|
||||||
|
if ((r = sc_profile_get_file(profile, "pinfile", &pinfile)) < 0)
|
||||||
|
goto done;
|
||||||
|
card->ctx->suppress_errors++;
|
||||||
|
r = sc_select_file(card, &pinfile->path, NULL);
|
||||||
|
card->ctx->suppress_errors--;
|
||||||
|
if (r == SC_ERROR_FILE_NOT_FOUND) {
|
||||||
|
sc_debug(card->ctx, "Global pin file doesn't exist, creating now");
|
||||||
|
/* Fix up the file's ACLs */
|
||||||
|
if ((r = sc_pkcs15init_fixup_file(profile, pinfile)) >= 0) {
|
||||||
|
/* Set life cycle state to SC_FILE_STATUS_CREATION */
|
||||||
|
pinfile->status = SC_FILE_STATUS_CREATION;
|
||||||
|
r = sc_create_file(card, pinfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (r < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
/* Set the SO pin/puk values into the pin file */
|
||||||
|
r = setcos_create_pin_internal(profile, card, 1, pin_info,
|
||||||
|
pin, pin_len, puk, puk_len);
|
||||||
|
if (r < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
/* OK, now we can change the life cycle state to SC_FILE_STATUS_ACTIVATED
|
||||||
|
* so the normal ACs on the pinfile and MF apply. */
|
||||||
|
if ((r = sc_select_file(card, &pinfile->path, NULL)) >= 0) /* pinfile */
|
||||||
|
r = sc_card_ctl(card, SC_CARDCTL_SETCOS_ACTIVATE_FILE, NULL);
|
||||||
|
if (r < 0)
|
||||||
|
goto done;
|
||||||
|
if ((r = sc_select_file(card, &mf->path, NULL)) >= 0) /* MF */
|
||||||
|
r = sc_card_ctl(card, SC_CARDCTL_SETCOS_ACTIVATE_FILE, NULL);
|
||||||
|
if (r < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (pinfile)
|
||||||
|
free(pinfile);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Select the PIN reference
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
setcos_select_pin_reference(sc_profile_t *profile, sc_card_t *card,
|
||||||
|
sc_pkcs15_pin_info_t *pin_info)
|
||||||
|
{
|
||||||
|
sc_pkcs15_pin_info_t pin_info_prof;
|
||||||
|
|
||||||
|
pin_info_prof.reference = 1; /* Default SO PIN ref. */
|
||||||
|
sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &pin_info_prof);
|
||||||
|
|
||||||
|
/* For the SO pin, we take the first available pin reference = 1 */
|
||||||
|
if (pin_info->flags & SC_PKCS15_PIN_FLAG_SO_PIN)
|
||||||
|
pin_info->reference = pin_info_prof.reference;
|
||||||
|
/* sc_pkcs15init_create_pin() starts checking if 0 is an acceptable
|
||||||
|
* pin reference, which isn't for the SetCOS cards. And since the
|
||||||
|
* value 1 has been assigned to the SO pin, we'll jump to 2. */
|
||||||
|
else if (pin_info->reference == 0)
|
||||||
|
pin_info->reference = pin_info_prof.reference + 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a new PIN
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
setcos_create_pin(sc_profile_t *profile, sc_card_t *card,
|
||||||
|
sc_file_t *df,
|
||||||
|
sc_pkcs15_object_t *pin_obj,
|
||||||
|
const u8 *pin, size_t pin_len,
|
||||||
|
const u8 *puk, size_t puk_len)
|
||||||
|
{
|
||||||
|
return setcos_create_pin_internal(profile, card, 0,
|
||||||
|
(sc_pkcs15_pin_info_t *) pin_obj->data,
|
||||||
|
pin, pin_len, puk, puk_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setup file struct & path: get correct template from the profile, construct full path
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
setcos_new_file(sc_profile_t *profile, sc_card_t *card,
|
||||||
|
unsigned int type,
|
||||||
|
unsigned int num, /* number of objects of this type already on the card */
|
||||||
|
sc_file_t **out)
|
||||||
|
{
|
||||||
|
sc_file_t *file;
|
||||||
|
sc_path_t *p;
|
||||||
|
char name[64], *tag;
|
||||||
|
|
||||||
|
if ((type & SC_PKCS15_TYPE_PRKEY_RSA) == SC_PKCS15_TYPE_PRKEY_RSA)
|
||||||
|
tag = "private-key";
|
||||||
|
else if ((type & SC_PKCS15_TYPE_PRKEY) == SC_PKCS15_TYPE_PRKEY)
|
||||||
|
tag = "extractable-key";
|
||||||
|
else if ((type & SC_PKCS15_TYPE_PUBKEY_RSA) == SC_PKCS15_TYPE_PUBKEY_RSA)
|
||||||
|
tag = "public-key";
|
||||||
|
else if ((type & SC_PKCS15_TYPE_CERT) == SC_PKCS15_TYPE_CERT)
|
||||||
|
tag = "certificate";
|
||||||
|
else if ((type & SC_PKCS15_TYPE_DATA_OBJECT) == SC_PKCS15_TYPE_DATA_OBJECT)
|
||||||
|
tag = "data";
|
||||||
|
else {
|
||||||
|
sc_error(card->ctx, "Unsupported file type");
|
||||||
|
return SC_ERROR_INVALID_ARGUMENTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get template from profile */
|
||||||
|
snprintf(name, sizeof(name), "template-%s", tag);
|
||||||
|
if (sc_profile_get_file(profile, name, &file) < 0) {
|
||||||
|
sc_error(card->ctx, "Profile doesn't define %s", name);
|
||||||
|
return SC_ERROR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Auto-increment FID for next object */
|
||||||
|
file->id += num;
|
||||||
|
p = &file->path;
|
||||||
|
*p = profile->df_info->file->path;
|
||||||
|
p->value[p->len++] = (u8) (file->id / 256);
|
||||||
|
p->value[p->len++] = (u8) (file->id % 256);
|
||||||
|
|
||||||
|
*out = file;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
setcos_encode_private_key(sc_profile_t *profile, sc_card_t *card,
|
||||||
|
struct sc_pkcs15_prkey_rsa *rsa,
|
||||||
|
u8 *key, size_t *keysize, int key_ref)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
setcos_encode_public_key(sc_profile_t *profile, sc_card_t *card,
|
||||||
|
struct sc_pkcs15_prkey_rsa *rsa,
|
||||||
|
u8 *key, size_t *keysize, int key_ref)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate RSA key
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
setcos_old_generate_key(sc_profile_t *profile, sc_card_t *card,
|
||||||
|
unsigned int index, /* keyref: 0 for 1st key, ... */
|
||||||
|
unsigned int keybits,
|
||||||
|
sc_pkcs15_pubkey_t *pubkey,
|
||||||
|
struct sc_pkcs15_prkey_info *info)
|
||||||
|
{
|
||||||
|
return setcos_generate_store_key(profile, card, index,
|
||||||
|
keybits, pubkey,
|
||||||
|
NULL, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Store RSA key
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
setcos_new_key(sc_profile_t *profile, sc_card_t *card,
|
||||||
|
struct sc_pkcs15_prkey *key, unsigned int index,
|
||||||
|
struct sc_pkcs15_prkey_info *info)
|
||||||
|
{
|
||||||
|
return setcos_generate_store_key(profile, card, index,
|
||||||
|
key->u.rsa.modulus.len * 8, NULL,
|
||||||
|
key, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Common code for generating or storing a private key.
|
||||||
|
* If pubkey == NULL and prkey != NULL, we have to store a private key
|
||||||
|
* In the oposite case, we have to generate a private key
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
setcos_generate_store_key(sc_profile_t *profile, sc_card_t *card,
|
||||||
|
unsigned int index, /* keynumber: 0 for 1st priv key, ... */
|
||||||
|
unsigned int keybits,
|
||||||
|
sc_pkcs15_pubkey_t *pubkey,
|
||||||
|
sc_pkcs15_prkey_t *prkey,
|
||||||
|
sc_pkcs15_prkey_info_t *info)
|
||||||
|
{
|
||||||
|
struct sc_cardctl_setcos_gen_store_key_info args;
|
||||||
|
struct sc_cardctl_setcos_data_obj data_obj;
|
||||||
|
unsigned char raw_pubkey[256];
|
||||||
|
unsigned char pinbuf[12];
|
||||||
|
int r, mod_len;
|
||||||
|
sc_file_t *prkf = NULL;
|
||||||
|
u8 bData[32];
|
||||||
|
|
||||||
|
/* Parameter check */
|
||||||
|
if ( (keybits < 512) || (keybits > 1024) || (keybits & 0X7)) {
|
||||||
|
sc_error(card->ctx, "Unsupported key size [%u]: 512-1024 bit + 8-multiple\n", keybits);
|
||||||
|
return SC_ERROR_INVALID_ARGUMENTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the private key file */
|
||||||
|
r = setcos_new_file(profile, card, SC_PKCS15_TYPE_PRKEY_RSA, index, &prkf);
|
||||||
|
if (r < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
/* Take enough room for a 1024 bit key */
|
||||||
|
if (prkf->size < 512)
|
||||||
|
prkf->size = 512;
|
||||||
|
|
||||||
|
/* Now create the key file */
|
||||||
|
r = sc_pkcs15init_create_file(profile, card, prkf);
|
||||||
|
if (r < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
/* Fill in data structure */
|
||||||
|
memset(&args, 0, sizeof(args));
|
||||||
|
args.mod_len = keybits;
|
||||||
|
if (prkey == NULL) {
|
||||||
|
args.op_type = OP_TYPE_GENERATE;
|
||||||
|
args.pubexp_len = SETCOS_DEFAULT_PUBKEY_LEN * 8;
|
||||||
|
args.pubexp = SETCOS_DEFAULT_PUBKEY;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
args.op_type = OP_TYPE_STORE;
|
||||||
|
args.pubexp_len = prkey->u.rsa.exponent.len * 8;
|
||||||
|
args.pubexp = prkey->u.rsa.exponent.data;
|
||||||
|
args.primep_len = prkey->u.rsa.p.len * 8;
|
||||||
|
args.primep = prkey->u.rsa.p.data;
|
||||||
|
args.primeq_len = prkey->u.rsa.q.len * 8;
|
||||||
|
args.primeq = prkey->u.rsa.q.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Authenticate */
|
||||||
|
r = sc_pkcs15init_authenticate(profile, card, prkf, SC_AC_OP_UPDATE);
|
||||||
|
if (r < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
/* Generate/store rsa key */
|
||||||
|
r = sc_card_ctl(card, SC_CARDCTL_SETCOS_GENERATE_STORE_KEY, &args);
|
||||||
|
if (r < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
/* Keypair generation -> collect public key info */
|
||||||
|
if (pubkey != NULL) {
|
||||||
|
pubkey->algorithm = SC_ALGORITHM_RSA;
|
||||||
|
pubkey->u.rsa.modulus.len = (keybits + 7) / 8;
|
||||||
|
pubkey->u.rsa.modulus.data = (u8 *) malloc(pubkey->u.rsa.modulus.len);
|
||||||
|
pubkey->u.rsa.exponent.len = SETCOS_DEFAULT_PUBKEY_LEN;
|
||||||
|
pubkey->u.rsa.exponent.data = (u8 *) malloc(SETCOS_DEFAULT_PUBKEY_LEN);
|
||||||
|
memcpy(pubkey->u.rsa.exponent.data, SETCOS_DEFAULT_PUBKEY, SETCOS_DEFAULT_PUBKEY_LEN);
|
||||||
|
|
||||||
|
/* Get public key modulus */
|
||||||
|
if ( (r = sc_select_file(card, &prkf->path, NULL)) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
data_obj.P1 = 01;
|
||||||
|
data_obj.P2 = 01;
|
||||||
|
data_obj.Data = raw_pubkey;
|
||||||
|
data_obj.DataLen = sizeof(raw_pubkey);
|
||||||
|
|
||||||
|
if ((r = sc_card_ctl(card, SC_CARDCTL_SETCOS_GETDATA, &data_obj)) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
mod_len = ((raw_pubkey[0] * 256) + raw_pubkey[1]); /* modulus bit length */
|
||||||
|
if (mod_len != keybits){
|
||||||
|
sc_error(card->ctx, "key-size from card[%i] does not match[%i]\n", mod_len, keybits);
|
||||||
|
r = SC_ERROR_PKCS15INIT;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
memcpy (pubkey->u.rsa.modulus.data, &raw_pubkey[2], pubkey->u.rsa.modulus.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
info->key_reference = 0;
|
||||||
|
info->path = prkf->path;
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (prkf)
|
||||||
|
sc_file_free(prkf);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a new PIN
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
setcos_create_pin_internal(sc_profile_t *profile, sc_card_t *card,
|
||||||
|
int ignore_ac, sc_pkcs15_pin_info_t *pin_info,
|
||||||
|
const u8 *pin, size_t pin_len,
|
||||||
|
const u8 *puk, size_t puk_len)
|
||||||
|
{
|
||||||
|
u8 data[32];
|
||||||
|
int so_pin_ref;
|
||||||
|
int r;
|
||||||
|
int pin_type;
|
||||||
|
struct sc_cardctl_setcos_data_obj data_obj;
|
||||||
|
sc_file_t *pinfile = NULL;
|
||||||
|
|
||||||
|
if (pin_info->reference >= SETCOS_MAX_PINS)
|
||||||
|
return SC_ERROR_INVALID_ARGUMENTS;
|
||||||
|
if (pin == NULL || puk == NULL || pin_len < 4 || puk_len < 4)
|
||||||
|
return SC_ERROR_INVALID_PIN_LENGTH;
|
||||||
|
|
||||||
|
/* Verify required access rights if needed (i.e. if the
|
||||||
|
* pin file isn't in the CREATE life cycle state). */
|
||||||
|
if (!ignore_ac) {
|
||||||
|
/* Re-ink the SO pin to the MF because there is the pin file */
|
||||||
|
so_pin_ref = sc_keycache_find_named_pin(&profile->df_info->file->path,
|
||||||
|
SC_PKCS15INIT_SO_PIN);
|
||||||
|
if (so_pin_ref >= 0)
|
||||||
|
sc_keycache_set_pin_name(&profile->mf_info->file->path,
|
||||||
|
so_pin_ref, SC_PKCS15INIT_SO_PIN);
|
||||||
|
|
||||||
|
r = sc_profile_get_file(profile, "pinfile", &pinfile);
|
||||||
|
if (r >= 0)
|
||||||
|
r = sc_pkcs15init_authenticate(profile, card, pinfile, SC_AC_OP_UPDATE);
|
||||||
|
sc_file_free(pinfile);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make command to add a pin-record */
|
||||||
|
|
||||||
|
data_obj.P1 = 01;
|
||||||
|
data_obj.P2 = 01;
|
||||||
|
|
||||||
|
/* setcos pin number */
|
||||||
|
data[0] = pin_info->reference;
|
||||||
|
|
||||||
|
memset(&data[1], pin_info->pad_char, 16); /* padding */
|
||||||
|
memcpy(&data[1], (u8 *)pin, pin_len); /* copy pin*/
|
||||||
|
memcpy(&data[9], (u8 *)puk, puk_len); /* copy puk */
|
||||||
|
|
||||||
|
data[17] = pin_info->tries_left & 0x0F;
|
||||||
|
data[18] = pin_info->tries_left & 0x0F;
|
||||||
|
/* 0xF0: unlimited unblock tries */
|
||||||
|
data[19] = 0xF0 | setcos_puk_retries(profile, pin_info->reference);
|
||||||
|
|
||||||
|
/* Allow an unlimited number of signatures after a pin verification.
|
||||||
|
* If set to 1 or so, we would have a UserConsent PIN. */
|
||||||
|
data[20] = 0x00;
|
||||||
|
|
||||||
|
if (pin_info->type == 0)
|
||||||
|
data[21] = 0x01; /* BCD */
|
||||||
|
else
|
||||||
|
data[21] = 0x00; /* ASCII */
|
||||||
|
if ((pin_info->flags & 0x010) == 0) /* test for initial pin */
|
||||||
|
data[21] |= 0x80;
|
||||||
|
|
||||||
|
data[22] = 0x00; /* not used */
|
||||||
|
data[23] = 0x00; /* not used */
|
||||||
|
|
||||||
|
data_obj.Data = data;
|
||||||
|
data_obj.DataLen = 24;
|
||||||
|
|
||||||
|
r = sc_card_ctl(card, SC_CARDCTL_SETCOS_PUTDATA, &data_obj);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int setcos_puk_retries(sc_profile_t *profile, int pin_ref)
|
||||||
|
{
|
||||||
|
sc_pkcs15_pin_info_t pin_info;
|
||||||
|
|
||||||
|
pin_info.reference = 1; /* Default SO PIN ref. */
|
||||||
|
sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &pin_info);
|
||||||
|
|
||||||
|
/* If pin_ref is the SO PIN, get the SO PUK info, otherwise the User PUK info */
|
||||||
|
sc_profile_get_pin_info(profile,
|
||||||
|
pin_ref == pin_info.reference ? SC_PKCS15INIT_SO_PUK : SC_PKCS15INIT_USER_PUK,
|
||||||
|
&pin_info);
|
||||||
|
|
||||||
|
if ((pin_info.tries_left < 0) || (pin_info.tries_left > 15))
|
||||||
|
return 3; /* Little extra safety */
|
||||||
|
return pin_info.tries_left;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct sc_pkcs15init_operations sc_pkcs15init_setcos_operations;
|
||||||
|
|
||||||
|
struct sc_pkcs15init_operations *
|
||||||
|
sc_pkcs15init_get_setcos_ops(void)
|
||||||
|
{
|
||||||
|
struct sc_pkcs15init_operations *ops;
|
||||||
|
|
||||||
|
ops = &sc_pkcs15init_setcos_operations;
|
||||||
|
ops->erase_card = setcos_erase_card;
|
||||||
|
ops->init_app = setcos_init_app;
|
||||||
|
ops->select_pin_reference = setcos_select_pin_reference;
|
||||||
|
ops->create_pin = setcos_create_pin;
|
||||||
|
ops->old_generate_key = setcos_old_generate_key;
|
||||||
|
ops->new_key = setcos_new_key;
|
||||||
|
ops->new_file = setcos_new_file;
|
||||||
|
ops->encode_private_key = setcos_encode_private_key;
|
||||||
|
ops->encode_public_key = setcos_encode_public_key;
|
||||||
|
|
||||||
|
return ops;
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
#
|
||||||
|
# General purpose PKCS15 profile for SetCOS4.4 cards
|
||||||
|
#
|
||||||
|
cardinfo {
|
||||||
|
max-pin-length = 8;
|
||||||
|
pin-encoding = ascii-numeric;
|
||||||
|
pin-pad-char = 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Define reasonable limits for PINs and PUK
|
||||||
|
PIN user-pin {
|
||||||
|
attempts = 3;
|
||||||
|
}
|
||||||
|
PIN user-puk {
|
||||||
|
attempts = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
PIN so-pin {
|
||||||
|
reference = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Additional filesystem info.
|
||||||
|
# This is added to the file system info specified in the
|
||||||
|
# main profile.
|
||||||
|
filesystem {
|
||||||
|
DF MF {
|
||||||
|
ACL = *=NONE, CREATE=$SOPIN; # Allow to delete the MF
|
||||||
|
size = 42; # size = 2 + 2*(number of sub-files) -> 20 sub-files
|
||||||
|
|
||||||
|
# There's 1 pin/key file
|
||||||
|
EF pinfile {
|
||||||
|
file-id = 0080; # Recommended by Setec
|
||||||
|
structure = 0x22; # ISF key-file, Setcos V4.4 specific
|
||||||
|
record-length = 28;
|
||||||
|
size = 112; # 28 * 4 = 112 -> 1 SO + 3 user pins/puks
|
||||||
|
ACL = WRITE=$SOPIN, UPDATE=$SOPIN; # WATCH OUT IF YOU CHANGE THESE!!
|
||||||
|
}
|
||||||
|
|
||||||
|
DF PKCS15-AppDF {
|
||||||
|
ACL = *=$SOPIN, SELECT=NONE, FILES=NONE, CREATE=NONE;
|
||||||
|
size = 82; # size = 2 + 2*(number of sub-files) -> 40 sub-files
|
||||||
|
|
||||||
|
EF PKCS15-PrKDF {
|
||||||
|
file-id = 4402;
|
||||||
|
size = 512;
|
||||||
|
acl = *=$SOPIN, READ=NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
EF PKCS15-PuKDF {
|
||||||
|
file-id = 4403;
|
||||||
|
size = 512;
|
||||||
|
acl = *=$SOPIN, READ=NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
EF PKCS15-CDF {
|
||||||
|
file-id = 4404;
|
||||||
|
size = 1024;
|
||||||
|
acl = *=$SOPIN, READ=NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
EF PKCS15-DODF {
|
||||||
|
file-id = 4405;
|
||||||
|
size = 512;
|
||||||
|
acl = *=$SOPIN, READ=NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
EF template-private-key {
|
||||||
|
file-id = 5101; # incremented for following objects: 5102, 5103 ...
|
||||||
|
type = internal-ef;
|
||||||
|
size = 512; # 512bit=196, 768bit=410, 1024bit:512
|
||||||
|
ACL = *=NEVER, READ=NONE, CRYPTO=$PIN, UPDATE=$SOPIN; # READ: only for public key
|
||||||
|
}
|
||||||
|
EF template-extractable-key {
|
||||||
|
file-id = 7000; # incremented for following objects: 5102, 5103 ...
|
||||||
|
type = internal-ef;
|
||||||
|
size = 512; # 512bit=196, 768bit=410, 1024bit:512
|
||||||
|
ACL = *=NEVER, READ=$PIN, UPDATE=$SOPIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
EF template-public-key {
|
||||||
|
file-id = 5201; # incremented for following objects: 5202, 5203 ...
|
||||||
|
ACL = *=$SOPIN, READ=NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
EF template-certificate {
|
||||||
|
file-id = 5501; # incremented for following objects: 5502, 5503 ...
|
||||||
|
ACL = *=$SOPIN, READ=NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
EF template-data {
|
||||||
|
file-id = 5000;
|
||||||
|
structure = transparent;
|
||||||
|
size = 1000;
|
||||||
|
ACL = *=$SOPIN, *=NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue