diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am index 99b1c3ad..59523dca 100644 --- a/src/libopensc/Makefile.am +++ b/src/libopensc/Makefile.am @@ -20,7 +20,7 @@ libopensc_la_SOURCES = \ pkcs15-prkey.c pkcs15-pubkey.c pkcs15-sec.c \ pkcs15-wrap.c pkcs15-algo.c pkcs15-cache.c pkcs15-syn.c \ \ - emv.c \ + emv.c muscle.c muscle-filesystem.c \ \ ctbcs.c reader-ctapi.c reader-pcsc.c reader-openct.c \ \ @@ -28,7 +28,7 @@ libopensc_la_SOURCES = \ card-cardos.c card-tcos.c card-emv.c card-default.c \ card-mcrd.c card-starcos.c card-openpgp.c card-jcop.c \ card-oberthur.c card-belpic.c card-atrust-acos.c \ - card-incrypto34.c card-piv.c \ + card-incrypto34.c card-piv.c card-muscle.c \ \ pkcs15-openpgp.c pkcs15-infocamere.c pkcs15-starcert.c \ pkcs15-tcos.c pkcs15-esteid.c pkcs15-postecert.c pkcs15-gemsafe.c \ @@ -41,7 +41,7 @@ include_HEADERS = \ cardctl.h asn1.h log.h ui.h \ errors.h types.h -noinst_HEADERS = cards.h ctbcs.h internal.h esteid.h +noinst_HEADERS = cards.h ctbcs.h internal.h esteid.h muscle.h muscle-filesystem.h pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libopensc.pc libpkcs15init.pc libscconf.pc diff --git a/src/libopensc/Makefile.mak b/src/libopensc/Makefile.mak index 8f6b3a47..5423c973 100644 --- a/src/libopensc/Makefile.mak +++ b/src/libopensc/Makefile.mak @@ -28,6 +28,7 @@ OBJECTS = \ card-mcrd.obj card-starcos.obj card-openpgp.obj card-jcop.obj \ card-oberthur.obj card-belpic.obj card-atrust-acos.obj \ card-incrypto34.obj card-piv.obj\ + muscle.obj card-muscle.obj muscle-filesystem.obj \ \ pkcs15-openpgp.obj pkcs15-infocamere.obj pkcs15-starcert.obj \ pkcs15-tcos.obj pkcs15-esteid.obj pkcs15-postecert.obj \ diff --git a/src/libopensc/card-muscle.c b/src/libopensc/card-muscle.c new file mode 100644 index 00000000..8996dccb --- /dev/null +++ b/src/libopensc/card-muscle.c @@ -0,0 +1,772 @@ +/* + * card-muscle.c: Support for MuscleCard Applet from musclecard.com + * + * Copyright (C) 2006, Identity Alliance, Thomas Harning + * + * 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 "internal.h" +#include "cardctl.h" +#include "muscle.h" +#include "muscle-filesystem.h" +#include +#include + +#include +#include + +/* ATR Values pulled from the Muscle Card library's 'bundle' */ +#if 0 +/* UNUSED */ +static struct sc_atr_table muscle_atrs[] = { + { "3B:75:13:00:00:9C:02:02:01:02", NULL, "Muscle card", SC_CARD_TYPE_MUSCLE_GENERIC, 0, NULL }, + { "3B:65:00:00:9C:02:02:01:02", NULL, "Muscle card", SC_CARD_TYPE_MUSCLE_GENERIC, 0, NULL }, + { "3B:3B:94:00:90:65:AF:03:0D:01:74:83:0F:90:00", NULL, "Muscle card", SC_CARD_TYPE_MUSCLE_GENERIC, 0, NULL }, + { "3F:6D:00:00:80:31:80:65:B0:05:01:02:5E:83:00:90:00", NULL, "Muscle card", SC_CARD_TYPE_MUSCLE_GENERIC, 0, NULL }, + { NULL, NULL, NULL, 0, 0, NULL } +}; +#endif + +static struct sc_card_operations muscle_ops; +static struct sc_card_driver muscle_drv = { + "Muscle Card Driver", + "muscle", + &muscle_ops, + NULL, 0, NULL +}; + +#define MUSCLE_DATA(card) ( (muscle_private_t*)card->drv_data ) +#define MUSCLE_FS(card) ( ((muscle_private_t*)card->drv_data)->fs ) +typedef struct muscle_private { + sc_security_env_t env; + unsigned short verifiedPins; + mscfs_t *fs; + int rsa_key_ref; + +} muscle_private_t; + +static int muscle_finish(sc_card_t *card) +{ + muscle_private_t *priv = MUSCLE_DATA(card); + mscfs_free(priv->fs); + free(priv); + return 0; +} + + +static u8 muscleAppletId[] = { 0xA0, 0x00,0x00,0x00, 0x01, 0x01 }; + +static int muscle_match_card(sc_card_t *card) +{ + /* Use SELECT APPLET, since its a more deterministic way of detection */ + return msc_select_applet(card, muscleAppletId, 5); +} + +/* Since Musclecard has a different ACL system then PKCS15 + * objects need to have their READ/UPDATE/DELETE permissions mapped for files + * and directory ACLS need to be set + * For keys.. they have different ACLS, but are accessed in different locations, so it shouldn't be an issue here + */ +static unsigned short muscle_parse_singleAcl(const sc_acl_entry_t* acl) +{ + unsigned short access = 0; + while(acl) { + int key = acl->key_ref; + int method = acl->method; + switch(method) { + case SC_AC_NEVER: + return 0xFFFF; + /* Ignore... other items overwrite these */ + case SC_AC_NONE: + case SC_AC_UNKNOWN: + break; + case SC_AC_CHV: + access |= (1 << key); /* Assuming key 0 == SO */ + break; + case SC_AC_AUT: + case SC_AC_TERM: + case SC_AC_PRO: + default: + /* Ignored */ + break; + } + acl = acl->next; + } + return access; +} + +static void muscle_parse_acls(const sc_file_t* file, unsigned short* read, unsigned short* write, unsigned short* delete) +{ + assert(read && write && delete); + *read = muscle_parse_singleAcl(sc_file_get_acl_entry(file, SC_AC_OP_READ)); + *write = muscle_parse_singleAcl(sc_file_get_acl_entry(file, SC_AC_OP_UPDATE)); + *delete = muscle_parse_singleAcl(sc_file_get_acl_entry(file, SC_AC_OP_DELETE)); +} + +static int muscle_create_directory(sc_card_t *card, sc_file_t *file) +{ + mscfs_t *fs = MUSCLE_FS(card); + u8 objectId[4]; + unsigned id = file->id; + unsigned short read = 0, write = 0, delete = 0; + int objectSize; + int r; + if(id == 0) /* No null name files */ + return SC_ERROR_INVALID_ARGUMENTS; + + /* No nesting directories */ + if(fs->currentPath[0] != 0x3F || fs->currentPath[1] != 0x00) + return SC_ERROR_NOT_SUPPORTED; + objectId[0] = ((id & 0xFF00) >> 8) & 0xFF; + objectId[1] = id & 0xFF; + objectId[2] = objectId[3] = 0; + + objectSize = file->size; + + muscle_parse_acls(file, &read, &write, &delete); + r = msc_create_object(card, *(int*)objectId, objectSize, read, write, delete); + mscfs_clear_cache(fs); + if(r >= 0) return 0; + return r; +} + + +static int muscle_create_file(sc_card_t *card, sc_file_t *file) +{ + mscfs_t *fs = MUSCLE_FS(card); + int objectSize = file->size; + unsigned short read = 0, write = 0, delete = 0; + unsigned int objectId; + int r; + if(file->type == SC_FILE_TYPE_DF) + return muscle_create_directory(card, file); + if(file->type != SC_FILE_TYPE_WORKING_EF) + return SC_ERROR_NOT_SUPPORTED; + if(file->id == 0) /* No null name files */ + return SC_ERROR_INVALID_ARGUMENTS; + + muscle_parse_acls(file, &read, &write, &delete); + + mscfs_lookup_local(fs, file->id, (u8*)&objectId); + r = msc_create_object(card, objectId, objectSize, read, write, delete); + mscfs_clear_cache(fs); + if(r >= 0) return 0; + return r; +} + +static int muscle_read_binary(sc_card_t *card, unsigned int index, u8* buf, size_t count, unsigned long flags) +{ + mscfs_t *fs = MUSCLE_FS(card); + int r; + u8 objectId[4]; + mscfs_file_t *file; + + r = mscfs_check_selection(fs, -1); + if(r < 0) SC_FUNC_RETURN(card->ctx, 0, r); + file = &fs->cache.array[fs->currentFileIndex]; + memcpy(objectId, file->objectId, 4); + if(!file->ef) { + objectId[0] = objectId[2]; + objectId[1] = objectId[3]; + objectId[2] = objectId[3] = 0; + } + r = msc_read_object(card, *(int*)objectId, index, buf, count); + SC_FUNC_RETURN(card->ctx, 0, r); +} + +static int muscle_update_binary(sc_card_t *card, unsigned int index, const u8* buf, size_t count, unsigned long flags) +{ + mscfs_t *fs = MUSCLE_FS(card); + int r; + mscfs_file_t *file; + u8 objectId[4]; + + r = mscfs_check_selection(fs, -1); + if(r < 0) SC_FUNC_RETURN(card->ctx, 0, r); + file = &fs->cache.array[fs->currentFileIndex]; + + memcpy(objectId, file->objectId, 4); + if(!file->ef) { + objectId[0] = objectId[2]; + objectId[1] = objectId[3]; + objectId[2] = objectId[3] = 0; + } + if(file->size < index + count) { + int newFileSize = index + count; + u8* buffer = malloc(newFileSize); + if(buffer == NULL) SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_OUT_OF_MEMORY); + + r = msc_read_object(card, *(int*)objectId, 0, buffer, file->size); + /* TODO: RETREIVE ACLS */ + if(r < 0) goto update_bin_free_buffer; + r = msc_delete_object(card, *(int*)objectId, 0); + if(r < 0) goto update_bin_free_buffer; + r = msc_create_object(card, *(int*)objectId, newFileSize, 0,0,0); + if(r < 0) goto update_bin_free_buffer; + memcpy(buffer + index, buf, count); + r = msc_update_object(card, *(int*)objectId, 0, buffer, newFileSize); + if(r < 0) goto update_bin_free_buffer; + file->size = newFileSize; +update_bin_free_buffer: + free(buffer); + SC_FUNC_RETURN(card->ctx, 0, r); + } else { + r = msc_update_object(card, *(int*)objectId, index, buf, count); + } + //mscfs_clear_cache(fs); + return r; +} + +static int muscle_delete_mscfs_file(sc_card_t *card, mscfs_file_t *file_data) +{ + mscfs_t *fs = MUSCLE_FS(card); + u8 *id = file_data->objectId; + int objectId = *(int*)id; + int r; + + if(!file_data->ef) { + int x; + mscfs_file_t *childFile; + /* Delete children */ + mscfs_check_cache(fs); + + if (card->ctx->debug >= 2) { + sc_debug(card->ctx, "DELETING Children of: %02X%02X%02X%02X\n", + id[0],id[1],id[2],id[3]); + } + for(x = 0; x < fs->cache.size; x++) { + u8 *objectId; + childFile = &fs->cache.array[x]; + objectId = childFile->objectId; + + if(0 == memcmp(id + 2, objectId, 2)) { + if (card->ctx->debug >= 2) { + sc_debug(card->ctx, "DELETING: %02X%02X%02X%02X\n", + objectId[0],objectId[1],objectId[2],objectId[3]); + } + r = muscle_delete_mscfs_file(card, childFile); + if(r < 0) SC_FUNC_RETURN(card->ctx, 2,r); + } + } + objectId = objectId >> 16; + } + + r = msc_delete_object(card, objectId, 1); + /* Check if its the root... this file generally is virtual + * So don't return an error if it fails */ + if((0 == memcmp(id, "\x3F\x00\x00\x00", 4)) + || (0 == memcmp(id, "\x3F\x00\x3F\x00", 4))) + return 0; + if(r < 0) SC_FUNC_RETURN(card->ctx, 2,r); + return 0; +} + +static int muscle_delete_file(sc_card_t *card, const sc_path_t *path_in) +{ + mscfs_t *fs = MUSCLE_FS(card); + mscfs_file_t *file_data = NULL; + int r = 0; + + r = mscfs_loadFileInfo(fs, path_in->value, path_in->len, &file_data, NULL); + if(r < 0) SC_FUNC_RETURN(card->ctx, 2,r); + r = muscle_delete_mscfs_file(card, file_data); + mscfs_clear_cache(fs); + if(r < 0) SC_FUNC_RETURN(card->ctx, 2,r); + return 0; +} + +static void muscle_load_single_acl(sc_file_t* file, int operation, unsigned short acl) +{ + int key; + // Everybody by default.... + sc_file_add_acl_entry(file, operation, SC_AC_NONE, 0); + if(acl == 0xFFFF) { + sc_file_add_acl_entry(file, operation, SC_AC_NEVER, 0); + return; + } + for(key = 0; key < 16; key++) { + if(acl >> key & 1) { + sc_file_add_acl_entry(file, operation, SC_AC_CHV, key); + } + } +} +static void muscle_load_file_acls(sc_file_t* file, mscfs_file_t *file_data) +{ + muscle_load_single_acl(file, SC_AC_OP_READ, file_data->read); + muscle_load_single_acl(file, SC_AC_OP_WRITE, file_data->write); + muscle_load_single_acl(file, SC_AC_OP_UPDATE, file_data->write); + muscle_load_single_acl(file, SC_AC_OP_DELETE, file_data->delete); +} +static void muscle_load_dir_acls(sc_file_t* file, mscfs_file_t *file_data) +{ + muscle_load_single_acl(file, SC_AC_OP_SELECT, 0); + muscle_load_single_acl(file, SC_AC_OP_LIST_FILES, 0); + muscle_load_single_acl(file, SC_AC_OP_LOCK, 0xFFFF); + muscle_load_single_acl(file, SC_AC_OP_DELETE, file_data->delete); + muscle_load_single_acl(file, SC_AC_OP_CREATE, file_data->write); +} + +/* Required type = -1 for don't care, 1 for EF, 0 for DF */ +static int select_item(sc_card_t *card, const sc_path_t *path_in, sc_file_t ** file_out, int requiredType) +{ + mscfs_t *fs = MUSCLE_FS(card); + mscfs_file_t *file_data = NULL; + const u8 *path = path_in->value; + int pathlen = path_in->len; + int r = 0; + int objectIndex; + + mscfs_check_cache(fs); + r = mscfs_loadFileInfo(fs, path_in->value, path_in->len, &file_data, &objectIndex); + if(r < 0) SC_FUNC_RETURN(card->ctx, 2,r); + + /* Check if its the right type */ + if(requiredType >= 0 && requiredType != file_data->ef) { + SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_INVALID_ARGUMENTS); + } + /* Is it a file or directory */ + if(file_data->ef) { + fs->currentPath[0] = file_data->objectId[0]; + fs->currentPath[1] = file_data->objectId[1]; + fs->currentFile[0] = file_data->objectId[2]; + fs->currentFile[1] = file_data->objectId[3]; + } else { + fs->currentPath[0] = file_data->objectId[pathlen - 2]; + fs->currentPath[1] = file_data->objectId[pathlen - 1]; + fs->currentFile[0] = 0; + fs->currentFile[1] = 0; + } + + fs->currentFileIndex = objectIndex; + if(file_out) { + sc_file_t *file; + file = sc_file_new(); + file->path = *path_in; + file->size = file_data->size; + file->id = (file_data->objectId[2] << 8) | file_data->objectId[3]; + memcpy(file->name, path, pathlen); + file->namelen = pathlen; + if(!file_data->ef) { + file->type = SC_FILE_TYPE_DF; + } else { + file->type = SC_FILE_TYPE_WORKING_EF; + file->ef_structure = SC_FILE_EF_TRANSPARENT; + } + + /* Setup ACLS */ + if(file_data->ef) { + muscle_load_file_acls(file, file_data); + } else { + muscle_load_dir_acls(file, file_data); + /* Setup directory acls... */ + } + + file->magic = SC_FILE_MAGIC; + *file_out = file; + } + return 0; +} + +static int muscle_select_file(sc_card_t *card, const sc_path_t *path_in, + sc_file_t **file_out) +{ + int r; + + assert(card != NULL && path_in != NULL); + + switch (path_in->type) { + case SC_PATH_TYPE_FILE_ID: + r = select_item(card, path_in, file_out, 1); + break; + case SC_PATH_TYPE_DF_NAME: + r = select_item(card, path_in, file_out, 0); + break; + case SC_PATH_TYPE_PATH: + r = select_item(card, path_in, file_out, -1); + break; + default: + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_INVALID_ARGUMENTS); + } + if(r > 0) r = 0; + SC_FUNC_RETURN(card->ctx, 2,r); +} + +static int _listFile(mscfs_file_t *file, int reset, void *udata) +{ + int next = reset ? 0x00 : 0x01; + return msc_list_objects( (sc_card_t*)udata, next, file); +} + +static int muscle_init(sc_card_t *card) +{ + int r = 0; + muscle_private_t *priv; + + card->name = "Muscle Card"; + card->drv_data = malloc(sizeof(muscle_private_t)); + if(!card->drv_data) { + SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_OUT_OF_MEMORY); + } + memset(card->drv_data, 0, sizeof(muscle_private_t)); + priv = MUSCLE_DATA(card); + priv->verifiedPins = 0; + priv->fs = mscfs_new(); + if(!priv->fs) { + free(card->drv_data); + SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_OUT_OF_MEMORY); + } + priv->fs->udata = card; + priv->fs->listFile = _listFile; + //r = autodetect_class(card); + card->cla = 0xB0; + if (r) { + sc_error(card->ctx, "unable to determine the right class byte\n"); + return SC_ERROR_INVALID_CARD; + } + card->flags |= SC_CARD_FLAG_ONBOARD_KEY_GEN; + card->flags |= SC_CARD_FLAG_RNG; + card->caps |= SC_CARD_CAP_RNG; + /* FIXME: Card type detection */ + if (1) { + unsigned long flags; + + flags = SC_ALGORITHM_RSA_RAW; + flags |= SC_ALGORITHM_RSA_HASH_NONE; + flags |= SC_ALGORITHM_ONBOARD_KEY_GEN; + + _sc_card_add_rsa_alg(card, 512, flags, 0); + _sc_card_add_rsa_alg(card, 768, flags, 0); + _sc_card_add_rsa_alg(card, 1024, flags, 0); + _sc_card_add_rsa_alg(card, 2048, flags, 0); + } + card->max_recv_size = 1024 * 64; + card->max_send_size = 1024 * 64; + return 0; +} + +static int muscle_list_files(sc_card_t *card, u8 *buf, size_t bufLen) +{ + muscle_private_t* priv = MUSCLE_DATA(card); + mscfs_t *fs = priv->fs; + int x; + int count = 0; + + mscfs_check_cache(priv->fs); + + for(x = 0; x < fs->cache.size; x++) { + u8 *objectId; + objectId = fs->cache.array[x].objectId; + if (card->ctx->debug >= 2) { + sc_debug(card->ctx, "FILE: %02X%02X%02X%02X\n", + objectId[0],objectId[1],objectId[2],objectId[3]); + } + if(0 == memcmp(fs->currentPath, objectId, 2)) { + buf[0] = objectId[2]; + buf[1] = objectId[3]; + if(buf[0] == 0x00 && buf[1] == 0x00) continue; /* No directories/null names outside of root */ + buf += 2; + count+=2; + } + } + return count; +} + +static int (*iso_pin_cmd)(struct sc_card *, struct sc_pin_cmd_data *, + int *tries_left); + +static int muscle_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *cmd, + int *tries_left) +{ + muscle_private_t* priv = MUSCLE_DATA(card); + switch(cmd->cmd) { + case SC_PIN_CMD_VERIFY: + switch(cmd->pin_type) { + case SC_AC_CHV: { + sc_apdu_t apdu; + int r; + msc_verify_pin_apdu(card, &apdu, cmd->pin_reference, cmd->pin1.data, cmd->pin1.len); + cmd->apdu = &apdu; + cmd->pin1.offset = 5; + r = iso_pin_cmd(card, cmd, tries_left); + if(r >= 0) + priv->verifiedPins |= (1 << cmd->pin_reference); + return r; + } + case SC_AC_TERM: + case SC_AC_PRO: + case SC_AC_AUT: + case SC_AC_NONE: + default: + sc_error(card->ctx, "Unsupported authentication method\n"); + return SC_ERROR_NOT_SUPPORTED; + } + case SC_PIN_CMD_CHANGE: + switch(cmd->pin_type) { + case SC_AC_CHV: { + sc_apdu_t apdu; + msc_change_pin_apdu(card, &apdu, cmd->pin_reference, cmd->pin1.data, cmd->pin1.len, cmd->pin2.data, cmd->pin2.len); + cmd->apdu = &apdu; + return iso_pin_cmd(card, cmd, tries_left); + } + case SC_AC_TERM: + case SC_AC_PRO: + case SC_AC_AUT: + case SC_AC_NONE: + default: + sc_error(card->ctx, "Unsupported authentication method\n"); + return SC_ERROR_NOT_SUPPORTED; + } + case SC_PIN_CMD_UNBLOCK: + switch(cmd->pin_type) { + case SC_AC_CHV: { + sc_apdu_t apdu; + msc_unblock_pin_apdu(card, &apdu, cmd->pin_reference, cmd->pin1.data, cmd->pin1.len); + cmd->apdu = &apdu; + return iso_pin_cmd(card, cmd, tries_left); + } + case SC_AC_TERM: + case SC_AC_PRO: + case SC_AC_AUT: + case SC_AC_NONE: + default: + sc_error(card->ctx, "Unsupported authentication method\n"); + return SC_ERROR_NOT_SUPPORTED; + } + default: + sc_error(card->ctx, "Unsupported command\n"); + return SC_ERROR_NOT_SUPPORTED; + + } + +} + +static int muscle_card_extract_key(sc_card_t *card, sc_cardctl_muscle_key_info_t *info) +{ + /* CURRENTLY DONT SUPPOT EXTRACTING PRIVATE KEYS... */ + switch(info->keyType) { + case 1: // RSA + return msc_extract_rsa_public_key(card, + info->keyLocation, + &info->modLength, + &info->modValue, + &info->expLength, + &info->expValue); + default: + return SC_ERROR_NOT_SUPPORTED; + } +} + +static int muscle_card_import_key(sc_card_t *card, sc_cardctl_muscle_key_info_t *info) +{ + /* CURRENTLY DONT SUPPOT EXTRACTING PRIVATE KEYS... */ + switch(info->keyType) { + case 0x02: // RSA_PRIVATE + case 0x03: // RSA_PRIVATE_CRT + return msc_import_key(card, + info->keyLocation, + info); + default: + return SC_ERROR_NOT_SUPPORTED; + } +} + +static int muscle_card_generate_key(sc_card_t *card, sc_cardctl_muscle_gen_key_info_t *info) +{ + return msc_generate_keypair(card, + info->privateKeyLocation, + info->publicKeyLocation, + info->keyType, + info->keySize, + 0); +} + +static int muscle_card_verified_pins(sc_card_t *card, sc_cardctl_muscle_verified_pins_info_t *info) +{ + muscle_private_t* priv = MUSCLE_DATA(card); + info->verifiedPins = priv->verifiedPins; + return 0; +} +static int muscle_card_ctl(sc_card_t *card, unsigned long request, void *data) +{ + switch(request) { + case SC_CARDCTL_MUSCLE_GENERATE_KEY: + return muscle_card_generate_key(card, (sc_cardctl_muscle_gen_key_info_t*) data); + case SC_CARDCTL_MUSCLE_EXTRACT_KEY: + return muscle_card_extract_key(card, (sc_cardctl_muscle_key_info_t*) data); + case SC_CARDCTL_MUSCLE_IMPORT_KEY: + return muscle_card_import_key(card, (sc_cardctl_muscle_key_info_t*) data); + case SC_CARDCTL_MUSCLE_VERIFIED_PINS: + return muscle_card_verified_pins(card, (sc_cardctl_muscle_verified_pins_info_t*) data); + default: + return SC_ERROR_NOT_SUPPORTED; /* Unsupported.. whatever it is */ + } +} + +static int muscle_set_security_env(sc_card_t *card, + const sc_security_env_t *env, + int se_num) +{ + muscle_private_t* priv = MUSCLE_DATA(card); + + if (env->operation != SC_SEC_OPERATION_SIGN && + env->operation != SC_SEC_OPERATION_DECIPHER) { + sc_error(card->ctx, "Invalid crypto operation supplied.\n"); + return SC_ERROR_NOT_SUPPORTED; + } + if (env->algorithm != SC_ALGORITHM_RSA) { + sc_error(card->ctx, "Invalid crypto algorithm supplied.\n"); + return SC_ERROR_NOT_SUPPORTED; + } + /* ADJUST FOR PKCS1 padding support for decryption only */ + if ((env->algorithm_flags & SC_ALGORITHM_RSA_PADS) || + (env->algorithm_flags & SC_ALGORITHM_RSA_HASHES)) { + sc_error(card->ctx, "Card supports only raw RSA.\n"); + return SC_ERROR_NOT_SUPPORTED; + } + if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) { + if (env->key_ref_len != 1 || + (env->key_ref[0] > 0x0F)) { + sc_error(card->ctx, "Invalid key reference supplied.\n"); + return SC_ERROR_NOT_SUPPORTED; + } + priv->rsa_key_ref = env->key_ref[0]; + } + if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT) { + sc_error(card->ctx, "Algorithm reference not supported.\n"); + return SC_ERROR_NOT_SUPPORTED; + } + //if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT) + // if (memcmp(env->file_ref.value, "\x00\x12", 2) != 0) { + // sc_error(card->ctx, "File reference is not 0012.\n"); + // return SC_ERROR_NOT_SUPPORTED; + // } + priv->env = *env; + return 0; +} + +static int muscle_restore_security_env(sc_card_t *card, int se_num) +{ + muscle_private_t* priv = MUSCLE_DATA(card); + memset(&priv->env, 0, sizeof(priv->env)); + return 0; +} + + +static int muscle_decipher(sc_card_t * card, + const u8 * crgram, size_t crgram_len, u8 * out, + size_t out_len) +{ + muscle_private_t* priv = MUSCLE_DATA(card); + + u8 key_id; + int r; + + /* saniti check */ + if (priv->env.operation != SC_SEC_OPERATION_DECIPHER) + return SC_ERROR_INVALID_ARGUMENTS; + + key_id = priv->rsa_key_ref * 2; /* Private key */ + + if (out_len < crgram_len) { + sc_error(card->ctx, "Output buffer too small"); + return SC_ERROR_BUFFER_TOO_SMALL; + } + + r = msc_compute_crypt(card, + key_id, + 0x00, /* RSA NO PADDING */ + 0x04, /* decrypt */ + crgram, + out, + crgram_len, + out_len); + SC_TEST_RET(card->ctx, r, "Card signature failed"); + return r; +} + +static int muscle_compute_signature(sc_card_t *card, const u8 *data, + size_t data_len, u8 * out, size_t outlen) +{ + muscle_private_t* priv = MUSCLE_DATA(card); + u8 key_id; + int r; + + key_id = priv->rsa_key_ref * 2; /* Private key */ + + if (outlen < data_len) { + sc_error(card->ctx, "Output buffer too small"); + return SC_ERROR_BUFFER_TOO_SMALL; + } + + r = msc_compute_crypt(card, + key_id, + 0x00, /* RSA NO PADDING */ + 0x04, /* -- decrypt raw... will do what we need since signing isn't yet supported */ + data, + out, + data_len, + outlen); + SC_TEST_RET(card->ctx, r, "Card signature failed"); + return r; +} + +static int muscle_get_challenge(sc_card_t *card, u8 *rnd, size_t len) +{ + return msc_get_challenge(card, len, 0, NULL, rnd); +} + +static int muscle_logout(sc_card_t *card) +{ + return 0; +} + +static struct sc_card_driver * sc_get_driver(void) +{ + struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); + + muscle_ops = *iso_drv->ops; + iso_pin_cmd = iso_drv->ops->pin_cmd; + muscle_ops.check_sw = iso_drv->ops->check_sw; + muscle_ops.pin_cmd = muscle_pin_cmd; + muscle_ops.get_response = iso_drv->ops->get_response; + muscle_ops.match_card = muscle_match_card; + muscle_ops.init = muscle_init; + muscle_ops.finish = muscle_finish; + muscle_ops.logout = muscle_logout; + + muscle_ops.get_challenge = muscle_get_challenge; + + muscle_ops.set_security_env = muscle_set_security_env; + muscle_ops.restore_security_env = muscle_restore_security_env; + muscle_ops.compute_signature = muscle_compute_signature; + muscle_ops.decipher = muscle_decipher; + muscle_ops.card_ctl = muscle_card_ctl; + muscle_ops.read_binary = muscle_read_binary; + muscle_ops.update_binary = muscle_update_binary; + muscle_ops.create_file = muscle_create_file; + muscle_ops.select_file = muscle_select_file; + muscle_ops.delete_file = muscle_delete_file; + muscle_ops.list_files = muscle_list_files; + + return &muscle_drv; +} + +#if 1 +struct sc_card_driver * sc_get_muscle_driver(void) +{ + return sc_get_driver(); +} +#endif diff --git a/src/libopensc/card.c b/src/libopensc/card.c index 253d1577..9cbf3d00 100644 --- a/src/libopensc/card.c +++ b/src/libopensc/card.c @@ -188,6 +188,8 @@ int sc_connect_card(sc_reader_t *reader, int slot_id, sc_card_t **card_out) sc_debug(ctx, "trying driver: %s\n", drv->short_name); if (ops == NULL || ops->match_card == NULL) continue; + /* Needed if match_card() needs to talk with the card (e.g. card-muscle) */ + *card->ops = *ops; if (ops->match_card(card) != 1) continue; if (ctx->debug >= 3) diff --git a/src/libopensc/cardctl.h b/src/libopensc/cardctl.h index c9279df5..1fb03486 100644 --- a/src/libopensc/cardctl.h +++ b/src/libopensc/cardctl.h @@ -121,7 +121,16 @@ enum { SC_CARDCTL_INCRYPTO34_PUT_DATA_SECI, SC_CARDCTL_INCRYPTO34_GENERATE_KEY, SC_CARDCTL_INCRYPTO34_CHANGE_KEY_DATA, - SC_CARDCTL_INCRYPTO34_ERASE_FILES + SC_CARDCTL_INCRYPTO34_ERASE_FILES, + + /* + * Muscle specific calls + */ + SC_CARDCTL_MUSCLE_BASE = _CTL_PREFIX('M','S','C'), + SC_CARDCTL_MUSCLE_GENERATE_KEY, + SC_CARDCTL_MUSCLE_EXTRACT_KEY, + SC_CARDCTL_MUSCLE_IMPORT_KEY, + SC_CARDCTL_MUSCLE_VERIFIED_PINS }; enum { @@ -347,6 +356,44 @@ struct sc_cardctl_setcos_gen_store_key_info { unsigned char *primeq; }; +/* + * Muscle stuff + */ +typedef struct sc_cardctl_muscle_gen_key_info { + int keyType; + int keySize; + int privateKeyLocation; + int publicKeyLocation; +} sc_cardctl_muscle_gen_key_info_t; + + +typedef struct sc_cardctl_muscle_key_info { + int keyType; + int keyLocation; + int keySize; + int modLength; + u8* modValue; + int expLength; + u8* expValue; + int pLength; + u8* pValue; + int qLength; + u8* qValue; + int pqLength; + u8* pqValue; + int dp1Length; + u8* dp1Value; + int dq1Length; + u8* dq1Value; + int gLength; + u8* gValue; + int yLength; + u8* yValue; +} sc_cardctl_muscle_key_info_t; + +typedef struct sc_cardctl_muscle_verified_pins_info { + unsigned verifiedPins; +} sc_cardctl_muscle_verified_pins_info_t; #ifdef __cplusplus } #endif diff --git a/src/libopensc/cards.h b/src/libopensc/cards.h index 14cca68c..76709829 100644 --- a/src/libopensc/cards.h +++ b/src/libopensc/cards.h @@ -114,7 +114,11 @@ enum { /* PIV-II type cards */ SC_CARD_TYPE_PIV_II_BASE = 14000, - SC_CARD_TYPE_PIV_II_GENERIC + SC_CARD_TYPE_PIV_II_GENERIC, + + /* Muscle cards */ + SC_CARD_TYPE_MUSCLE_BASE = 15000, + SC_CARD_TYPE_MUSCLE_GENERIC }; #ifdef __cplusplus diff --git a/src/libopensc/ctx.c b/src/libopensc/ctx.c index a2d3a9ac..99157454 100644 --- a/src/libopensc/ctx.c +++ b/src/libopensc/ctx.c @@ -70,6 +70,7 @@ static const struct _sc_driver_entry internal_card_drivers[] = { #endif { "belpic", (void *(*)(void)) sc_get_belpic_driver }, { "atrust-acos",(void *(*)(void))sc_get_atrust_acos_driver }, + { "muscle", (void *(*)(void)) sc_get_muscle_driver }, // Above EMV because the detection gets caught there first { "emv", (void *(*)(void)) sc_get_emv_driver }, { "incrypto34", (void *(*)(void)) sc_get_incrypto34_driver }, #ifdef HAVE_OPENSSL diff --git a/src/libopensc/muscle-filesystem.c b/src/libopensc/muscle-filesystem.c new file mode 100644 index 00000000..3cb9dd74 --- /dev/null +++ b/src/libopensc/muscle-filesystem.c @@ -0,0 +1,237 @@ +/* + * muscle-filesystem.c: Support for MuscleCard Applet from musclecard.com + * + * Copyright (C) 2006, Identity Alliance, Thomas Harning + * + * 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 "muscle-filesystem.h" +#include +#include +#include +#include + +#define MSCFS_NO_MEMORY SC_ERROR_OUT_OF_MEMORY +#define MSCFS_INVALID_ARGS SC_ERROR_INVALID_ARGUMENTS +#define MSCFS_FILE_NOT_FOUND SC_ERROR_FILE_NOT_FOUND +#define MSCFS_CACHE_INCREMENT 128 + + +static u8* ignoredFiles[] = { + "l0\0\0", + "L0\0\0", + NULL +}; + +mscfs_t *mscfs_new() { + mscfs_t *fs = (mscfs_t*)malloc(sizeof(mscfs_t)); + memset(fs, 0, sizeof(mscfs_t)); + memcpy(fs->currentPath, "\x3F\x00", 2); + return fs; +} + +void mscfs_free(mscfs_t *fs) { + mscfs_clear_cache(fs); +} + +void mscfs_clear_cache(mscfs_t* fs) { + if(!fs->cache.array) { + return; + } + free(fs->cache.array); + fs->cache.array = NULL; + fs->cache.totalSize = 0; + fs->cache.size = 0; +} + +int mscfs_is_ignored(mscfs_t* fs, u8* objectId) +{ + int ignored = 0; + u8** ptr = ignoredFiles; + while(ptr && *ptr && !ignored) { + if(0 == memcmp(objectId, *ptr, 4)) + ignored = 1; + ptr++; + } + return ignored; +} + +int mscfs_push_file(mscfs_t* fs, mscfs_file_t *file) +{ + mscfs_cache_t *cache = &fs->cache; + if(!cache->array || cache->size == cache->totalSize) { + int length = cache->totalSize + MSCFS_CACHE_INCREMENT; + mscfs_file_t *oldArray; + cache->totalSize = length; + oldArray = cache->array; + cache->array = malloc(sizeof(mscfs_file_t) * length); + if(!cache->array) + return MSCFS_NO_MEMORY; + if(oldArray) { + memcpy(cache->array, oldArray, sizeof(mscfs_file_t) * cache->size); + free(oldArray); + } + } + cache->array[cache->size] = *file; + cache->size++; + return 0; +} + +int mscfs_update_cache(mscfs_t* fs) { + mscfs_file_t file; + int r; + mscfs_clear_cache(fs); + r = fs->listFile(&file, 1, fs->udata); + if(r == 0) + return 0; + else if(r < 0) + return r; + while(1) { + if(!mscfs_is_ignored(fs, file.objectId)) { + /* Check if its a directory in the root */ + if(file.objectId[2] == 0 && file.objectId[3] == 0) { + file.objectId[2] = file.objectId[0]; + file.objectId[3] = file.objectId[1]; + file.objectId[0] = 0x3F; + file.objectId[1] = 0x00; + file.ef = 0; + } else { + file.ef = 1; /* File is a working elementary file */ + } + + mscfs_push_file(fs, &file); + } + r = fs->listFile(&file, 0, fs->udata); + if(r == 0) + break; + else if(r < 0) + return r; + } + return fs->cache.size; +} + +void mscfs_check_cache(mscfs_t* fs) +{ + if(!fs->cache.array) { + mscfs_update_cache(fs); + } +} + +int mscfs_lookup_path(mscfs_t* fs, const u8 *path, int pathlen, u8 objectId[4], int isDirectory) +{ + if ((pathlen & 1) != 0) /* not divisble by 2 */ + return MSCFS_INVALID_ARGS; + if(isDirectory) { + /* Directory must be right next to root */ + if((0 == memcmp(path, "\x3F\x00", 2) && pathlen == 4) + || (0 == memcmp(fs->currentPath, "\x3F\x00", 2) && pathlen == 2)) { + objectId[0] = path[pathlen - 2]; + objectId[1] = path[pathlen - 1]; + objectId[2] = objectId[3] = 0; + } else { + return MSCFS_INVALID_ARGS; + } + } + objectId[0] = fs->currentPath[0]; + objectId[1] = fs->currentPath[1]; + /* Chop off the root in the path */ + if(pathlen > 2 && memcmp(path, "\x3F\x00", 2) == 0) { + path += 2; + pathlen -= 2; + objectId[0] = 0x3F; + objectId[1] = 0x00; + } + /* Limit to a single directory */ + if(pathlen > 4) + return MSCFS_INVALID_ARGS; + /* Reset to root */ + if(0 == memcmp(path, "\x3F\x00", 2) && pathlen == 2) { + objectId[0] = objectId[2] = path[0]; + objectId[1] = objectId[3] = path[1]; + } else if(pathlen == 2) { /* Path preserved for current-path */ + objectId[2] = path[0]; + objectId[3] = path[1]; + } else if(pathlen == 4) { + objectId[0] = path[0]; + objectId[1] = path[1]; + objectId[2] = path[2]; + objectId[3] = path[3]; + } + + return 0; +} + +int mscfs_lookup_local(mscfs_t* fs, const int id, u8 objectId[4]) +{ + objectId[0] = fs->currentPath[0]; + objectId[1] = fs->currentPath[1]; + objectId[2] = (id >> 8) & 0xFF; + objectId[3] = id & 0xFF; + return 0; +} + +/* -1 any, 0 DF, 1 EF */ +int mscfs_check_selection(mscfs_t *fs, int requiredItem) +{ + if(fs->currentPath[0] == 0 && fs->currentPath[1] == 0) + return MSCFS_INVALID_ARGS; + if(requiredItem == 1 && fs->currentFile[0] == 0 && fs->currentFile[1] == 0) + return MSCFS_INVALID_ARGS; + return 0; +} + +int mscfs_loadFileInfo(mscfs_t* fs, const u8 *path, int pathlen, mscfs_file_t **file_data, int* index) +{ + u8 fullPath[4]; + int x; + assert(fs != NULL && path != NULL && file_data != NULL); + mscfs_lookup_path(fs, path, pathlen, fullPath, 0); + + /* Obtain file information while checking if it exists */ + mscfs_check_cache(fs); + if(index) *index = -1; + for(x = 0; x < fs->cache.size; x++) { + u8 *objectId; + *file_data = &fs->cache.array[x]; + objectId = (*file_data)->objectId; + if(0 == memcmp(objectId, fullPath, 4)) { + if(index) *index = x; + break; + } + *file_data = NULL; + } + if(*file_data == NULL && (0 == memcmp("\x3F\x00\x00\x00", fullPath, 4) || 0 == memcmp("\x3F\x00\x3F\x00", fullPath, 4 ))) { + static mscfs_file_t ROOT_FILE; + ROOT_FILE.ef = 0; + ROOT_FILE.size = 0; + /* Faked Root ID */ + ROOT_FILE.objectId[0] = 0x3F; + ROOT_FILE.objectId[1] = 0x00; + ROOT_FILE.objectId[2] = 0x3F; + ROOT_FILE.objectId[3] = 0x00; + + ROOT_FILE.read = 0; + ROOT_FILE.write = 0x02; /* User Pin access */ + ROOT_FILE.delete = 0x02; + + *file_data = &ROOT_FILE; + if(index) *index = -2; + } else if(*file_data == NULL) { + return MSCFS_FILE_NOT_FOUND; + } + + return 0; +} diff --git a/src/libopensc/muscle-filesystem.h b/src/libopensc/muscle-filesystem.h new file mode 100644 index 00000000..19febe92 --- /dev/null +++ b/src/libopensc/muscle-filesystem.h @@ -0,0 +1,67 @@ +/* + * muscle-filesystem.h: Support for MuscleCard Applet from musclecard.com + * + * Copyright (C) 2006, Identity Alliance, Thomas Harning + * + * 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 + */ + +#ifndef MUSCLE_FILESYSTEM_H +#define MUSCLE_FILESYSTEM_H + +#include + +#include + +typedef struct mscfs_file { + u8 objectId[4]; + size_t size; + unsigned short read, write, delete; + int ef; +} mscfs_file_t; + +typedef struct mscfs_cache { + int size; + int totalSize; + mscfs_file_t *array; +} mscfs_cache_t; + +typedef struct mscsfs { + u8 currentFile[2]; + u8 currentPath[2]; + int currentFileIndex; + mscfs_cache_t cache; + void* udata; + int (*listFile)(mscfs_file_t *fileOut, int reset, void* udata); +} mscfs_t; + +mscfs_t *mscfs_new(); +void mscfs_free(mscfs_t *fs); +void mscfs_clear_cache(mscfs_t* fs); +int mscfs_push_file(mscfs_t* fs, mscfs_file_t *file); +int mscfs_update_cache(mscfs_t* fs); + +void mscfs_check_cache(mscfs_t* fs); + +int mscfs_lookup_path(mscfs_t* fs, const u8 *path, int pathlen, u8 objectId[4], int +isDirectory); + +int mscfs_lookup_local(mscfs_t* fs, const int id, u8 objectId[4]); +/* -1 any, 0 DF, 1 EF */ +int mscfs_check_selection(mscfs_t *fs, int requiredItem); +int mscfs_loadFileInfo(mscfs_t* fs, const u8 *path, int pathlen, mscfs_file_t **file_data, int* index); + + +#endif diff --git a/src/libopensc/muscle.c b/src/libopensc/muscle.c new file mode 100644 index 00000000..f1bb78fe --- /dev/null +++ b/src/libopensc/muscle.c @@ -0,0 +1,1009 @@ +/* + * muscle.c: Support for MuscleCard Applet from musclecard.com + * + * Copyright (C) 2006, Identity Alliance, Thomas Harning + * + * 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 "muscle.h" +#include "internal.h" + +#include + +#define MSC_MAX_WRITE_UNIT 255 +#define MSC_MAX_READ_UNIT 246 + +#define MSC_MAX_CRYPTINIT_DATA (255 - 5) +#define MSC_MAX_CRYPTPROCESS_DATA (255 - 3) + +#define MSC_RSA_PUBLIC 0x01 +#define MSC_RSA_PRIVATE 0x02 +#define MSC_RSA_PRIVATE_CRT 0x03 +#define MSC_DSA_PUBLIC 0x04 +#define MSC_DSA_PRIVATE 0x05 + +#ifndef MAX +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#endif +#ifndef MIN +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif + +#if defined(_WIN32) || defined(_WIN64) +static unsigned long bswap_32(unsigned long x) +{ + unsigned long res = x % 256; + x /= 256; + res = 256 * res + x % 256; + x /= 256; + res = 256 * res + x % 256; + x /= 256; + return 256 * res + x % 256; +} +static unsigned short bswap_16(unsigned short x) +{ + return 256 * (x % 256) + (x / 256); +} +#define BIG_ENDIAN 1 +#else +#include +#include +#endif + +int msc_list_objects(sc_card_t* card, u8 next, mscfs_file_t* file) { + sc_apdu_t apdu; + u8 fileData[14]; + int r; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0x58, next, 0x00); + apdu.cla = 0xB0; + apdu.le = 14; + apdu.resplen = 14; + apdu.resp = fileData; + r = sc_transmit_apdu(card, &apdu); + if (r) + return r; + + if(apdu.sw1 == 0x9C && apdu.sw2 == 0x12) { + return 0; + } + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (r) + return r; + if(apdu.resplen == 0) /* No more left */ + return 0; + if (apdu.resplen != 14) { + sc_error(card->ctx, "expected 14 bytes, got %d.\n", apdu.resplen); + return SC_ERROR_UNKNOWN_DATA_RECEIVED; + } + memcpy(file->objectId, fileData, 4); + file->size = *(int*)(fileData + 4); + file->read = *(short*)(fileData + 8); + file->write = *(short*)(fileData + 10); + file->delete = *(short*)(fileData + 12); + + if(BIG_ENDIAN) { + file->size = bswap_32(file->size); + file->read = bswap_16(file->read); + file->write = bswap_16(file->write); + file->delete = bswap_16(file->delete); + } + return 1; +} + +int msc_partial_read_object(sc_card_t *card, unsigned int le_objectId, int offset, u8 *data, size_t dataLength) +{ + u8 buffer[9]; + sc_apdu_t apdu; + int r; + unsigned int le_offset = offset; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x56, 0x00, 0x00); + + if(BIG_ENDIAN) + le_offset = bswap_32(le_offset); + if (card->ctx->debug >= 2) + sc_debug(card->ctx, "READ: Offset: %x\tLength: %i\n", le_offset, dataLength); + memcpy(buffer, &le_objectId, 4); + memcpy(buffer + 4, &le_offset, 4); + buffer[8] = (u8)dataLength; + apdu.data = buffer; + apdu.datalen = 9; + apdu.lc = 9; + apdu.le = dataLength; + apdu.resplen = dataLength; + apdu.resp = data; + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) + return dataLength; + if(apdu.sw1 == 0x9C) { + if(apdu.sw2 == 0x07) { + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_FILE_NOT_FOUND); + } else if(apdu.sw2 == 0x06) { + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_NOT_ALLOWED); + } else if(apdu.sw2 == 0x0F) { + /* GUESSED */ + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_INVALID_ARGUMENTS); + } + } + if (card->ctx->debug >= 2) { + sc_debug(card->ctx, "got strange SWs: 0x%02X 0x%02X\n", + apdu.sw1, apdu.sw2); + } + return dataLength; + +} + +int msc_read_object(sc_card_t *card, unsigned int objectId, int offset, u8 *data, size_t dataLength) +{ + int r; + int i; + for(i = 0; i < dataLength; i += MSC_MAX_WRITE_UNIT) { + r = msc_partial_read_object(card, objectId, offset + i, data + i, MIN(dataLength - i, MSC_MAX_WRITE_UNIT)); + SC_TEST_RET(card->ctx, r, "Error in partial object read"); + } + return dataLength; +} + +int msc_zero_object(sc_card_t *card, unsigned int objectId, size_t dataLength) +{ + u8 zeroBuffer[MSC_MAX_WRITE_UNIT]; + int i; + memset(zeroBuffer, 0, MSC_MAX_WRITE_UNIT); + for(i = 0; i < dataLength; i += MSC_MAX_WRITE_UNIT) { + int r = msc_partial_update_object(card, objectId, i, zeroBuffer, MIN(dataLength - i, MSC_MAX_WRITE_UNIT)); + SC_TEST_RET(card->ctx, r, "Error in zeroing file update"); + } + return 0; +} + +int msc_create_object(sc_card_t *card, unsigned int objectId, size_t objectSize, unsigned short read, unsigned short write, unsigned short deletion) +{ + u8 buffer[14]; + sc_apdu_t apdu; + unsigned short readAcl = read, writeAcl = write, deleteAcl = deletion; + int r; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x5A, 0x00, 0x00); + apdu.lc = 14; + apdu.data = buffer, + apdu.datalen = 14; + + if(BIG_ENDIAN) { + objectSize = bswap_32(objectSize); + readAcl = bswap_16(readAcl); + writeAcl = bswap_16(writeAcl); + deleteAcl = bswap_16(deleteAcl); + } + memcpy(buffer, &objectId, 4); + memcpy(buffer + 4, &objectSize, 4); + memcpy(buffer + 8, &readAcl, 2); + memcpy(buffer + 10, &writeAcl, 2); + memcpy(buffer + 12, &deleteAcl, 2); + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) + return BIG_ENDIAN ? bswap_32(objectSize) : objectSize; + if(apdu.sw1 == 0x9C) { + if(apdu.sw2 == 0x01) { + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_MEMORY_FAILURE); + } else if(apdu.sw2 == 0x08) { + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_FILE_ALREADY_EXISTS); + } else if(apdu.sw2 == 0x06) { + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_NOT_ALLOWED); + } + } + if (card->ctx->debug >= 2) { + sc_debug(card->ctx, "got strange SWs: 0x%02X 0x%02X\n", + apdu.sw1, apdu.sw2); + } + msc_zero_object(card, objectId, BIG_ENDIAN ? bswap_32(objectSize) : objectSize); + return BIG_ENDIAN ? bswap_32(objectSize) : objectSize; +} + +/* Update up to 246 bytes */ +int msc_partial_update_object(sc_card_t *card, unsigned int le_objectId, int offset, const u8 *data, size_t dataLength) +{ + u8 buffer[256]; + sc_apdu_t apdu; + unsigned int le_offset; + int r; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x54, 0x00, 0x00); + apdu.lc = dataLength + 9; + le_offset = offset; + if(BIG_ENDIAN) + le_offset = bswap_32(le_offset); + if (card->ctx->debug >= 2) + sc_debug(card->ctx, "WRITE: Offset: %x\tLength: %i\n", le_offset, dataLength); + memcpy(buffer, &le_objectId, 4); + memcpy(buffer + 4, &le_offset, 4); + buffer[8] = (u8)dataLength; + memcpy(buffer + 9, data, dataLength); + apdu.data = buffer; + apdu.datalen = apdu.lc; + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) + return dataLength; + if(apdu.sw1 == 0x9C) { + if(apdu.sw2 == 0x07) { + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_FILE_NOT_FOUND); + } else if(apdu.sw2 == 0x06) { + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_NOT_ALLOWED); + } else if(apdu.sw2 == 0x0F) { + /* GUESSED */ + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_INVALID_ARGUMENTS); + } + } + if (card->ctx->debug >= 2) { + sc_debug(card->ctx, "got strange SWs: 0x%02X 0x%02X\n", + apdu.sw1, apdu.sw2); + } + return dataLength; +} + +int msc_update_object(sc_card_t *card, unsigned int objectId, int offset, const u8 *data, size_t dataLength) +{ + int r, i; + for(i = 0; i < dataLength; i += MSC_MAX_READ_UNIT) { + r = msc_partial_update_object(card, objectId, offset + i, data + i, MIN(dataLength - i, MSC_MAX_READ_UNIT)); + SC_TEST_RET(card->ctx, r, "Error in partial object update"); + } + return dataLength; +} + +int msc_delete_object(sc_card_t *card, unsigned int objectId, int zero) +{ + sc_apdu_t apdu; + int r; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x52, 0x00, zero ? 0x01 : 0x00); + apdu.lc = 4; + apdu.data = (u8*)&objectId; + apdu.datalen = 4; + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) + return 0; + if(apdu.sw1 == 0x9C) { + if(apdu.sw2 == 0x07) { + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_FILE_NOT_FOUND); + } else if(apdu.sw2 == 0x06) { + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_NOT_ALLOWED); + } + } + if (card->ctx->debug >= 2) { + sc_debug(card->ctx, "got strange SWs: 0x%02X 0x%02X\n", + apdu.sw1, apdu.sw2); + } + return 0; +} + +int msc_select_applet(sc_card_t *card, u8 *appletId, size_t appletIdLength) +{ + sc_apdu_t apdu; + int r; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 4, 0); + apdu.lc = appletIdLength; + apdu.data = appletId; + apdu.datalen = appletIdLength; + apdu.resplen = 0; + apdu.le = 0; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) + return 1; + + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_CARD_CMD_FAILED); +} + +int msc_verify_pin(sc_card_t *card, int pinNumber, const u8 *pinValue, int pinLength, int *tries) +{ + sc_apdu_t apdu; + int r; + + msc_verify_pin_apdu(card, &apdu, pinNumber, pinValue, pinLength); + if(tries) + *tries = -1; + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { + return 0; + } else if(apdu.sw1 == 0x63) { /* Invalid auth */ + if(tries) + *tries = apdu.sw2 & 0x0F; + SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_PIN_CODE_INCORRECT); + } else if(apdu.sw1 == 0x9C && apdu.sw2 == 0x02) { + SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_PIN_CODE_INCORRECT); + } else if(apdu.sw1 == 0x69 && apdu.sw2 == 0x83) { + SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_AUTH_METHOD_BLOCKED); + } + + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_CARD_CMD_FAILED); +} + +/* USE ISO_VERIFY due to tries return */ +void msc_verify_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, int pinNumber, const u8 *pinValue, int pinLength) +{ + /* FORCE PIN TO END AFTER LAST NULL */ + for(; pinLength > 0; pinLength--) { + if(pinValue[pinLength - 1]) break; + } + sc_format_apdu(card, apdu, SC_APDU_CASE_3_SHORT, 0x42, pinNumber, 0); + apdu->lc = pinLength; + apdu->data = pinValue; + apdu->datalen = pinLength; +} + +int msc_unblock_pin(sc_card_t *card, int pinNumber, const u8 *pukValue, int pukLength, int *tries) +{ + sc_apdu_t apdu; + int r; + + msc_unblock_pin_apdu(card, &apdu, pinNumber, pukValue, pukLength); + if(tries) + *tries = -1; + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { + return 0; + } else if(apdu.sw1 == 0x63) { /* Invalid auth */ + if(tries) + *tries = apdu.sw2 & 0x0F; + SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_PIN_CODE_INCORRECT); + } else if(apdu.sw1 == 0x9C && apdu.sw2 == 0x02) { + SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_PIN_CODE_INCORRECT); + } else if(apdu.sw1 == 0x69 && apdu.sw2 == 0x83) { + SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_AUTH_METHOD_BLOCKED); + } + + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_CARD_CMD_FAILED); +} + +void msc_unblock_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, int pinNumber, const u8 *pukValue, int pukLength) +{ + sc_format_apdu(card, apdu, SC_APDU_CASE_3_SHORT, 0x46, pinNumber, 0); + apdu->lc = pukLength; + apdu->data = pukValue; + apdu->datalen = pukLength; +} + +int msc_change_pin(sc_card_t *card, int pinNumber, const u8 *pinValue, int pinLength, const u8 *newPin, int newPinLength, int *tries) +{ + sc_apdu_t apdu; + int r; + + msc_change_pin_apdu(card, &apdu, pinNumber, pinValue, pinLength, newPin, newPinLength); + if(tries) + *tries = -1; + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { + return 0; + } else if(apdu.sw1 == 0x63) { /* Invalid auth */ + if(tries) + *tries = apdu.sw2 & 0x0F; + SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_PIN_CODE_INCORRECT); + } else if(apdu.sw1 == 0x9C && apdu.sw2 == 0x02) { + SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_PIN_CODE_INCORRECT); + } else if(apdu.sw1 == 0x69 && apdu.sw2 == 0x83) { + SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_AUTH_METHOD_BLOCKED); + } + + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_CARD_CMD_FAILED); +} + +/* USE ISO_VERIFY due to tries return */ +void msc_change_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, int pinNumber, const u8 *pinValue, int pinLength, const u8 *newPin, int newPinLength) +{ + u8 pinData[512]; /* Absolute max size 255 * 2 + 2 */ + u8 *ptr = pinData; + + sc_format_apdu(card, apdu, SC_APDU_CASE_3_SHORT, 0x44, pinNumber, 0); + *ptr = pinLength; + ptr++; + memcpy(ptr, pinValue, pinLength); + ptr += pinLength; + *ptr = newPinLength; + ptr++; + memcpy(ptr, newPin, newPinLength); + ptr += newPinLength; + apdu->lc = pinLength + newPinLength + 2; + apdu->datalen = apdu->lc; + apdu->data = pinData; +} + +int msc_get_challenge(sc_card_t *card, short dataLength, short seedLength, u8 *seedData, u8* outputData) +{ + sc_apdu_t apdu; + int r, location, cse, len; + short dataLength_le = dataLength; + short seedLength_le = seedLength; + u8 *buffer, *ptr; + + location = (dataLength < 255) ? 1 : 2; /* 1 == APDU, 2 == (seed in 0xFFFFFFFE, out in 0xFFFFFFFF) */ + cse = (location == 1) ? SC_APDU_CASE_4_SHORT : SC_APDU_CASE_3_SHORT; + len = seedLength + 4; + + assert(seedLength < 251); + assert(dataLength < 255); /* Output buffer doesn't seem to operate as desired.... nobody can read/delete */ + if(BIG_ENDIAN) { + dataLength_le = bswap_16(dataLength_le); + seedLength_le = bswap_16(seedLength_le); + } + + buffer = malloc(len); + if(!buffer) SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_OUT_OF_MEMORY); + ptr = buffer; + memcpy(ptr, &dataLength_le, 2); + ptr+=2; + memcpy(ptr, &seedLength_le, 2); + ptr+=2; + if(seedLength > 0) { + memcpy(ptr, seedData, seedLength); + } + sc_format_apdu(card, &apdu, cse, 0x72, 0x00, location); + apdu.data = buffer; + apdu.datalen = len; + apdu.lc = len; + + if(location == 1) { + u8* outputBuffer = malloc(dataLength + 2); + if(outputBuffer == NULL) SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_OUT_OF_MEMORY); + apdu.le = dataLength + 2; + apdu.resp = outputBuffer; + apdu.resplen = dataLength + 2; + } + r = sc_transmit_apdu(card, &apdu); + if(location == 1) { + memcpy(outputData, apdu.resp + 2, dataLength); + free(apdu.resp); + } + free(buffer); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + if(location == 1) { + if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { + return dataLength; + } else { + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (r) { + if (card->ctx->debug >= 2) { + sc_debug(card->ctx, "got strange SWs: 0x%02X 0x%02X\n", + apdu.sw1, apdu.sw2); + } + SC_FUNC_RETURN(card->ctx, 0, r); + } + SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_CARD_CMD_FAILED); + } + } else { + if(apdu.sw1 != 0x90 || apdu.sw2 != 0x00) { + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (r) { + if (card->ctx->debug >= 2) { + sc_debug(card->ctx, "got strange SWs: 0x%02X 0x%02X\n", + apdu.sw1, apdu.sw2); + } + SC_FUNC_RETURN(card->ctx, 0, r); + } + SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_CARD_CMD_FAILED); + } + r = msc_read_object(card, 0xFFFFFFFFul, 2, outputData, dataLength); + if(r < 0) + SC_FUNC_RETURN(card->ctx, 0, r); + r = msc_delete_object(card, 0xFFFFFFFFul,0); + SC_FUNC_RETURN(card->ctx, 0, r); + } +} + +int msc_generate_keypair(sc_card_t *card, int privateKey, int publicKey, int algorithm, int keySize, int options) +{ + sc_apdu_t apdu; + u8 buffer[256]; + u8 *ptr = buffer; + int r; + unsigned short prRead = 0xFFFF, prWrite = 0x0002, prCompute = 0x0002, + puRead = 0x0000, puWrite = 0x0002, puCompute = 0x0000; + + assert(privateKey <= 0x0F && publicKey <= 0x0F); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x30, privateKey, publicKey); + if(BIG_ENDIAN) { + keySize = bswap_16(keySize); + prRead = bswap_16(prRead); + prWrite = bswap_16(prWrite); + prCompute = bswap_16(prCompute); + puRead = bswap_16(puRead); + puWrite = bswap_16(puWrite); + puCompute = bswap_16(puCompute); + } + *ptr = algorithm; ptr++; + + memcpy(ptr, &keySize, 2); + ptr+=2; + + memcpy(ptr, &prRead, 2); + ptr+=2; + memcpy(ptr, &prWrite, 2); + ptr+=2; + memcpy(ptr, &prCompute, 2); + ptr+=2; + + memcpy(ptr, &puRead, 2); + ptr+=2; + memcpy(ptr, &puWrite, 2); + ptr+=2; + memcpy(ptr, &puCompute, 2); + ptr+=2; + + *ptr = 0; /* options; -- no options for now, they need extra data */ + + apdu.data = buffer; + apdu.datalen = 16; + apdu.lc = 16; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { + return 0; + } + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (r) { + if (card->ctx->debug >= 2) { + sc_debug(card->ctx, "got strange SWs: 0x%02X 0x%02X\n", + apdu.sw1, apdu.sw2); + } + SC_FUNC_RETURN(card->ctx, 0, r); + } + SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_CARD_CMD_FAILED); +} + +int msc_extract_key(sc_card_t *card, + int keyLocation) +{ + sc_apdu_t apdu; + u8 encoding = 0; + int r; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x34, keyLocation, 0x00); + apdu.data = &encoding; + apdu.datalen = 1; + apdu.lc = 1; + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { + return 0; + } + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (r) { + if (card->ctx->debug >= 2) { + sc_debug(card->ctx, "got strange SWs: 0x%02X 0x%02X\n", + apdu.sw1, apdu.sw2); + } + SC_FUNC_RETURN(card->ctx, 0, r); + } + SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_CARD_CMD_FAILED); +} + +int msc_extract_rsa_public_key(sc_card_t *card, + int keyLocation, + int* modLength, + u8** modulus, + int* expLength, + u8** exponent) +{ + int r; + const int buffer_size = 1024; + u8 buffer[1024]; /* Should be plenty... */ + int fileLocation = 1; + + assert(modLength && expLength && modulus && exponent); + r = msc_extract_key(card, keyLocation); + if(r < 0) SC_FUNC_RETURN(card->ctx, 0, r); + + /* Read keyType, keySize, and what should be the modulus size */ + r = msc_read_object(card, 0xFFFFFFFFul, fileLocation, buffer, 5); + fileLocation += 5; + if(r < 0) SC_FUNC_RETURN(card->ctx, 0, r); + + if(buffer[0] != MSC_RSA_PUBLIC) SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_UNKNOWN_DATA_RECEIVED); + *modLength = (buffer[3] << 8) | buffer[4]; + /* Read the modulus and the exponent length */ + assert(*modLength + 2 < buffer_size); + + r = msc_read_object(card, 0xFFFFFFFFul, fileLocation, buffer, *modLength + 2); + fileLocation += *modLength + 2; + if(r < 0) SC_FUNC_RETURN(card->ctx, 0, r); + + *modulus = malloc(*modLength); + if(!*modulus) SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_OUT_OF_MEMORY); + memcpy(*modulus, buffer, *modLength); + *expLength = (buffer[*modLength] << 8) | buffer[*modLength + 1]; + assert(*expLength < buffer_size); + r = msc_read_object(card, 0xFFFFFFFFul, fileLocation, buffer, *expLength); + if(r < 0) { + free(*modulus); *modulus = NULL; + SC_FUNC_RETURN(card->ctx, 0, r); + } + *exponent = malloc(*expLength); + if(!*exponent) { + free(*modulus); + SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_OUT_OF_MEMORY); + } + memcpy(*exponent, buffer, *expLength); + return 0; +} + + + +/* For the moment, only support streaming data to the card + in blocks, not through file IO */ +int msc_compute_crypt_init(sc_card_t *card, + int keyLocation, + int cipherMode, + int cipherDirection, + const u8* initData, + u8* outputData, + size_t dataLength, + size_t* outputDataLength) +{ + sc_apdu_t apdu; + u8 buffer[255]; + u8 *ptr; + int r; + + u8 outputBuffer[255]; + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x36, keyLocation, 0x01); /* Init */ + apdu.data = buffer; + apdu.datalen = dataLength + 5; + apdu.lc = dataLength + 5; + + memset(outputBuffer, 0, sizeof(outputBuffer)); + apdu.resp = outputBuffer; + apdu.resplen = dataLength + 2; + apdu.le = dataLength + 2; + ptr = buffer; + *ptr = cipherMode; ptr++; + *ptr = cipherDirection; ptr++; + *ptr = 0x01; ptr++; /* DATA LOCATION: APDU */ + *ptr = (dataLength >> 8) & 0xFF; ptr++; + *ptr = dataLength & 0xFF; ptr++; + memcpy(ptr, initData, dataLength); + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { + short receivedData = outputBuffer[0] << 8 | outputBuffer[1]; + *outputDataLength = receivedData; + *outputDataLength = 0; + assert(receivedData <= 255); + memcpy(outputData, outputBuffer + 2, receivedData); + return 0; + } + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (r) { + if (card->ctx->debug >= 2) { + sc_debug(card->ctx, "init: got strange SWs: 0x%02X 0x%02X\n", + apdu.sw1, apdu.sw2); + } + SC_FUNC_RETURN(card->ctx, 0, r); + } + SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_CARD_CMD_FAILED); +} + +int msc_compute_crypt_process( + sc_card_t *card, + int keyLocation, + const u8* inputData, + u8* outputData, + size_t dataLength, + size_t* outputDataLength) +{ + sc_apdu_t apdu; + u8 buffer[255]; + u8 outputBuffer[255]; + u8 *ptr; + int r; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x36, keyLocation, 0x02); /* Process */ + + apdu.data = buffer; + apdu.datalen = dataLength + 3; + apdu.lc = dataLength + 3; +/* Specs say crypt returns data all the time??? But... its not implemented that way */ + + memset(outputBuffer, 0, sizeof(outputBuffer)); + apdu.resp = outputBuffer; + apdu.resplen = 255; + apdu.le = dataLength; + ptr = buffer; + *ptr = 0x01; ptr++; /* DATA LOCATION: APDU */ + *ptr = (dataLength >> 8) & 0xFF; ptr++; + *ptr = dataLength & 0xFF; ptr++; + memcpy(ptr, inputData, dataLength); + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { + short receivedData = outputBuffer[0] << 8 | outputBuffer[1]; + *outputDataLength = receivedData; + *outputDataLength = 0; + assert(receivedData <= 255); + memcpy(outputData, outputBuffer + 2, receivedData); + return 0; + } + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (r) { + if (card->ctx->debug >= 2) { + sc_debug(card->ctx, "process: got strange SWs: 0x%02X 0x%02X\n", + apdu.sw1, apdu.sw2); + } + SC_FUNC_RETURN(card->ctx, 0, r); + } + SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_CARD_CMD_FAILED); +} + +int msc_compute_crypt_final( + sc_card_t *card, + int keyLocation, + const u8* inputData, + u8* outputData, + size_t dataLength, + size_t* outputDataLength) +{ + sc_apdu_t apdu; + u8 buffer[255]; + u8 outputBuffer[255]; + u8 *ptr; + int r; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x36, keyLocation, 0x03); /* Final */ + + apdu.data = buffer; + apdu.datalen = dataLength + 3; + apdu.lc = dataLength + 3; + + memset(outputBuffer, 0, sizeof(outputBuffer)); + apdu.resp = outputBuffer; + apdu.resplen = 255; + apdu.le = 255; + ptr = buffer; + *ptr = 0x01; ptr++; /* DATA LOCATION: APDU */ + *ptr = (dataLength >> 8) & 0xFF; ptr++; + *ptr = dataLength & 0xFF; ptr++; + memcpy(ptr, inputData, dataLength); + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { + short receivedData = outputBuffer[0] << 8 | outputBuffer[1]; + *outputDataLength = receivedData; + assert(receivedData <= 255); + memcpy(outputData, outputBuffer + 2, receivedData); + return 0; + } + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (r) { + if (card->ctx->debug >= 2) { + sc_debug(card->ctx, "final: got strange SWs: 0x%02X 0x%02X\n", + apdu.sw1, apdu.sw2); + } + SC_FUNC_RETURN(card->ctx, 0, r); + } + SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_CARD_CMD_FAILED); +} + +int msc_compute_crypt(sc_card_t *card, + int keyLocation, + int cipherMode, + int cipherDirection, + const u8* data, + u8* outputData, + size_t dataLength, + size_t outputDataLength) +{ + int left = dataLength; + const u8* inPtr = data; + u8* outPtr = outputData; + int toSend; + int r; + + size_t received = 0; + assert(outputDataLength >= dataLength); + + /* Don't send data during init... apparently current version does not support it */ + toSend = 0; + r = msc_compute_crypt_init(card, + keyLocation, + cipherMode, + cipherDirection, + inPtr, + outPtr, + toSend, + &received); + if(r < 0) SC_FUNC_RETURN(card->ctx, 0, r); + left -= toSend; + inPtr += toSend; + outPtr += received; + while(left > MSC_MAX_CRYPTPROCESS_DATA) { + toSend = MIN(left, MSC_MAX_CRYPTINIT_DATA); + r = msc_compute_crypt_process(card, + keyLocation, + inPtr, + outPtr, + toSend, + &received); + if(r < 0) SC_FUNC_RETURN(card->ctx, 0, r); + left -= toSend; + inPtr += toSend; + outPtr += received; + } + toSend = MIN(left, MSC_MAX_CRYPTINIT_DATA); + r = msc_compute_crypt_final(card, + keyLocation, + inPtr, + outPtr, + toSend, + &received); + if(r < 0) SC_FUNC_RETURN(card->ctx, 0, r); + left -= toSend; + inPtr += toSend; + outPtr += received; + return outPtr - outputData; /* Amt received */ +} + +/* USED IN KEY ITEM WRITING */ +#define CPYVAL(valName) \ + length = BIG_ENDIAN ? bswap_16(data->valName ## Length) : data->valName ## Length; \ + memcpy(p, &length, 2); p+= 2; \ + memcpy(p, data->valName ## Value, data->valName ## Length); p+= data->valName ## Length + +int msc_import_key(sc_card_t *card, + int keyLocation, + sc_cardctl_muscle_key_info_t *data) +{ + unsigned short read = 0xFFFF, + write = 0x0002, + use = 0x0002, + keySize = data->keySize; + int bufferSize = 0; + u8 *buffer, *p; + unsigned int objectId; + u8 apduBuffer[6]; + sc_apdu_t apdu; + int r; + + assert(data->keyType == 0x02 || data->keyType == 0x03); + if(data->keyType == 0x02) { + if( (data->pLength == 0 || !data->pValue) + || (data->modLength == 0 || !data->modValue)) + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_INVALID_ARGUMENTS); + } else if(data->keyType == 0x03) { + if( (data->pLength == 0 || !data->pValue) + || (data->qLength == 0 || !data->qValue) + || (data->pqLength == 0 || !data->pqValue) + || (data->dp1Length == 0 || !data->dp1Value) + || (data->dq1Length == 0 || !data->dq1Value)) + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_INVALID_ARGUMENTS); + } else { + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_INVALID_ARGUMENTS) + } + + if(BIG_ENDIAN) { + read = bswap_16(read); + write = bswap_16(write); + use = bswap_16(use); + keySize = bswap_16(keySize); + } + + if(data->keyType == 0x02) { + bufferSize = 4 + 4 + data->pLength + data->modLength; + } else if(data->keyType == 0x03) { + bufferSize = 4 + 10 + + data->pLength + data->qLength + data->pqLength + + data->dp1Length + data->dq1Length; + } + buffer = malloc(bufferSize); + if(!buffer) SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_OUT_OF_MEMORY); + p = buffer; + *p = 0x00; p++; /* Encoding plain */ + *p = data->keyType; p++; /* RSA_PRIVATE */ + memcpy(p, &keySize, 2); p+=2; /* key size */ + + if(data->keyType == 0x02) { + unsigned int length; + CPYVAL(mod); + CPYVAL(p); + } else if(data->keyType == 0x03) { + unsigned int length; + CPYVAL(p); + CPYVAL(q); + CPYVAL(pq); + CPYVAL(dp1); + CPYVAL(dq1); + } + objectId = 0xFFFFFFFEul; + if(BIG_ENDIAN) { + objectId = bswap_32(objectId); + } + r = msc_create_object(card, objectId, bufferSize, 0x02, 0x02, 0x02); + if(r < 0) { + if(r == SC_ERROR_FILE_ALREADY_EXISTS) { + r = msc_delete_object(card, objectId, 0); + if(r < 0) { + free(buffer); + SC_FUNC_RETURN(card->ctx, 2, r); + } + r = msc_create_object(card, objectId, bufferSize, 0x02, 0x02, 0x02); + if(r < 0) { + free(buffer); + SC_FUNC_RETURN(card->ctx, 2, r); + } + } + } + r = msc_update_object(card, objectId, 0, buffer, bufferSize); + free(buffer); + if(r < 0) return r; + + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x32, keyLocation, 0x00); + apdu.lc = 6; + apdu.data = apduBuffer; + apdu.datalen = 6; + p = apduBuffer; + memcpy(p, &read, 2); p+=2; + memcpy(p, &write, 2); p+=2; + memcpy(p, &use, 2); p+=2; + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { + msc_delete_object(card, objectId, 0); + return 0; + } + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (r) { + if (card->ctx->debug >= 2) { + sc_debug(card->ctx, "keyimport: got strange SWs: 0x%02X 0x%02X\n", + apdu.sw1, apdu.sw2); + } + /* no error checks.. this is last ditch cleanup */ + msc_delete_object(card, objectId, 0); + SC_FUNC_RETURN(card->ctx, 0, r); + } + /* no error checks.. this is last ditch cleanup */ + msc_delete_object(card, objectId, 0); + + SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_CARD_CMD_FAILED); +} +#undef CPYVAL + +/* For future implementation of check_sw */ +/* +switch(apdu.sw1) { + case 0x9C: + switch(apdu.sw2) { + case 0x03: // Operation not allowed + case 0x05: // Unsupported + case 0x06: // Unauthorized + case 0x11: // Bad private key num + case 0x12: // Bad public key num + case 0x0E: + case 0x0F: // Invalid parameters... + } +} +*/ diff --git a/src/libopensc/muscle.h b/src/libopensc/muscle.h new file mode 100644 index 00000000..a5a00603 --- /dev/null +++ b/src/libopensc/muscle.h @@ -0,0 +1,95 @@ +/* + * muscle.h: Support for MuscleCard Applet from musclecard.com + * + * Copyright (C) 2006, Identity Alliance, Thomas Harning + * + * 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 + */ +#ifndef MUSCLE_H_ +#define MUSCLE_H_ + +#include + +#include +#include +#include + +#include "muscle-filesystem.h" + +int msc_list_objects(sc_card_t* card, u8 next, mscfs_file_t* file); +int msc_partial_read_object(sc_card_t *card, unsigned int le_objectId, int offset, u8 *data, size_t dataLength); +int msc_read_object(sc_card_t *card, unsigned int objectId, int offset, u8 *data, size_t dataLength); +int msc_create_object(sc_card_t *card, unsigned int objectId, size_t objectSize, unsigned short read, unsigned short write, unsigned short deletion); +int msc_partial_update_object(sc_card_t *card, unsigned int le_objectId, int offset, const u8 *data, size_t dataLength); +int msc_update_object(sc_card_t *card, unsigned int objectId, int offset, const u8 *data, size_t dataLength); +int msc_zero_object(sc_card_t *card, unsigned int objectId, size_t dataLength); + +int msc_delete_object(sc_card_t *card, unsigned int objectId, int zero); +int msc_select_applet(sc_card_t *card, u8 *appletId, size_t appletIdLength); + +int msc_verify_pin(sc_card_t *card, int pinNumber, const u8 *pinValue, int pinLength, int *tries); +void msc_verify_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, int pinNumber, const u8 *pinValue, int pinLength); +int msc_unblock_pin(sc_card_t *card, int pinNumber, const u8 *pukValue, int pukLength, int *tries); +void msc_unblock_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, int pinNumber, const u8 *pukValue, int pukLength); +int msc_change_pin(sc_card_t *card, int pinNumber, const u8 *pinValue, int pinLength, const u8 *newPin, int newPinLength, int *tries); +void msc_change_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, int pinNumber, const u8 *pinValue, int pinLength, const u8 *newPin, int newPinLength); + +int msc_get_challenge(sc_card_t *card, short dataLength, short seedLength, u8 *seedData, u8* outputData); + +int msc_generate_keypair(sc_card_t *card, int privateKey, int publicKey, int algorithm, int keySize, int options); +int msc_extract_rsa_public_key(sc_card_t *card, + int keyLocation, + int* modLength, + u8** modulus, + int* expLength, + u8** exponent); +int msc_extract_key(sc_card_t *card, + int keyLocation); +int msc_compute_crypt_init(sc_card_t *card, + int keyLocation, + int cipherMode, + int cipherDirection, + const u8* initData, + u8* outputData, + size_t dataLength, + size_t* outputDataLength); +int msc_compute_crypt_process( + sc_card_t *card, + int keyLocation, + const u8* inputData, + u8* outputData, + size_t dataLength, + size_t* outputDataLength); +int msc_compute_crypt_final( + sc_card_t *card, + int keyLocation, + const u8* inputData, + u8* outputData, + size_t dataLength, + size_t* outputDataLength); +int msc_compute_crypt(sc_card_t *card, + int keyLocation, + int cipherMode, + int cipherDirection, + const u8* data, + u8* outputData, + size_t dataLength, + size_t outputDataLength); +int msc_import_key(sc_card_t *card, + int keyLocation, + sc_cardctl_muscle_key_info_t *data); + + +#endif /*MUSCLE_H_*/ diff --git a/src/libopensc/opensc.h b/src/libopensc/opensc.h index af3f3ecf..352c16ad 100644 --- a/src/libopensc/opensc.h +++ b/src/libopensc/opensc.h @@ -1154,6 +1154,7 @@ extern sc_card_driver_t *sc_get_belpic_driver(void); extern sc_card_driver_t *sc_get_atrust_acos_driver(void); extern sc_card_driver_t *sc_get_incrypto34_driver(void); extern sc_card_driver_t *sc_get_piv_driver(void); +extern sc_card_driver_t *sc_get_muscle_driver(void); #ifdef __cplusplus } diff --git a/src/pkcs15init/Makefile.mak b/src/pkcs15init/Makefile.mak index 7114e83e..e5d957e5 100644 --- a/src/pkcs15init/Makefile.mak +++ b/src/pkcs15init/Makefile.mak @@ -8,7 +8,8 @@ HEADERSDIR = $(TOPDIR)\src\include\opensc OBJECTS = profile.obj pkcs15-lib.obj keycache.obj \ pkcs15-miocos.obj pkcs15-gpk.obj pkcs15-cflex.obj \ pkcs15-cardos.obj pkcs15-jcop.obj pkcs15-starcos.obj \ - pkcs15-oberthur.obj pkcs15-setcos.obj pkcs15-incrypto34.obj + pkcs15-oberthur.obj pkcs15-setcos.obj pkcs15-incrypto34.obj \ + pkcs15-muscle.obj all: install-headers $(TARGET) diff --git a/src/pkcs15init/muscle.profile b/src/pkcs15init/muscle.profile new file mode 100644 index 00000000..b6574682 --- /dev/null +++ b/src/pkcs15init/muscle.profile @@ -0,0 +1,170 @@ +# +# PKCS15 r/w profile for MuscleCards +# + +cardinfo { + label = "MUSCLE"; + manufacturer = "Identity Alliance"; + + max-pin-length = 8; + min-pin-length = 4; + pin-encoding = ascii-numeric; + +} +option default { + macros { + protected = *=$PIN, READ=NONE; + unprotected = *=NONE; + so-pin-flags = local, initialized; #, soPin; + so-min-pin-length = 4; + so-pin-attempts = 2; + so-auth-id = 1; + so-puk-attempts = 4; + so-min-puk-length = 4; + unusedspace-size = 128; + odf-size = 256; + aodf-size = 256; + cdf-size = 512; + prkdf-size = 256; + pukdf-size = 256; + dodf-size = 256; + } +} + +PIN so-pin { + reference = 0; + flags = local, initialized; +} +PIN so-puk { + reference = 0; +} +PIN user-pin { + reference = 1; + attempts = 3; + flags = local, initialized; +} +PIN user-puk { + reference = 1; + attempts = 10; +} + + +filesystem { + DF MF { + path = 3F00; + type = DF; + acl = *=NONE, ERASE=$PIN; + # This is the DIR file + EF DIR { + type = EF; + file-id = 2F00; + size = 128; + acl = *=$PIN; + } + + # Here comes the application DF + DF PKCS15-AppDF { + type = DF; + file-id = 5015; + aid = A0:00:00:00:63:50:4B:43:53:2D:31:35; + acl = *=$PIN; + size = 1; # NO DATA SHOULD BE STORED DIRECTLY HERE! + + EF PKCS15-ODF { + file-id = 5031; + size = $odf-size; + ACL = $unprotected; + } + + EF PKCS15-TokenInfo { + file-id = 5032; + ACL = $unprotected; + size = 128; + } + + EF PKCS15-UnusedSpace { + file-id = 5033; + size = $unusedspace-size; + ACL = $unprotected; + } + + EF PKCS15-AODF { + file-id = 4401; + size = $aodf-size; + ACL = $protected; + } + + EF PKCS15-PrKDF { + file-id = 4402; + size = $prkdf-size; + acl = $protected; + } + + EF PKCS15-PuKDF { + file-id = 4403; + size = $pukdf-size; + acl = $protected; + } + + EF PKCS15-CDF { + file-id = 4404; + size = $cdf-size; + acl = $protected; + } + + EF PKCS15-DODF { + file-id = 4405; + size = $dodf-size; + ACL = $protected; + } + template key-domain { + # This is a dummy entry - pkcs15-init insists that + # this is present + EF private-key { + file-id = FFFF; + ACL = *=$PIN, READ=NEVER; + } + EF public-key { + file-id = 3000; + structure = transparent; + ACL = *=NEVER, + READ=NONE, + UPDATE=$PIN, + ERASE=$PIN; + } + + # Certificate template + EF certificate { + file-id = 3100; + structure = transparent; + ACL = *=NEVER, + READ=NONE, + UPDATE=$PIN, + ERASE=$PIN; + } + + # Extractable private keys are stored in transparent EFs. + # Encryption of the content is performed by libopensc. + EF extractable-key { + file-id = 3200; + structure = transparent; + ACL = *=NEVER, + READ=$PIN, + UPDATE=$PIN, + ERASE=$PIN; + } + + # data objects are stored in transparent EFs. + EF data { + file-id = 3300; + structure = transparent; + ACL = *=NEVER, + READ=NONE, + UPDATE=$PIN, + ERASE=$PIN; + } + + } + } + } +} diff --git a/src/pkcs15init/pkcs15-init.h b/src/pkcs15init/pkcs15-init.h index 1d05159e..e12ebcf6 100644 --- a/src/pkcs15init/pkcs15-init.h +++ b/src/pkcs15init/pkcs15-init.h @@ -399,6 +399,7 @@ 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_setcos_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_incrypto34_ops(void); +extern struct sc_pkcs15init_operations *sc_pkcs15init_get_muscle_ops(void); #ifdef __cplusplus } diff --git a/src/pkcs15init/pkcs15-lib.c b/src/pkcs15init/pkcs15-lib.c index abc8f650..725adc42 100644 --- a/src/pkcs15init/pkcs15-lib.c +++ b/src/pkcs15init/pkcs15-lib.c @@ -155,6 +155,7 @@ static struct profile_operations { { "oberthur", (void *) sc_pkcs15init_get_oberthur_ops }, { "setcos", (void *) sc_pkcs15init_get_setcos_ops }, { "incrypto34", (void *) sc_pkcs15init_get_incrypto34_ops }, + { "muscle", (void*) sc_pkcs15init_get_muscle_ops }, { NULL, NULL }, }; diff --git a/src/pkcs15init/pkcs15-muscle.c b/src/pkcs15init/pkcs15-muscle.c new file mode 100644 index 00000000..fae1f0f6 --- /dev/null +++ b/src/pkcs15init/pkcs15-muscle.c @@ -0,0 +1,328 @@ +/* + * pkcs15-muscle.c: Support for MuscleCard Applet from musclecard.com + * + * Copyright (C) 2006, Identity Alliance, Thomas Harning + * + * 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 +#endif +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include +#include +#include "pkcs15-init.h" +#include "profile.h" + + +#define MUSCLE_KEY_ID_MIN 0x00 +#define MUSCLE_KEY_ID_MAX 0x0F + +static int muscle_erase_card(sc_profile_t *profile, sc_card_t *card) +{ + int r; + struct sc_file *file; + struct sc_path path; + memset(&file, 0, sizeof(file)); + sc_format_path("3F00", &path); + if ((r = sc_select_file(card, &path, &file)) < 0) + return r; + if ((r = sc_pkcs15init_authenticate(profile, card, file, SC_AC_OP_ERASE)) < 0) + return r; + if ((r = sc_delete_file(card, &path)) < 0) + return r; + return 0; +} + + +static int muscle_init_card(sc_profile_t *profile, sc_card_t *card) +{ + return 0; +} + +static int +muscle_create_dir(sc_profile_t *profile, sc_card_t *card, sc_file_t *df) +{ + int r; + struct sc_file *file; + struct sc_path path; + memset(&file, 0, sizeof(file)); + sc_format_path("3F00", &path); + if ((r = sc_select_file(card, &path, &file)) < 0) + return r; + if ((r = sc_pkcs15init_authenticate(profile, card, file, SC_AC_OP_CREATE)) < 0) + return r; + /* Create the application DF */ + if ((r = sc_pkcs15init_create_file(profile, card, df)) < 0) + return r; + + if ((r = sc_select_file(card, &df->path, NULL)) < 0) + return r; + + + return 0; +} + +static int +muscle_create_pin(sc_profile_t *profile, sc_card_t *card, + 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) +{ + sc_file_t *file; + sc_pkcs15_pin_info_t *pin_info = (sc_pkcs15_pin_info_t *) pin_obj->data; + int r; + int type; + if (pin_info->flags & SC_PKCS15_PIN_FLAG_SO_PIN) { + type = SC_PKCS15INIT_SO_PIN; + } else { + type = SC_PKCS15INIT_USER_PIN; + } + if ((r = sc_select_file(card, &df->path, &file)) < 0) + return r; + if ((r = sc_pkcs15init_authenticate(profile, card, file, SC_AC_OP_WRITE)) < 0) + return r; + sc_keycache_set_pin_name(&df->path, + pin_info->reference, + type); + pin_info->flags &= ~SC_PKCS15_PIN_FLAG_LOCAL; + return 0; +} + +static int +muscle_select_pin_reference(sc_profile_t *profike, sc_card_t *card, + sc_pkcs15_pin_info_t *pin_info) +{ + int preferred; + + if (pin_info->flags & SC_PKCS15_PIN_FLAG_SO_PIN) { + preferred = 0; + } else { + preferred = 1; + } + if (pin_info->reference <= preferred) { + pin_info->reference = preferred; + return 0; + } + + if (pin_info->reference > 2) + return SC_ERROR_INVALID_ARGUMENTS; + + /* Caller, please select a different PIN reference */ + return SC_ERROR_INVALID_PIN_REFERENCE; +} + +/* + * Select a key reference + */ +static int +muscle_select_key_reference(sc_profile_t *profile, sc_card_t *card, + sc_pkcs15_prkey_info_t *key_info) +{ + struct sc_file *df = profile->df_info->file; + + if (key_info->key_reference < MUSCLE_KEY_ID_MIN) + key_info->key_reference = MUSCLE_KEY_ID_MIN; + if (key_info->key_reference > MUSCLE_KEY_ID_MAX) + return SC_ERROR_TOO_MANY_OBJECTS; + + key_info->path = df->path; + return 0; +} + +/* + * Create a private key object. + * This is a no-op. + */ +static int +muscle_create_key(sc_profile_t *profile, sc_card_t *card, + sc_pkcs15_object_t *obj) +{ + return 0; +} + +/* + * Store a private key object. + */ +static int +muscle_store_key(sc_profile_t *profile, sc_card_t *card, + sc_pkcs15_object_t *obj, + sc_pkcs15_prkey_t *key) +{ + sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; + sc_file_t* prkf; + struct sc_pkcs15_prkey_rsa *rsa; + sc_cardctl_muscle_key_info_t info; + int r; + + if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { + sc_error(card->ctx, "Muscle supports RSA keys only."); + return SC_ERROR_NOT_SUPPORTED; + } + /* Verification stuff */ + /* Used for verification AND for obtaining private key acls */ + r = sc_profile_get_file_by_path(profile, &key_info->path, &prkf); + if(!prkf) SC_FUNC_RETURN(card->ctx, 2,SC_ERROR_NOT_SUPPORTED); + r = sc_pkcs15init_authenticate(profile, card, prkf, SC_AC_OP_CRYPTO); + if (r < 0) { + sc_file_free(prkf); + SC_FUNC_RETURN(card->ctx, 2,SC_ERROR_NOT_SUPPORTED); + } + sc_file_free(prkf); + r = muscle_select_key_reference(profile, card, key_info); + if (r < 0) { + SC_FUNC_RETURN(card->ctx, 2,r); + } + rsa = &key->u.rsa; + + info.keySize = rsa->modulus.len << 3; + info.keyType = 0x03; /* CRT type */ + info.keyLocation = key_info->key_reference * 2; /* Mult by 2 to preserve even/odd keynumber structure */ + + info.pLength = rsa->p.len; + info.pValue = rsa->p.data; + info.qLength = rsa->q.len; + info.qValue = rsa->q.data; + + info.pqLength = rsa->iqmp.len; + info.pqValue = rsa->iqmp.data; + + info.dp1Length = rsa->dmp1.len; + info.dp1Value = rsa->dmp1.data; + info.dq1Length = rsa->dmq1.len; + info.dq1Value = rsa->dmq1.data; + + r = sc_card_ctl(card, SC_CARDCTL_MUSCLE_IMPORT_KEY, &info); + if (r < 0) { + sc_error(card->ctx, "Unable to import key"); + SC_FUNC_RETURN(card->ctx, 2,r); + } + return r; +} + +static int +muscle_generate_key(sc_profile_t *profile, sc_card_t *card, + sc_pkcs15_object_t *obj, + sc_pkcs15_pubkey_t *pubkey) +{ + sc_cardctl_muscle_gen_key_info_t args; + sc_cardctl_muscle_key_info_t extArgs; + sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; + sc_file_t* prkf; + unsigned int keybits; + int r; + + if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { + sc_error(card->ctx, "Muscle supports only RSA keys (for now)."); + SC_FUNC_RETURN(card->ctx, 2,SC_ERROR_NOT_SUPPORTED); + } + keybits = key_info->modulus_length & ~7UL; + if (keybits > 2048) { + sc_error(card->ctx, "Unable to generate key, max size is %d", + 2048); + SC_FUNC_RETURN(card->ctx, 2,SC_ERROR_INVALID_ARGUMENTS); + } + /* Verification stuff */ + /* Used for verification AND for obtaining private key acls */ + r = sc_profile_get_file_by_path(profile, &key_info->path, &prkf); + if(!prkf) SC_FUNC_RETURN(card->ctx, 2,SC_ERROR_NOT_SUPPORTED); + r = sc_pkcs15init_authenticate(profile, card, prkf, SC_AC_OP_CRYPTO); + if (r < 0) { + sc_file_free(prkf); + SC_FUNC_RETURN(card->ctx, 2,SC_ERROR_NOT_SUPPORTED); + } + sc_file_free(prkf); + + /* END VERIFICATION STUFF */ + + /* Public key acls... get_file_by_path as well? */ + + memset(&args, 0, sizeof(args)); + args.keyType = 0x01; // RSA forced + args.privateKeyLocation = key_info->key_reference * 2; + args.publicKeyLocation = key_info->key_reference * 2 + 1; + + args.keySize = keybits; + + r = sc_card_ctl(card, SC_CARDCTL_MUSCLE_GENERATE_KEY, &args); + if (r < 0) { + sc_error(card->ctx, "Unable to generate key"); + SC_FUNC_RETURN(card->ctx, 2,r); + } + + memset(&extArgs, 0, sizeof(extArgs)); + memset(pubkey, 0, sizeof(*pubkey)); + + extArgs.keyType = 0x01; + extArgs.keyLocation = args.publicKeyLocation; + r = sc_card_ctl(card, SC_CARDCTL_MUSCLE_EXTRACT_KEY, &extArgs); + if (r < 0) { + sc_error(card->ctx, "Unable to extract the public key"); + SC_FUNC_RETURN(card->ctx, 2,r); + } + + pubkey->algorithm = SC_ALGORITHM_RSA; + pubkey->u.rsa.modulus.len = extArgs.modLength; + pubkey->u.rsa.modulus.data = extArgs.modValue; + pubkey->u.rsa.exponent.len = extArgs.expLength; + pubkey->u.rsa.exponent.data = extArgs.expValue; + + if (r < 0) { + if (pubkey->u.rsa.modulus.data) + free (pubkey->u.rsa.modulus.data); + if (pubkey->u.rsa.exponent.data) + free (pubkey->u.rsa.exponent.data); + } + return r; +} + + +static struct sc_pkcs15init_operations sc_pkcs15init_muscle_operations = { + muscle_erase_card, /* erase card */ + muscle_init_card, /* init_card */ + muscle_create_dir, /* create_dir */ + NULL, /* create_domain */ + muscle_select_pin_reference, /* select pin reference */ + muscle_create_pin, /* Create PIN */ + muscle_select_key_reference, /* select_key_reference */ + muscle_create_key, /* create_key */ + muscle_store_key, /* store_key */ + muscle_generate_key, /* generate_key */ + NULL, NULL, /* encode private/public key */ + NULL, /* finalize_card */ + NULL, /* old - initapp*/ + NULL, /* new_pin */ + NULL, /* new key */ + NULL, /* new file */ + NULL, /* generate key */ + NULL /* delete_object */ +}; + +struct sc_pkcs15init_operations * +sc_pkcs15init_get_muscle_ops(void) +{ + return &sc_pkcs15init_muscle_operations; +}