Added support for MuscleCard applet. Thanks to Thomas Harning, David Corcoran of Identity Alliance

git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@2968 c6295689-39f2-0310-b995-f0e70906c6a9
This commit is contained in:
sth 2006-06-07 08:33:37 +00:00
parent e225efc982
commit 50490acca0
17 changed files with 2743 additions and 6 deletions

View File

@ -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

View File

@ -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 \

772
src/libopensc/card-muscle.c Normal file
View File

@ -0,0 +1,772 @@
/*
* card-muscle.c: Support for MuscleCard Applet from musclecard.com
*
* Copyright (C) 2006, Identity Alliance, Thomas Harning <support@identityalliance.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "internal.h"
#include "cardctl.h"
#include "muscle.h"
#include "muscle-filesystem.h"
#include <opensc/types.h>
#include <opensc.h>
#include <stdlib.h>
#include <string.h>
/* 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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,237 @@
/*
* muscle-filesystem.c: Support for MuscleCard Applet from musclecard.com
*
* Copyright (C) 2006, Identity Alliance, Thomas Harning <support@identityalliance.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "muscle-filesystem.h"
#include <opensc/errors.h>
#include <memory.h>
#include <stdio.h>
#include <assert.h>
#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;
}

View File

@ -0,0 +1,67 @@
/*
* muscle-filesystem.h: Support for MuscleCard Applet from musclecard.com
*
* Copyright (C) 2006, Identity Alliance, Thomas Harning <support@identityalliance.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef MUSCLE_FILESYSTEM_H
#define MUSCLE_FILESYSTEM_H
#include <stdlib.h>
#include <opensc/types.h>
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

1009
src/libopensc/muscle.c Normal file

File diff suppressed because it is too large Load Diff

95
src/libopensc/muscle.h Normal file
View File

@ -0,0 +1,95 @@
/*
* muscle.h: Support for MuscleCard Applet from musclecard.com
*
* Copyright (C) 2006, Identity Alliance, Thomas Harning <support@identityalliance.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef MUSCLE_H_
#define MUSCLE_H_
#include <stddef.h>
#include <opensc/types.h>
#include <opensc.h>
#include <opensc/cardctl.h>
#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_*/

View File

@ -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
}

View File

@ -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)

View File

@ -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;
}
}
}
}
}

View File

@ -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
}

View File

@ -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 },
};

View File

@ -0,0 +1,328 @@
/*
* pkcs15-muscle.c: Support for MuscleCard Applet from musclecard.com
*
* Copyright (C) 2006, Identity Alliance, Thomas Harning <support@identityalliance.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdarg.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <opensc/pkcs15.h>
#include <opensc/opensc.h>
#include <opensc/cardctl.h>
#include <opensc/cards.h>
#include <opensc/log.h>
#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;
}