/* * Starcos SPK 2.3 specific operation for PKCS15 initialization * * Copyright (C) 2004 Nils Larsch * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include #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; /* test if we already have a MF */ 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) /* we already have a MF => return OK */ return ret; sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &sopin); /* get mf profile */ ret = sc_profile_get_file(profile, "MF", &mf_file); if (ret < 0) return ret; /* get size of the isf */ 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; /* AC CREATE EF */ *p++ = get_so_ac(mf_file, SC_AC_OP_CREATE, &sopin, STARCOS_AC_ALWAYS, 1); /* AC CREATE KEY */ *p++ = get_so_ac(isf_file, SC_AC_OP_WRITE, &sopin, STARCOS_AC_NEVER, 1); /* AC CREATE DF */ *p++ = get_so_ac(mf_file, SC_AC_OP_CREATE, &sopin, STARCOS_AC_ALWAYS, 1); /* AC REGISTER DF */ *p++ = get_so_ac(mf_file, SC_AC_OP_CREATE, &sopin, STARCOS_AC_ALWAYS, 1); *p++ = 0x00; /* SM CR: no */ *p++ = 0x00; /* SM EF: no */ *p = 0x00; /* SM ISF: no */ sc_file_free(mf_file); sc_file_free(isf_file); /* call CREATE MF */ ret = sc_card_ctl(card, SC_CARDCTL_STARCOS_CREATE_FILE, &mf_data); if (ret != SC_SUCCESS) return ret; /* create IPF */ /* get size of the ipf */ 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; /* AC READ: always */ /* AC WRITE IPF */ *p++ = get_so_ac(ipf_file,SC_AC_OP_CREATE, &sopin, STARCOS_AC_ALWAYS, 1); *p++ = STARCOS_AC_NEVER; /* AC ERASE */ *p++ = STARCOS_AC_NEVER; /* AC LOCK */ *p++ = STARCOS_AC_NEVER; /* AC UNLOCK */ *p++ = STARCOS_AC_NEVER; /* AC INCREASE */ *p++ = STARCOS_AC_NEVER; /* AC_DECREASE */ *p++ = STARCOS_AC_NEVER; /* RFU */ *p++ = STARCOS_AC_NEVER; /* RFU */ *p++ = 0x00; /* SM */ *p++ = 0x00; /* SID */ *p++ = 0xA1; /* IPF */ *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; } /* init IPF */ 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); /* get p15_isf profile */ 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; /* AC CREATE EF */ *p++ = get_so_ac(df, SC_AC_OP_CREATE, &sopin, STARCOS_AC_ALWAYS, 0); /* AC CREATE KEY */ *p++ = get_so_ac(isf_file, SC_AC_OP_WRITE, &sopin, STARCOS_AC_NEVER, 0); *p++ = 0x00; /* SM EF: no */ *p = 0x00; /* SM ISF: no */ df_data.data.df.size[0] = (df->size >> 8) & 0xff; df_data.data.df.size[1] = df->size & 0xff; sc_file_free(isf_file); /* call CREATE DF */ ret = sc_card_ctl(card, SC_CARDCTL_STARCOS_CREATE_FILE, &df_data); if (ret != SC_SUCCESS) return ret; /* create IPF */ 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; /* AC READ */ /* AC WRITE IPF */ *p++ = get_so_ac(ipf_file, SC_AC_OP_CREATE, &sopin, STARCOS_AC_ALWAYS, 0); *p++ = STARCOS_AC_NEVER; /* AC ERASE */ *p++ = STARCOS_AC_NEVER; /* AC LOCK */ *p++ = STARCOS_AC_NEVER; /* AC UNLOCK */ *p++ = STARCOS_AC_NEVER; /* AC INCREASE */ *p++ = STARCOS_AC_NEVER; /* AC_DECREASE */ *p++ = STARCOS_AC_NEVER; /* RFU */ *p++ = STARCOS_AC_NEVER; /* RFU */ *p++ = 0x00; /* SM */ *p++ = 0x00; /* SID */ *p++ = 0xA1; /* IPF */ *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; } /* init IPF */ 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; } /* range of possible key ids for pins (note: the key id of the puk * is the key id of the pin plus one) */ #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)) { /* we have the onepin profile */ auth_info->attrs.pin.reference = STARCOS_SOPIN_GID; return SC_SUCCESS; } if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_LOCAL) { /* use local KID */ /* SO-pin */ 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)) /* odd KIDs for PINs and even KIDs for PUKs */ tmp++; if (tmp > STARCOS_MAX_LPIN_ID) return SC_ERROR_TOO_MANY_OBJECTS; } } else { /* use global KID */ /* SO-pin */ 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)) /* odd KIDs for PINs and even KIDs for PUKs */ tmp++; if (tmp > STARCOS_MAX_GPIN_ID) return SC_ERROR_TOO_MANY_OBJECTS; } } auth_info->attrs.pin.reference = tmp; return SC_SUCCESS; } /* About STARCOS_PINID2STATE * Starcos SPK 2.3 uses a state machine to control the access * to files or keys. This means that the access to a certain * object is granted if the current state (of either the current * DF or the MF) is =, <, >= or != a specified state (see * Starcos S 2.1 manual). To map the pkcs15 access control model *(one object is protected by one pin etc.) to the Starcos S 2.1 * model the following approach is used: * the pin with the key id 3 (or 0x81) sets the global (or local) * state to 15 (note: 16 is the lowest initial state). * the pin with the key id 4 (or 0x82) is reserved for the PUK * the pin with the key id 5 (or 0x83) sets the global (or local) * state to 14. * ... * Note: the key id 1 and 2 (or local 0x81 and 0x82) is used for * the 'SO-pin' which sets the state to 0x01. * XXX: some card operations, like terminate card usage are only * possible in state 0x00 * * Nils */ #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; /* get and verify sopin if necessary */ 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; /* pad pin with 0 */ memset(tpin, 0, 8); memcpy(tpin, pin, pin_len); /* write PIN */ tmp = auth_info->tries_left; pin_id = auth_info->attrs.pin.reference; pin_d.mode = 0; /* install */ 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; /* AKD: standard + every char != 0 + * pin min length */ pin_d.key_header[10] = 0x00; /* never allow WRITE KEY */ pin_d.key_header[11] = 0x81; /* key attribute: akd + pin */ /* create/write PIN */ 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; /* install */ 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; /* create/write PUK */ r = sc_card_ctl(card, SC_CARDCTL_STARCOS_WRITE_KEY, &puk_d); if (r != SC_SUCCESS) return r; } /* in case of a global pin: write dummy entry in df isf */ 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; /* create/write dummy PIN */ r = sc_card_ctl(card, SC_CARDCTL_STARCOS_WRITE_KEY, &pin_d); if (r != SC_SUCCESS) return r; } /* in case of a SOPIN: if AC WRITE KEY is protected by the * SOPIN, call starcos_finalize_card to activate the ACs */ if (need_finalize) r = starcos_finalize_card(card); return r; } /* range of possible key ids for private keys */ #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) { /* use (local) KIDs 0x91-0x9f for private rsa keys */ 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; /* clear key buffer */ memset(buf, 0, STARCOS_MAX_PR_KEYSIZE); if (rsa->p.len && rsa->q.len && rsa->dmp1.len && rsa->dmq1.len && rsa->iqmp.len) { /* CRT RSA key */ /* get number of 0x00 bytes */ i = STARCOS_MAX_PR_KEYSIZE - rsa->p.len - rsa->q.len - rsa->dmp1.len - rsa->dmq1.len - 45 - rsa->p.len; /* key format list */ *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; /* copy key components */ 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) { /* normal RSA key */ i = STARCOS_MAX_PR_KEYSIZE - 7 - rsa->modulus.len - rsa->d.len - 16; /* key format list */ *p++ = 6; *p++ = 0x90; *p++ = (u8) rsa->modulus.len; *p++ = 0x93; *p++ = (u8) rsa->d.len + 16; *p++ = 0x00; *p++ = (u8) i; /* copy key components */ 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; } /* XXX the whole IPF stuff doesn't really work very well */ /** starcos_ipf_get_lastpos * returns the offset to the first byte after the last key */ 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++; /* the first bytes contains the number of keys*/ if (num_keys == 0xff) num_keys = 0; if (!num_keys) return 1; while (num_keys--) { size_t offset = p - ipf; /* note: p > ipf */ /* get offset to the next key header */ 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 == NULL return key header for key generation */ if (!rsa) { if (!buf) /* if buf == NULL return length of the encoded key */ return (int) 12 + (kinfo->modulus_length >> 3); *p++ = 0x06; /* length key header */ *p++ = 0x01; /* CHA byte */ *p++ = 0x01; *p++ = 0x10; /* RSA: n */ *p++ = (kinfo->modulus_length >> 3) & 0xff; *p++ = 0x13; /* RSA: e */ *p++ = 0x04; *p = (u8) kinfo->key_reference; /* CHA byte */ } else { /* encode normal public key */ 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; /* length key header */ *p++ = 0x01; /* CHA byte */ *p++ = 0x01; *p++ = 0x10; /* RSA: n */ *p++ = mod_len; *p++ = 0x13; /* RSA: e */ *p++ = exp_len + 1; *p++ = (u8) kinfo->key_reference; /* CHA byte */ /* copy modulus */ for (i = mod_len; i != 0; i--) *p++ = rsa->modulus.data[i - 1]; /* copy exponent */ 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; /* get ipf profile */ 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) /* unable to select ipf */ return r; len = tfile->size; sc_file_free(tfile); buf = malloc(len); if (!buf) return SC_ERROR_OUT_OF_MEMORY; /* read the complete IPF */ r = sc_read_binary(card, 0, buf, len, 0); if (r < 0 || r != (int)len) return r; /* get/fix number of keys */ num_keys = buf[0]; if (num_keys == 0xff) num_keys = 0; /* encode public key */ 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; /* AC WRITE etc XXX */ *p++ = 0x0f; *p++ = 0; *p++ = 0x09; /* ALGO XXX */ *p++ = 0x4a; /* AKD XXX */ *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; /* CPI */ *p = (u8) kinfo->key_reference; /* CHA */ /* updated IPF (XXX: currently append only) */ 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; /* get and verify sopin if necessary */ 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; /* create sc_starcos_wkey_data */ tkey.mode = 0x00; /* install new key */ 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); /* get the necessary state */ state |= pin_id & 0x80 ? 0x10 : 0x00; /* local vs. global key id */ } tkey.key_header[3] = state; /* AC to access key */ if (obj->user_consent) tkey.key_header[4] = 0x0f; /* do state transition */ else tkey.key_header[4] = 0x8f; /* no state transition */ tkey.key_header[5] = 0x11; /* require local state == 1 to update key */ 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; /* allow DS, IA and PKCS11 */ if (kinfo->usage & SC_PKCS15_PRKEY_USAGE_SIGNRECOVER) akd |= 0x31; /* allow DS, IA and PKCS11 */ 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) /* ignore DSA keys */ return SC_ERROR_INVALID_ARGUMENTS; /* create sc_starcos_wkey_data */ if (starcos_encode_prkey(rsa, key_buf)) return SC_ERROR_INTERNAL; /* get and verify sopin if necessary */ 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; /* update key */ 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; /* store public key in the IPF */ 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; /* get and verify sopin if necessary */ 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; /* XXX It would be better to write the public key header * in the IPF when the private key header is created, but * as we don't know the size of the exponent at this time * we would waste space. */ /* create (empty) public key entry */ r = starcos_write_pukey(profile, p15card->card, NULL, kinfo); if (r < 0) return r; /* generate key pair */ 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; /* get the modulus via READ PUBLIC KEY */ if (pubkey) { u8 *buf; struct sc_pkcs15_pubkey_rsa *rsa = &pubkey->u.rsa; /* set the modulus */ rsa->modulus.data = gendat.modulus; rsa->modulus.len = kinfo->modulus_length >> 3; /* set the exponent (always 0x10001) */ 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 public key */ 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; /* SELECT FILE MF */ sc_format_path("3F00", &tpath); r = sc_select_file(card, &tpath, NULL); if (r < 0) return r; /* call CREATE END for the MF (ignore errors) */ 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"); /* call CREATE END for the apps (pkcs15) DF */ 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) /* card is already finalized */ return SC_SUCCESS; return r; } static struct sc_pkcs15init_operations sc_pkcs15init_starcos_operations = { starcos_erase_card, starcos_init_card, starcos_create_dir, NULL, /* create_domain */ starcos_pin_reference, starcos_create_pin, starcos_key_reference, starcos_create_key, starcos_store_key, starcos_generate_key, NULL, NULL, /* encode private/public key */ starcos_finalize_card, NULL, /* delete_object */ NULL, NULL, NULL, NULL, NULL, /* pkcs15init emulation */ NULL /* sanity_check */ }; struct sc_pkcs15init_operations *sc_pkcs15init_get_starcos_ops(void) { return &sc_pkcs15init_starcos_operations; }