#include "config.h"
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdarg.h>
#include "libopensc/log.h"
#include "libopensc/opensc.h"
#include "libopensc/cardctl.h"
#include "pkcs15-init.h"
#include "profile.h"
#define STARCOS_AC_NEVER 0x5f
#define STARCOS_AC_ALWAYS 0x9f
#define STARCOS_SOPIN_GID 0x01
#define STARCOS_SOPIN_STATE 0x01
#define STARCOS_SOPIN_GAC 0x01
#define STARCOS_SOPIN_LID 0x81
#define STARCOS_SOPIN_LAC 0x11;
static int starcos_finalize_card(sc_card_t *card);
static int starcos_erase_card(struct sc_profile *pro, sc_pkcs15_card_t *p15card)
{
return sc_card_ctl(p15card->card, SC_CARDCTL_ERASE_CARD, NULL);
}
static u8 get_so_ac(const sc_file_t *file, unsigned int op,
const sc_pkcs15_auth_info_t *auth, unsigned int def,
unsigned int need_global)
{
int is_global = 1;
const sc_acl_entry_t *acl;
if (auth->attrs.pin.flags & SC_PKCS15_PIN_FLAG_LOCAL)
is_global = 0;
if (!is_global && need_global)
return def & 0xff;
acl = sc_file_get_acl_entry(file, op);
if (acl->method == SC_AC_NONE)
return STARCOS_AC_ALWAYS;
else if (acl->method == SC_AC_NEVER)
return STARCOS_AC_NEVER;
else if (acl->method == SC_AC_SYMBOLIC) {
if (is_global)
return STARCOS_SOPIN_GAC;
else
return STARCOS_SOPIN_LAC;
} else
return def;
}
static int starcos_init_card(sc_profile_t *profile, sc_pkcs15_card_t *p15card)
{
struct sc_card *card = p15card->card;
static const u8 key[] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08};
int ret;
sc_starcos_create_data mf_data, ipf_data;
sc_file_t *mf_file, *isf_file, *ipf_file;
sc_path_t tpath;
u8 *p = mf_data.data.mf.header, tmp = 0;
sc_pkcs15_auth_info_t sopin;
memset(&tpath, 0, sizeof(sc_path_t));
tpath.value[0] = 0x3f;
tpath.value[1] = 0x00;
tpath.len = 2;
tpath.type = SC_PATH_TYPE_PATH;
ret = sc_select_file(card, &tpath, NULL);
if (ret == SC_SUCCESS)
return ret;
sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &sopin);
ret = sc_profile_get_file(profile, "MF", &mf_file);
if (ret < 0)
return ret;
ret = sc_profile_get_file(profile, "mf_isf", &isf_file);
if (ret < 0) {
sc_file_free(mf_file);
return ret;
}
mf_data.type = SC_STARCOS_MF_DATA;
memcpy(p, key, 8);
p += 8;
*p++ = (mf_file->size >> 8) & 0xff;
*p++ = mf_file->size & 0xff;
*p++ = (isf_file->size >> 8) & 0xff;
*p++ = isf_file->size & 0xff;
*p++ = get_so_ac(mf_file, SC_AC_OP_CREATE, &sopin, STARCOS_AC_ALWAYS, 1);
*p++ = get_so_ac(isf_file, SC_AC_OP_WRITE, &sopin, STARCOS_AC_NEVER, 1);
*p++ = get_so_ac(mf_file, SC_AC_OP_CREATE, &sopin, STARCOS_AC_ALWAYS, 1);
*p++ = get_so_ac(mf_file, SC_AC_OP_CREATE, &sopin, STARCOS_AC_ALWAYS, 1);
*p++ = 0x00;
*p++ = 0x00;
*p = 0x00;
sc_file_free(mf_file);
sc_file_free(isf_file);
ret = sc_card_ctl(card, SC_CARDCTL_STARCOS_CREATE_FILE, &mf_data);
if (ret != SC_SUCCESS)
return ret;
ret = sc_profile_get_file(profile, "mf_ipf", &ipf_file);
if (ret < 0)
return ret;
ipf_data.type = SC_STARCOS_EF_DATA;
p = ipf_data.data.ef.header;
*p++ = (ipf_file->id >> 8) & 0xff;
*p++ = ipf_file->id & 0xff;
*p++ = STARCOS_AC_ALWAYS;
*p++ = get_so_ac(ipf_file,SC_AC_OP_CREATE, &sopin, STARCOS_AC_ALWAYS, 1);
*p++ = STARCOS_AC_NEVER;
*p++ = STARCOS_AC_NEVER;
*p++ = STARCOS_AC_NEVER;
*p++ = STARCOS_AC_NEVER;
*p++ = STARCOS_AC_NEVER;
*p++ = STARCOS_AC_NEVER;
*p++ = STARCOS_AC_NEVER;
*p++ = 0x00;
*p++ = 0x00;
*p++ = 0xA1;
*p++ = (ipf_file->size >> 8) & 0xff;
*p = ipf_file->size & 0xff;
ret = sc_card_ctl(card, SC_CARDCTL_STARCOS_CREATE_FILE, &ipf_data);
if (ret != SC_SUCCESS) {
free(ipf_file);
return ret;
}
ret = sc_select_file(card, &ipf_file->path, NULL);
sc_file_free(ipf_file);
if (ret < 0)
return ret;
ret = sc_update_binary(card, 0, &tmp, 1, 0);
if (ret < 0)
return ret;
return SC_SUCCESS;
}
static int starcos_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card,
sc_file_t *df)
{
struct sc_card *card = p15card->card;
int ret;
sc_starcos_create_data df_data, ipf_data;
sc_file_t *isf_file, *ipf_file;
u8 *p = df_data.data.df.header, tmp = 0;
sc_pkcs15_auth_info_t sopin;
sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &sopin);
ret = sc_profile_get_file(profile, "p15_isf", &isf_file);
if (ret < 0)
return ret;
df_data.type = SC_STARCOS_DF_DATA;
memset(p, 0, 25);
*p++ = (df->id >> 8) & 0xff;
*p++ = df->id & 0xff;
*p++ = df->namelen & 0xff;
memcpy(p, df->name, (u8) df->namelen);
p += 16;
*p++ = (isf_file->size >> 8) & 0xff;
*p++ = isf_file->size & 0xff;
*p++ = get_so_ac(df, SC_AC_OP_CREATE, &sopin, STARCOS_AC_ALWAYS, 0);
*p++ = get_so_ac(isf_file, SC_AC_OP_WRITE, &sopin, STARCOS_AC_NEVER, 0);
*p++ = 0x00;
*p = 0x00;
df_data.data.df.size[0] = (df->size >> 8) & 0xff;
df_data.data.df.size[1] = df->size & 0xff;
sc_file_free(isf_file);
ret = sc_card_ctl(card, SC_CARDCTL_STARCOS_CREATE_FILE, &df_data);
if (ret != SC_SUCCESS)
return ret;
ret = sc_select_file(card, &df->path, NULL);
if (ret != SC_SUCCESS)
return ret;
ret = sc_profile_get_file(profile, "p15_ipf", &ipf_file);
if (ret < 0)
return ret;
ipf_data.type = SC_STARCOS_EF_DATA;
p = ipf_data.data.ef.header;
*p++ = (ipf_file->id >> 8) & 0xff;
*p++ = ipf_file->id & 0xff;
*p++ = STARCOS_AC_ALWAYS;
*p++ = get_so_ac(ipf_file, SC_AC_OP_CREATE, &sopin, STARCOS_AC_ALWAYS, 0);
*p++ = STARCOS_AC_NEVER;
*p++ = STARCOS_AC_NEVER;
*p++ = STARCOS_AC_NEVER;
*p++ = STARCOS_AC_NEVER;
*p++ = STARCOS_AC_NEVER;
*p++ = STARCOS_AC_NEVER;
*p++ = STARCOS_AC_NEVER;
*p++ = 0x00;
*p++ = 0x00;
*p++ = 0xA1;
*p++ = (ipf_file->size >> 8) & 0xff;
*p = ipf_file->size & 0xff;
ret = sc_card_ctl(card, SC_CARDCTL_STARCOS_CREATE_FILE, &ipf_data);
if (ret != SC_SUCCESS) {
free(ipf_file);
return ret;
}
ret = sc_select_file(card, &ipf_file->path, NULL);
sc_file_free(ipf_file);
if (ret < 0)
return ret;
ret = sc_update_binary(card, 0, &tmp, 1, 0);
if (ret < 0)
return ret;
return SC_SUCCESS;
}
static int have_onepin(sc_profile_t *profile)
{
sc_pkcs15_auth_info_t sopin;
sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &sopin);
if (!(sopin.attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN))
return 1;
else
return 0;
}
#define STARCOS_MIN_LPIN_ID 0x83
#define STARCOS_MAX_LPIN_ID 0x8f
#define STARCOS_MIN_GPIN_ID 0x03
#define STARCOS_MAX_GPIN_ID 0x0f
static int starcos_pin_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card,
sc_pkcs15_auth_info_t *auth_info)
{
int tmp;
if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN)
return SC_ERROR_OBJECT_NOT_VALID;
tmp = auth_info->attrs.pin.reference;
if (have_onepin(profile)) {
auth_info->attrs.pin.reference = STARCOS_SOPIN_GID;
return SC_SUCCESS;
}
if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_LOCAL) {
if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN)
tmp = STARCOS_SOPIN_LID;
else {
if (tmp < STARCOS_MIN_LPIN_ID)
tmp = STARCOS_MIN_LPIN_ID;
if (!(tmp & 0x01))
tmp++;
if (tmp > STARCOS_MAX_LPIN_ID)
return SC_ERROR_TOO_MANY_OBJECTS;
}
} else {
if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN)
tmp = STARCOS_SOPIN_GID;
else {
if (tmp < STARCOS_MIN_GPIN_ID)
tmp = STARCOS_MIN_GPIN_ID;
if (!(tmp & 0x01))
tmp++;
if (tmp > STARCOS_MAX_GPIN_ID)
return SC_ERROR_TOO_MANY_OBJECTS;
}
}
auth_info->attrs.pin.reference = tmp;
return SC_SUCCESS;
}
#define STARCOS_PINID2STATE(a) (((a) == STARCOS_SOPIN_GID) ? STARCOS_SOPIN_STATE : (0x0f - ((0x0f & (a)) >> 1)))
static int starcos_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card,
sc_file_t *df, sc_pkcs15_object_t *pin_obj,
const unsigned char *pin, size_t pin_len,
const unsigned char *puk, size_t puk_len)
{
struct sc_card *card = p15card->card;
int r, is_local, pin_id, tmp, need_finalize = 0;
size_t akd;
sc_file_t *tfile;
const sc_acl_entry_t *acl_entry;
sc_pkcs15_auth_info_t *auth_info = (sc_pkcs15_auth_info_t *) pin_obj->data;
sc_starcos_wkey_data pin_d, puk_d;
u8 tpin[8];
if (!pin || !pin_len || pin_len > 8)
return SC_ERROR_INVALID_ARGUMENTS;
if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN)
return SC_ERROR_OBJECT_NOT_VALID;
is_local = 0x80 & auth_info->attrs.pin.reference;
if (is_local)
r = sc_select_file(card, &df->path, NULL);
else
r = sc_select_file(card, &profile->mf_info->file->path, NULL);
if (r < 0)
return r;
r = sc_profile_get_file(profile, "p15_isf", &tfile);
if (r < 0)
return r;
acl_entry = sc_file_get_acl_entry(tfile, SC_AC_OP_WRITE);
if (acl_entry->method != SC_AC_NONE) {
if ((auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) || have_onepin(profile))
need_finalize = 1;
else
r = sc_pkcs15init_authenticate(profile, p15card, tfile, SC_AC_OP_WRITE);
}
sc_file_free(tfile);
if (r < 0)
return r;
memset(tpin, 0, 8);
memcpy(tpin, pin, pin_len);
tmp = auth_info->tries_left;
pin_id = auth_info->attrs.pin.reference;
pin_d.mode = 0;
pin_d.kid = (u8) pin_id;
pin_d.key = tpin;
pin_d.key_len = 8;
pin_d.key_header[0] = pin_d.kid;
pin_d.key_header[1] = 0;
pin_d.key_header[2] = 8;
pin_d.key_header[3] = STARCOS_AC_ALWAYS;
if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN)
pin_d.key_header[4] = STARCOS_SOPIN_STATE;
else
pin_d.key_header[4] = STARCOS_PINID2STATE(pin_id);
pin_d.key_header[5] = STARCOS_AC_ALWAYS;
pin_d.key_header[6] = ((0x0f & tmp) << 4) | (0x0f & tmp);
pin_d.key_header[7] = 0x00;
pin_d.key_header[8] = 0x00;
akd = auth_info->attrs.pin.min_length;
if (akd < 4)
akd = 4;
if (akd > 8)
akd = 8;
akd--;
akd |= 0x08;
pin_d.key_header[9] = akd;
pin_d.key_header[10] = 0x00;
pin_d.key_header[11] = 0x81;
r = sc_card_ctl(card, SC_CARDCTL_STARCOS_WRITE_KEY, &pin_d);
if (r != SC_SUCCESS)
return r;
if (puk && puk_len) {
sc_pkcs15_auth_info_t puk_info;
if (puk_len > 8)
return SC_ERROR_INVALID_ARGUMENTS;
memset(tpin, 0, 8);
memcpy(tpin, puk, puk_len);
sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PUK, &puk_info);
tmp = puk_info.tries_left;
puk_d.mode = 0;
puk_d.kid = (u8) pin_id + 1;
puk_d.key = tpin;
puk_d.key_len = 8;
puk_d.key_header[0] = puk_d.kid;
puk_d.key_header[1] = 0;
puk_d.key_header[2] = 8;
puk_d.key_header[3] = STARCOS_AC_ALWAYS;
puk_d.key_header[4] = ((pin_id & 0x1f) << 3) | 0x05;
puk_d.key_header[5] = 0x01;
puk_d.key_header[6] = ((0x0f & tmp) << 4) | (0x0f & tmp);
puk_d.key_header[7] = 0x0;
puk_d.key_header[8] = 0x0;
puk_d.key_header[9] = 0x0;
puk_d.key_header[10] = 0x00;
puk_d.key_header[11] = 0x02;
r = sc_card_ctl(card, SC_CARDCTL_STARCOS_WRITE_KEY, &puk_d);
if (r != SC_SUCCESS)
return r;
}
if (!is_local) {
r = sc_select_file(card, &df->path, NULL);
if (r < 0)
return r;
pin_d.key = NULL;
pin_d.key_len = 0;
pin_d.key_header[1] = 0;
pin_d.key_header[2] = 0;
r = sc_card_ctl(card, SC_CARDCTL_STARCOS_WRITE_KEY, &pin_d);
if (r != SC_SUCCESS)
return r;
}
if (need_finalize)
r = starcos_finalize_card(card);
return r;
}
#define STARCOS_MIN_LPKEY_ID 0x91
#define STARCOS_MAX_LPKEY_ID 0x9f
#define STARCOS_MIN_GPKEY_ID 0x11
#define STARCOS_MAX_GPKEY_ID 0x1f
static int starcos_key_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card,
sc_pkcs15_prkey_info_t *prkey)
{
if (prkey->key_reference < STARCOS_MIN_LPKEY_ID)
prkey->key_reference = STARCOS_MIN_LPKEY_ID;
if (prkey->key_reference > STARCOS_MAX_LPKEY_ID)
return SC_ERROR_TOO_MANY_OBJECTS;
return SC_SUCCESS;
}
#define STARCOS_MAX_PR_KEYSIZE 370
static int starcos_encode_prkey(struct sc_pkcs15_prkey_rsa *rsa, u8 *buf)
{
size_t i = 0;
u8 *p = buf;
memset(buf, 0, STARCOS_MAX_PR_KEYSIZE);
if (rsa->p.len && rsa->q.len && rsa->dmp1.len &&
rsa->dmq1.len && rsa->iqmp.len) {
i = STARCOS_MAX_PR_KEYSIZE - rsa->p.len - rsa->q.len -
rsa->dmp1.len - rsa->dmq1.len - 45 - rsa->p.len;
*p++ = 0x0c;
*p++ = 0x91;
*p++ = (u8) rsa->p.len;
*p++ = 0x92;
*p++ = (u8) rsa->q.len;
*p++ = 0x94;
*p++ = (u8) rsa->dmp1.len + 16;
*p++ = 0x95;
*p++ = (u8) rsa->dmq1.len + 16;
*p++ = 0x97;
*p++ = (u8) rsa->p.len;
*p++ = 0x00;
*p++ = (u8) i;
for (i = rsa->q.len; i != 0; i--)
*p++ = rsa->q.data[i - 1];
for (i = rsa->p.len; i != 0; i--)
*p++ = rsa->p.data[i - 1];
for (i = 16; i != 0; i--)
*p++ = 0x00;
for (i = rsa->dmp1.len; i != 0; i--)
*p++ = rsa->dmq1.data[i - 1];
for (i = 16; i != 0; i--)
*p++ = 0x00;
for (i = rsa->dmq1.len; i != 0; i--)
*p++ = rsa->dmp1.data[i - 1];
for (i = rsa->iqmp.len; i != 0; i--)
*p++ = rsa->iqmp.data[i - 1];
for (i = rsa->p.len - rsa->iqmp.len; i != 0; i--)
*p++ = 0x00;
} else if (rsa->modulus.len && rsa->d.len) {
i = STARCOS_MAX_PR_KEYSIZE - 7 - rsa->modulus.len
- rsa->d.len - 16;
*p++ = 6;
*p++ = 0x90;
*p++ = (u8) rsa->modulus.len;
*p++ = 0x93;
*p++ = (u8) rsa->d.len + 16;
*p++ = 0x00;
*p++ = (u8) i;
for (i = rsa->modulus.len; i != 0; i--)
*p++ = rsa->modulus.data[i - 1];
for (i = 16; i != 0; i--)
*p++ = 0x00;
for (i = rsa->d.len; i != 0; i--)
*p++ = rsa->d.data[i - 1];
} else
return SC_ERROR_INTERNAL;
return SC_SUCCESS;
}
static size_t starcos_ipf_get_lastpos(u8 *ipf, size_t ipf_len)
{
size_t num_keys, tmp;
u8 *p = ipf;
if (!ipf || ipf_len < 13)
return 0;
num_keys = *p++;
if (num_keys == 0xff)
num_keys = 0;
if (!num_keys)
return 1;
while (num_keys--) {
size_t offset = p - ipf;
tmp = 12 + (p[1] << 8) + p[2];
if (tmp + offset > ipf_len)
return 0;
p += tmp;
}
return p - ipf;
}
static int starcos_encode_pukey(struct sc_pkcs15_prkey_rsa *rsa, u8 *buf,
sc_pkcs15_prkey_info_t *kinfo)
{
size_t i = 0;
u8 *p = buf;
if (!rsa) {
if (!buf)
return (int) 12 + (kinfo->modulus_length >> 3);
*p++ = 0x06;
*p++ = 0x01;
*p++ = 0x01;
*p++ = 0x10;
*p++ = (kinfo->modulus_length >> 3) & 0xff;
*p++ = 0x13;
*p++ = 0x04;
*p = (u8) kinfo->key_reference;
} else {
size_t mod_len = rsa->modulus.len & 0xff,
exp_len = rsa->exponent.len & 0xff;
if (!buf)
return (int) 8 + mod_len + exp_len + 1;
*p++ = 0x06;
*p++ = 0x01;
*p++ = 0x01;
*p++ = 0x10;
*p++ = mod_len;
*p++ = 0x13;
*p++ = exp_len + 1;
*p++ = (u8) kinfo->key_reference;
for (i = mod_len; i != 0; i--)
*p++ = rsa->modulus.data[i - 1];
for (i = exp_len; i != 0; i--)
*p++ = rsa->exponent.data[i - 1];
*p = 0x00;
}
return SC_SUCCESS;
}
static int starcos_write_pukey(sc_profile_t *profile, sc_card_t *card,
struct sc_pkcs15_prkey_rsa *rsa, sc_pkcs15_prkey_info_t *kinfo)
{
int r;
size_t len, keylen, endpos;
u8 *buf, key[280], *p, num_keys;
sc_file_t *tfile = NULL;
sc_path_t tpath;
tpath = kinfo->path;
r = sc_profile_get_file_in(profile, &tpath, "p15_ipf", &tfile);
if (r < 0)
return r;
tpath = tfile->path;
sc_file_free(tfile);
tfile = NULL;
r = sc_select_file(card, &tpath, &tfile);
if (r != SC_SUCCESS)
return r;
len = tfile->size;
sc_file_free(tfile);
buf = malloc(len);
if (!buf)
return SC_ERROR_OUT_OF_MEMORY;
r = sc_read_binary(card, 0, buf, len, 0);
if (r < 0 || r != (int)len)
return r;
num_keys = buf[0];
if (num_keys == 0xff)
num_keys = 0;
keylen = starcos_encode_pukey(rsa, NULL, kinfo);
if (!keylen) {
free(buf);
return SC_ERROR_INTERNAL;
}
p = key;
*p++ = (u8) kinfo->key_reference;
*p++ = (keylen >> 8) & 0xff;
*p++ = keylen & 0xff;
*p++ = STARCOS_AC_ALWAYS;
*p++ = 0x0f;
*p++ = 0;
*p++ = 0x09;
*p++ = 0x4a;
*p++ = ((keylen >> 8) & 0xff) | 0x80;
*p++ = keylen & 0xff;
r = starcos_encode_pukey(rsa, p, kinfo);
if (r != SC_SUCCESS) {
free(buf);
return SC_ERROR_INTERNAL;
}
p += keylen;
*p++ = 0x04;
*p = (u8) kinfo->key_reference;
num_keys++;
r = sc_update_binary(card, 0, &num_keys, 1, 0);
if (r < 0)
return r;
endpos = starcos_ipf_get_lastpos(buf, len);
free(buf);
return sc_update_binary(card, endpos, key, keylen + 12, 0);
}
static int starcos_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card,
sc_pkcs15_object_t *obj)
{
struct sc_card *card = p15card->card;
int r, pin_id;
u8 akd = 0, state;
sc_file_t *tfile;
const sc_acl_entry_t *acl_entry;
sc_pkcs15_prkey_info_t *kinfo = (sc_pkcs15_prkey_info_t *)obj->data;
sc_starcos_wkey_data tkey;
r = sc_profile_get_file(profile, "p15_isf", &tfile);
if (r < 0)
return r;
acl_entry = sc_file_get_acl_entry(tfile, SC_AC_OP_WRITE);
if (acl_entry->method != SC_AC_NONE) {
r = sc_pkcs15init_authenticate(profile, p15card, tfile, SC_AC_OP_WRITE);
}
else {
r = sc_select_file(card, &tfile->path, NULL);
}
sc_file_free(tfile);
if (r < 0)
return r;
tkey.mode = 0x00;
tkey.kid = (u8) kinfo->key_reference;
tkey.key_header[0] = (u8) kinfo->key_reference;
tkey.key_header[1] = (STARCOS_MAX_PR_KEYSIZE >> 8) & 0xff;
tkey.key_header[2] = STARCOS_MAX_PR_KEYSIZE & 0xff;
pin_id = sc_pkcs15init_get_pin_reference(p15card, profile, SC_AC_SYMBOLIC,
SC_PKCS15INIT_USER_PIN);
if (pin_id < 0)
state = STARCOS_AC_ALWAYS;
else {
state = STARCOS_PINID2STATE(pin_id);
state |= pin_id & 0x80 ? 0x10 : 0x00;
}
tkey.key_header[3] = state;
if (obj->user_consent)
tkey.key_header[4] = 0x0f;
else
tkey.key_header[4] = 0x8f;
tkey.key_header[5] = 0x11;
tkey.key_header[6] = 0x33;
tkey.key_header[7] = 0x00;
tkey.key_header[8] = 0x09;
if (kinfo->usage & SC_PKCS15_PRKEY_USAGE_NONREPUDIATION)
akd |= 0x10;
if (kinfo->usage & SC_PKCS15_PRKEY_USAGE_SIGN)
akd |= 0x31;
if (kinfo->usage & SC_PKCS15_PRKEY_USAGE_SIGNRECOVER)
akd |= 0x31;
if (kinfo->usage & SC_PKCS15_PRKEY_USAGE_DECRYPT ||
kinfo->usage & SC_PKCS15_PRKEY_USAGE_UNWRAP)
akd |= 0x02;
tkey.key_header[9] = akd;
tkey.key_header[10] = 0x03;
tkey.key_header[11] = 0xa0;
tkey.key = NULL;
tkey.key_len = 0;
return sc_card_ctl(card, SC_CARDCTL_STARCOS_WRITE_KEY, &tkey);
}
static int starcos_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card,
sc_pkcs15_object_t *obj, sc_pkcs15_prkey_t *key)
{
int r;
u8 key_buf[STARCOS_MAX_PR_KEYSIZE];
sc_pkcs15_prkey_info_t *kinfo = (sc_pkcs15_prkey_info_t *) obj->data;
const sc_acl_entry_t *acl_entry;
sc_file_t *tfile;
struct sc_pkcs15_prkey_rsa *rsa = &key->u.rsa;
sc_starcos_wkey_data tkey;
if (key->algorithm != SC_ALGORITHM_RSA)
return SC_ERROR_INVALID_ARGUMENTS;
if (starcos_encode_prkey(rsa, key_buf))
return SC_ERROR_INTERNAL;
r = sc_profile_get_file(profile, "p15_isf", &tfile);
if (r < 0)
return r;
acl_entry = sc_file_get_acl_entry(tfile, SC_AC_OP_WRITE);
if (acl_entry->method != SC_AC_NONE) {
r = sc_pkcs15init_authenticate(profile, p15card, tfile, SC_AC_OP_WRITE);
}
sc_file_free(tfile);
if (r < 0)
return r;
tkey.mode = 0x01;
tkey.kid = (u8) kinfo->key_reference;
tkey.key = key_buf;
tkey.key_len = STARCOS_MAX_PR_KEYSIZE;
r = sc_card_ctl(p15card->card, SC_CARDCTL_STARCOS_WRITE_KEY, &tkey);
if (r != SC_SUCCESS)
return r;
return starcos_write_pukey(profile, p15card->card, rsa, kinfo);
}
static int starcos_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card,
sc_pkcs15_object_t *obj, sc_pkcs15_pubkey_t *pubkey)
{
int r;
const sc_acl_entry_t *acl_entry;
sc_file_t *tfile;
sc_starcos_gen_key_data gendat;
sc_pkcs15_prkey_info_t *kinfo = (sc_pkcs15_prkey_info_t *) obj->data;
if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA)
return SC_ERROR_NOT_SUPPORTED;
r = sc_profile_get_file(profile, "p15_isf", &tfile);
if (r < 0)
return r;
acl_entry = sc_file_get_acl_entry(tfile, SC_AC_OP_WRITE);
if (acl_entry->method != SC_AC_NONE) {
r = sc_pkcs15init_authenticate(profile, p15card, tfile, SC_AC_OP_WRITE);
}
sc_file_free(tfile);
if (r < 0)
return r;
r = starcos_write_pukey(profile, p15card->card, NULL, kinfo);
if (r < 0)
return r;
gendat.key_id = (u8) kinfo->key_reference;
gendat.key_length = (size_t) kinfo->modulus_length;
gendat.modulus = NULL;
r = sc_card_ctl(p15card->card, SC_CARDCTL_STARCOS_GENERATE_KEY, &gendat);
if (r != SC_SUCCESS)
return r;
if (pubkey) {
u8 *buf;
struct sc_pkcs15_pubkey_rsa *rsa = &pubkey->u.rsa;
rsa->modulus.data = gendat.modulus;
rsa->modulus.len = kinfo->modulus_length >> 3;
buf = malloc(3);
if (!buf)
return SC_ERROR_OUT_OF_MEMORY;
buf[0] = 0x01;
buf[1] = 0x00;
buf[2] = 0x01;
rsa->exponent.data = buf;
rsa->exponent.len = 3;
pubkey->algorithm = SC_ALGORITHM_RSA;
} else
free(gendat.modulus);
return SC_SUCCESS;
}
static int starcos_finalize_card(sc_card_t *card)
{
int r;
sc_file_t tfile;
sc_path_t tpath;
sc_format_path("3F00", &tpath);
r = sc_select_file(card, &tpath, NULL);
if (r < 0)
return r;
tfile.type = SC_FILE_TYPE_DF;
tfile.id = 0x3f00;
r = sc_card_ctl(card, SC_CARDCTL_STARCOS_CREATE_END, &tfile);
if (r < 0)
sc_log(card->ctx, "failed to call CREATE END for the MF\n");
tfile.type = SC_FILE_TYPE_DF;
tfile.id = 0x5015;
r = sc_card_ctl(card, SC_CARDCTL_STARCOS_CREATE_END, &tfile);
if (r == SC_ERROR_NOT_ALLOWED)
return SC_SUCCESS;
return r;
}
static struct sc_pkcs15init_operations sc_pkcs15init_starcos_operations = {
starcos_erase_card,
starcos_init_card,
starcos_create_dir,
NULL,
starcos_pin_reference,
starcos_create_pin,
starcos_key_reference,
starcos_create_key,
starcos_store_key,
starcos_generate_key,
NULL, NULL,
starcos_finalize_card,
NULL,
NULL, NULL, NULL, NULL, NULL,
NULL
};
struct sc_pkcs15init_operations *sc_pkcs15init_get_starcos_ops(void)
{
return &sc_pkcs15init_starcos_operations;
}