diff --git a/NEWS b/NEWS
index b1cb579e..36bf26f4 100644
--- a/NEWS
+++ b/NEWS
@@ -6,6 +6,9 @@ http://www.opensc-project.org/opensc/wiki/WhatsNew
Also see the svn changelog using svn command
or doc/nonpersistent/ChangeLog.
+New in 0.??.==; 200?-??-??; ??
+* New westcos driver by François Leblanc
+
New in 0.11.9; 2009-07-29; Andreas Jellinghaus
* New rutoken_ecp driver by Aktiv Co. / Aleksey Samsonov
* Allow more keys/certificates/files etc. with entersafe tokens
diff --git a/doc/tools/tools.xml b/doc/tools/tools.xml
index c1246010..6bdc675e 100644
--- a/doc/tools/tools.xml
+++ b/doc/tools/tools.xml
@@ -19,6 +19,7 @@
+
diff --git a/doc/tools/westcos-tool.xml b/doc/tools/westcos-tool.xml
new file mode 100644
index 00000000..fc2cc278
--- /dev/null
+++ b/doc/tools/westcos-tool.xml
@@ -0,0 +1,140 @@
+
+
+
+ westcos-tool
+ 1
+ opensc
+
+
+
+ westcos-tool
+ utility for manipulating data structure
+ on westcos smart card and similar security tokens
+
+
+
+ Synopsis
+
+ westcos-tool [OPTIONS]
+
+
+
+
+ Description
+
+ The westcos-tool utility is used to manipulate
+ the westcos data structures on 2 Ko smart cards. Users can create PINs,
+ keys and certificates stored on the token. User PIN authentication is
+ performed for those operations that require it.
+
+
+
+
+ Options
+
+
+
+
+ Generate a private key on smart card. The smart card must be
+ not finalized and pin installed (ig. file for pin must be created, see option
+ -i). By default key length is 1536 bits. User authentication is required for
+ this operation.
+
+
+
+ length
+ Change the length of private key, use with .
+
+
+
+
+
+ Install pin file in token, you must provide pin value
+ with .
+
+
+
+ value
+ set value of pin.
+
+
+
+ value
+ set value of puk (or value of new pin for change pin
+ command see ).
+
+
+
+
+ Changes a PIN stored on the token. User authentication
+ is required for this operation.
+
+
+
+
+ Unblocks a PIN stored on the token. Knowledge of the Pin
+ Unblock Key (PUK) is required for this operation.
+
+
+
+ file
+ Write certificate file in pem format on the
+ card. User authentication is required for this operation.
+
+
+
+
+ Finalize the card, once finalize default key is invalidate so pin and puk
+ can'be changed anymore without user authentification. Warning, smart cards not finalized are
+ unsecure because pin can be changed without user authentification (knowledge of default key
+ is enougth).
+
+
+
+ n
+ Forces westcos-tool to use reader
+ number n for operations.
+
+
+
+ path
+ Get the file path the file is written
+ on disk with path name. User authentication
+ is required for this operation.
+
+
+
+ path
+ Put the file with name path from disk
+ to card the file is written in path. User authentication
+ is required for this operation.
+
+
+
+
+ Causes westcos-tool to be more
+ verbose. Specify this flag several times to enable debug output
+ in the OpenSC library.
+
+
+
+
+ Print help message on screen.
+
+
+
+
+
+
+
+ See also
+ opensc(7)
+
+
+
+ Authors
+ westcos-tool was written by
+ Francois Leblanc francois.leblanc@cev-sa.com.
+
+
+
diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am
index 16b4f67f..ae3542b9 100644
--- a/src/libopensc/Makefile.am
+++ b/src/libopensc/Makefile.am
@@ -40,12 +40,12 @@ libopensc_la_SOURCES = \
card-oberthur.c card-belpic.c card-atrust-acos.c card-entersafe.c \
card-incrypto34.c card-piv.c card-muscle.c card-acos5.c \
card-asepcos.c card-akis.c card-gemsafeV1.c card-rutoken.c \
- card-rtecp.c \
+ card-rtecp.c card-westcos.c \
\
pkcs15-openpgp.c pkcs15-infocamere.c pkcs15-starcert.c \
pkcs15-tcos.c pkcs15-esteid.c pkcs15-postecert.c pkcs15-gemsafeGPK.c \
pkcs15-actalis.c pkcs15-atrust-acos.c pkcs15-tccardos.c pkcs15-piv.c \
- pkcs15-esinit.c \
+ pkcs15-esinit.c p15emu-westcos.c \
compression.c p15card-helper.c \
\
libopensc.exports
diff --git a/src/libopensc/Makefile.mak b/src/libopensc/Makefile.mak
index 5acccce6..a06fa827 100644
--- a/src/libopensc/Makefile.mak
+++ b/src/libopensc/Makefile.mak
@@ -22,6 +22,7 @@ OBJECTS = \
\
ctbcs.obj reader-ctapi.obj reader-pcsc.obj reader-openct.obj \
\
+ card-westcos.obj crc_AetB.obj \
card-setcos.obj card-miocos.obj card-flex.obj card-gpk.obj \
card-cardos.obj card-tcos.obj card-emv.obj card-default.obj \
card-mcrd.obj card-starcos.obj card-openpgp.obj card-jcop.obj \
@@ -30,6 +31,7 @@ OBJECTS = \
card-asepcos.obj card-akis.obj card-gemsafeV1.obj card-rutoken.obj \
card-rtecp.obj \
\
+ p15emu-westcos.obj \
pkcs15-openpgp.obj pkcs15-infocamere.obj pkcs15-starcert.obj \
pkcs15-tcos.obj pkcs15-esteid.obj pkcs15-postecert.obj pkcs15-gemsafeGPK.obj \
pkcs15-actalis.obj pkcs15-atrust-acos.obj pkcs15-tccardos.obj pkcs15-piv.obj \
diff --git a/src/libopensc/card-westcos.c b/src/libopensc/card-westcos.c
new file mode 100644
index 00000000..77091dc1
--- /dev/null
+++ b/src/libopensc/card-westcos.c
@@ -0,0 +1,1355 @@
+/*
+ * card-westcos.c: support for westcos card
+ *
+ * Copyright (C) 2009 francois.leblanc@cev-sa.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
+#include
+#include
+#include
+#include "cardctl.h"
+#include "asn1.h"
+
+#ifdef ENABLE_OPENSSL
+#include
+#include
+#include
+#include
+#include
+#include
+#endif
+
+#ifndef min
+#define min(a,b) (((a)<(b))?(a):(b))
+#endif
+
+#ifndef min
+#define max(a,b) (((a)>(b))?(a):(b))
+#endif
+
+#define DEFAULT_TRANSPORT_KEY "6f:59:b0:ed:6e:62:46:4a:5d:25:37:68:23:a8:a2:2d"
+
+#define JAVACARD (0x01)
+
+#ifdef ENABLE_OPENSSL
+#define DEBUG_SSL
+#ifdef DEBUG_SSL
+static void print_openssl_erreur(void)
+{
+ static int charge = 0;
+ long r;
+
+ if (!charge) {
+ ERR_load_crypto_strings();
+ charge = 1;
+ }
+ while ((r = ERR_get_error()) != 0)
+ fprintf(stderr, "%s\n", ERR_error_string(r, NULL));
+}
+#endif
+#endif
+
+typedef struct {
+ sc_security_env_t env;
+ sc_autkey_t default_key;
+ int flags;
+ int file_id;
+} priv_data_t;
+
+static const struct sc_card_operations *iso_ops = NULL;
+static struct sc_card_operations westcos_ops;
+
+static struct sc_card_driver westcos_drv = {
+ "WESTCOS compatible cards", "westcos", &westcos_ops, NULL, 0, NULL
+};
+
+static int westcos_get_default_key(sc_card_t * card,
+ struct sc_cardctl_default_key *data)
+{
+ const char *default_key;
+ if (card->ctx->debug >= 1)
+ sc_debug(card->ctx,
+ "westcos_get_default_key:data->method=%d, data->key_ref=%d\n",
+ data->method, data->key_ref);
+ if (data->method != SC_AC_AUT || data->key_ref != 0)
+ return SC_ERROR_NO_DEFAULT_KEY;
+ default_key =
+ scconf_get_str(card->ctx->conf_blocks[0], "westcos_default_key",
+ DEFAULT_TRANSPORT_KEY);
+ return sc_hex_to_bin(default_key, data->key_data, &data->len);
+}
+
+#define CRC_A 1
+#define CRC_B 2
+
+static unsigned short westcos_update_crc(unsigned char ch, unsigned short *lpwCrc)
+{
+ ch = (ch ^ (unsigned char)((*lpwCrc) & 0x00FF));
+ ch = (ch ^ (ch << 4));
+ *lpwCrc =
+ (*lpwCrc >> 8) ^ ((unsigned short)ch << 8) ^ ((unsigned short)ch <<
+ 3) ^ ((unsigned short)
+ ch >> 4);
+ return (*lpwCrc);
+}
+
+static void westcos_compute_aetb_crc(int CRCType,
+ unsigned char *Data,
+ size_t Length,
+ unsigned char * TransmitFirst,
+ unsigned char * TransmitSecond)
+{
+ unsigned char chBlock;
+ unsigned short wCrc;
+ switch (CRCType) {
+ case CRC_A:
+ wCrc = 0x6363; // ITU-V.41
+ break;
+ case CRC_B:
+ wCrc = 0xFFFF; // ISO 3309
+ break;
+ default:
+ return;
+ }
+
+ do {
+ chBlock = *Data++;
+ westcos_update_crc(chBlock, &wCrc);
+ } while (--Length);
+ if (CRCType == CRC_B)
+ wCrc = ~wCrc; // ISO 3309
+ *TransmitFirst = (unsigned char) (wCrc & 0xFF);
+ *TransmitSecond = (unsigned char) ((wCrc >> 8) & 0xFF);
+ return;
+}
+
+static int westcos_check_sw(sc_card_t * card, unsigned int sw1,
+ unsigned int sw2)
+{
+ if ((sw1 == 0x69) && (sw2 == 0x88))
+ return SC_ERROR_SECURITY_STATUS_NOT_SATISFIED;
+ assert(iso_ops && iso_ops->check_sw);
+ return iso_ops->check_sw(card, sw1, sw2);
+}
+
+typedef struct mon_atr {
+ size_t len;
+ int flags;
+ u8 *atr, *mask;
+} mon_atr_t;
+
+static mon_atr_t atrs[] = {
+ {13, 0x00,
+ "\x3f\x69\x00\x00\x00\x64\x01\x00\x00\x00\x80\x90\x00",
+ "\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\xf0\xff\xff"},
+ {12, JAVACARD,
+ "\x3b\x95\x94\x80\x1F\xC3\x80\x73\xC8\x21\x13\x54",
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"}
+};
+
+static int westcos_finish(sc_card_t * card)
+{
+ if (card->algorithms)
+ free(card->algorithms);
+ card->algorithms = NULL;
+ card->algorithm_count = 0;
+ if (card->drv_data)
+ free(card->drv_data);
+ return 0;
+}
+
+static int westcos_match_card(sc_card_t * card)
+{
+ u8 *p, j;
+ size_t i;
+ mon_atr_t *matr;
+ if (card->ctx->debug >= 1)
+ sc_debug(card->ctx, "westcos_match_card %d, %X:%X:%X\n",
+ card->atr_len, card->atr[0], card->atr[1],
+ card->atr[2]);
+ for (i = 0; i < sizeof(atrs) / sizeof(*atrs); i++) {
+ matr = &atrs[i];
+ if (matr->len != card->atr_len)
+ continue;
+ p = card->atr;
+ for (j = 0; j < card->atr_len; j++) {
+ if (((matr->mask[j]) & (*p)) != (matr->atr[j]))
+ break;
+ p++;
+ if (*p == ':')
+ p++;
+ }
+ if (j >= card->atr_len) {
+ if (matr->flags & JAVACARD) {
+ int r;
+ sc_apdu_t apdu;
+ u8 aid[] = {
+ 0xA0, 0x00, 0xCE, 0x00, 0x07, 0x01
+ };
+ sc_format_apdu(card, &apdu,
+ SC_APDU_CASE_3_SHORT, 0xA4, 0x04,
+ 0);
+ apdu.cla = 0x00;
+ apdu.lc = sizeof(aid);
+ apdu.datalen = sizeof(aid);
+ apdu.data = aid;
+ sc_ctx_suppress_errors_on(card->ctx);
+ r = sc_transmit_apdu(card, &apdu);
+ sc_ctx_suppress_errors_off(card->ctx);
+ if (r)
+ continue;
+ sc_ctx_suppress_errors_on(card->ctx);
+ r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+ sc_ctx_suppress_errors_off(card->ctx);
+ if (r)
+ continue;
+ }
+ card->drv_data = malloc(sizeof(priv_data_t));
+ if (card->drv_data == NULL)
+ return SC_ERROR_OUT_OF_MEMORY;
+ memset(card->drv_data, 0, sizeof(card->drv_data));
+ if (matr->flags & JAVACARD) {
+ priv_data_t *priv_data =
+ (priv_data_t *) card->drv_data;
+ priv_data->flags |= JAVACARD;
+ }
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int westcos_init(sc_card_t * card)
+{
+ int r;
+ const char *default_key;
+ unsigned long exponent, flags;
+ if (card == NULL || card->drv_data == NULL)
+ return SC_ERROR_INVALID_ARGUMENTS;
+ card->cla = 0x00;
+ card->max_send_size = 240;
+ card->max_recv_size = 240;
+ exponent = 0;
+ flags = SC_ALGORITHM_RSA_RAW;
+ flags |= SC_ALGORITHM_RSA_HASH_NONE;
+ flags |= SC_ALGORITHM_RSA_PAD_NONE | SC_ALGORITHM_RSA_PAD_PKCS1;
+ flags |= SC_ALGORITHM_ONBOARD_KEY_GEN;
+ _sc_card_add_rsa_alg(card, 128, flags, exponent);
+ _sc_card_add_rsa_alg(card, 256, flags, exponent);
+ _sc_card_add_rsa_alg(card, 512, flags, exponent);
+ _sc_card_add_rsa_alg(card, 768, flags, exponent);
+ _sc_card_add_rsa_alg(card, 1024, flags, exponent);
+ _sc_card_add_rsa_alg(card, 1100, flags, exponent);
+ _sc_card_add_rsa_alg(card, 1200, flags, exponent);
+ _sc_card_add_rsa_alg(card, 1300, flags, exponent);
+ _sc_card_add_rsa_alg(card, 1400, flags, exponent);
+ _sc_card_add_rsa_alg(card, 1536, flags, exponent);
+ _sc_card_add_rsa_alg(card, 2048, flags, exponent);
+ default_key =
+ scconf_get_str(card->ctx->conf_blocks[0], "westcos_default_key",
+ DEFAULT_TRANSPORT_KEY);
+ if (default_key) {
+ priv_data_t *priv_data = (priv_data_t *) (card->drv_data);
+ priv_data->default_key.key_reference = 0;
+ priv_data->default_key.key_len =
+ sizeof(priv_data->default_key.key_value);
+ r = sc_hex_to_bin(default_key, priv_data->default_key.key_value,
+ &(priv_data->default_key.key_len));
+ if (r)
+ return (r);
+ }
+ return 0;
+}
+
+static int westcos_select_file(sc_card_t * card, const sc_path_t * in_path,
+ sc_file_t ** file_out)
+{
+ sc_context_t *ctx;
+ sc_apdu_t apdu;
+ u8 buf[SC_MAX_APDU_BUFFER_SIZE];
+ u8 pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf;
+ int r, pathlen;
+ sc_file_t *file = NULL;
+ priv_data_t *priv_data = NULL;
+ if (card->ctx->debug >= 1)
+ sc_debug(card->ctx, "westcos_select_file\n");
+ if (card == NULL)
+ return SC_ERROR_INVALID_ARGUMENTS;
+ priv_data = (priv_data_t *) card->drv_data;
+ priv_data->file_id = 0;
+ ctx = card->ctx;
+ memcpy(path, in_path->value, in_path->len);
+ pathlen = (int)in_path->len;
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0);
+ switch (in_path->type) {
+ case SC_PATH_TYPE_FILE_ID:
+ apdu.p1 = 0;
+ if (pathlen != 2)
+ return SC_ERROR_INVALID_ARGUMENTS;
+ break;
+ case SC_PATH_TYPE_DF_NAME:
+ apdu.p1 = 4;
+ break;
+ case SC_PATH_TYPE_PATH:
+ apdu.p1 = 9;
+ if (pathlen == 2 && memcmp(path, "\x3F\x00", 2) == 0) {
+ apdu.p1 = 0;
+ }
+
+ else if (pathlen > 2 && memcmp(path, "\x3F\x00", 2) == 0) {
+ apdu.p1 = 8;
+ pathlen -= 2;
+ memcpy(path, &in_path->value[2], pathlen);
+ }
+ break;
+ case SC_PATH_TYPE_FROM_CURRENT:
+ apdu.p1 = 9;
+ break;
+ case SC_PATH_TYPE_PARENT:
+ apdu.p1 = 3;
+ pathlen = 0;
+ apdu.cse = SC_APDU_CASE_3_SHORT;
+ break;
+ default:
+ return SC_ERROR_INVALID_ARGUMENTS;
+ }
+ apdu.p2 = 0; /* first record, return FCI */
+ apdu.lc = pathlen;
+ apdu.data = path;
+ apdu.datalen = pathlen;
+ if (file_out != NULL) {
+ apdu.resp = buf;
+ apdu.resplen = sizeof(buf);
+ apdu.le = 255;
+ } else {
+ apdu.resplen = 0;
+ apdu.le = 0;
+ apdu.cse = SC_APDU_CASE_3_SHORT;
+ }
+ r = sc_transmit_apdu(card, &apdu);
+ if (r)
+ return (r);
+ if (file_out == NULL) {
+ if (apdu.sw1 == 0x61)
+ return 0;
+ return sc_check_sw(card, apdu.sw1, apdu.sw2);
+ }
+ r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+ if (r)
+ return (r);
+ switch (apdu.resp[0]) {
+ case 0x6F:
+ file = sc_file_new();
+ if (file == NULL)
+ return SC_ERROR_OUT_OF_MEMORY;
+ file->path = *in_path;
+ if (card->ops->process_fci == NULL) {
+ sc_file_free(file);
+ return SC_ERROR_NOT_SUPPORTED;
+ }
+ if (apdu.resp[1] <= apdu.resplen)
+ card->ops->process_fci(card, file, apdu.resp + 2,
+ apdu.resp[1]);
+ *file_out = file;
+ break;
+ case 0x00: /* proprietary coding */
+ return SC_ERROR_UNKNOWN_DATA_RECEIVED;
+ default:
+ return SC_ERROR_UNKNOWN_DATA_RECEIVED;
+ }
+ return 0;
+}
+
+static int _westcos2opensc_ac(u8 flag)
+{
+ if (flag == 0)
+ return SC_AC_NEVER;
+ else if (flag == 1)
+ return SC_AC_CHV;
+ else if (flag == 2)
+ return SC_AC_AUT;
+ else if (flag == 15)
+ return SC_AC_NONE;
+ return SC_AC_UNKNOWN;
+}
+
+static int westcos_process_fci(sc_card_t * card, sc_file_t * file,
+ const u8 * buf, size_t buflen)
+{
+ sc_context_t *ctx = card->ctx;
+ size_t taglen, len = buflen;
+ const u8 *tag = NULL, *p = buf;
+ if (card->ctx->debug >= 5)
+ sc_debug(card->ctx, "processing FCI bytes\n");
+ tag = sc_asn1_find_tag(ctx, p, len, 0x83, &taglen);
+ if (tag != NULL && taglen == 2) {
+ file->id = (tag[0] << 8) | tag[1];
+ if (card->ctx->debug >= 5)
+ sc_debug(card->ctx, " file identifier: 0x%02X%02X\n",
+ tag[0], tag[1]);
+ }
+ tag = sc_asn1_find_tag(ctx, p, len, 0x80, &taglen);
+ if (tag != NULL && taglen >= 2) {
+ int bytes = (tag[0] << 8) + tag[1];
+ if (card->ctx->debug >= 5)
+ sc_debug(card->ctx, " bytes in file: %d\n", bytes);
+ file->size = bytes;
+ }
+ if (tag == NULL) {
+ tag = sc_asn1_find_tag(ctx, p, len, 0x81, &taglen);
+ if (tag != NULL && taglen >= 2) {
+ int bytes = (tag[0] << 8) + tag[1];
+ if (card->ctx->debug >= 5)
+ sc_debug(card->ctx, " bytes in file: %d\n",
+ bytes);
+ file->size = bytes;
+ }
+ }
+ tag = sc_asn1_find_tag(ctx, p, len, 0x82, &taglen);
+ if (tag != NULL) {
+ if (taglen > 0) {
+ unsigned char byte = tag[0];
+ const char *type;
+ file->shareable = 0;
+ if (card->ctx->debug >= 5)
+ sc_debug(card->ctx, " shareable: %s\n",
+ (file->shareable) ? "yes" : "no");
+ file->ef_structure = SC_FILE_EF_UNKNOWN;
+ switch (byte) {
+ case 0x38:
+ type = "DF";
+ file->type = SC_FILE_TYPE_DF;
+ break;
+ case 0x01:
+ type = "working or internal EF";
+ file->type = SC_FILE_TYPE_WORKING_EF;
+ file->ef_structure = SC_FILE_EF_TRANSPARENT;
+ break;
+ case 0x02:
+ type = "working or internal EF";
+ file->type = SC_FILE_TYPE_WORKING_EF;
+ file->ef_structure = SC_FILE_EF_LINEAR_FIXED;
+ break;
+ case 0x06:
+ type = "working or internal EF";
+ file->type = SC_FILE_TYPE_WORKING_EF;
+ file->ef_structure = SC_FILE_EF_CYCLIC;
+ break;
+ default:
+ type = "unknow";
+ }
+ if (card->ctx->debug >= 5)
+ sc_debug(card->ctx, " type: %s\n", type);
+ if (card->ctx->debug >= 5)
+ sc_debug(card->ctx, " EF structure: %d\n",
+ file->ef_structure);
+ }
+ }
+ tag = sc_asn1_find_tag(ctx, p, len, 0x84, &taglen);
+ if (tag != NULL && taglen > 0 && taglen <= 16) {
+ memcpy(file->name, tag, taglen);
+ file->namelen = taglen;
+ if (card->ctx->debug >= 5) {
+ char tbuf[128];
+ sc_hex_dump(ctx, file->name, file->namelen, tbuf,
+ sizeof(tbuf));
+ sc_debug(card->ctx, " File name: %s\n", tbuf);
+ }
+ }
+ if (file->type == SC_FILE_TYPE_DF) {
+ tag = sc_asn1_find_tag(ctx, p, len, 0x85, &taglen);
+ if (tag != NULL && taglen == 3) {
+ file->size = tag[1] * 256 + tag[2];
+ } else
+ file->size = 0;
+ }
+ tag = sc_asn1_find_tag(ctx, p, len, 0xA5, &taglen);
+ if (tag != NULL && taglen) {
+ sc_file_set_prop_attr(file, tag, taglen);
+ }
+ tag = sc_asn1_find_tag(ctx, p, len, 0x86, &taglen);
+ if (tag != NULL && taglen) {
+ sc_file_set_sec_attr(file, tag, taglen);
+
+ /* FIXME: compact file system only */
+ if (file->type == SC_FILE_TYPE_DF) {
+ sc_file_add_acl_entry(file, SC_AC_OP_SELECT,
+ _westcos2opensc_ac(tag[0] >>
+ 4),
+ tag[0 + 4] >> 4);
+ sc_file_add_acl_entry(file, SC_AC_OP_CREATE,
+ _westcos2opensc_ac(tag[0] &
+ 0x0f),
+ tag[0 + 4] & 0x0f);
+ sc_file_add_acl_entry(file, SC_AC_OP_INVALIDATE,
+ _westcos2opensc_ac(tag[1] >>
+ 4),
+ tag[1 + 4] >> 4);
+ }
+
+ else {
+ if (file->ef_structure == SC_FILE_EF_TRANSPARENT) {
+ sc_file_add_acl_entry(file, SC_AC_OP_READ,
+ _westcos2opensc_ac(tag[0]
+ >>
+ 4),
+ tag[0 + 4] >> 4);
+ sc_file_add_acl_entry(file, SC_AC_OP_UPDATE,
+ _westcos2opensc_ac(tag[0]
+ &
+ 0x0f),
+ tag[0 + 4] & 0x0f);
+ sc_file_add_acl_entry(file,
+ SC_AC_OP_INVALIDATE,
+ _westcos2opensc_ac(tag[1]
+ >>
+ 4),
+ tag[1 + 4] >> 4);
+ sc_file_add_acl_entry(file, SC_AC_OP_ERASE,
+ _westcos2opensc_ac(tag[1]
+ &
+ 0x0f),
+ tag[1 + 4] & 0x0f);
+ }
+
+ else {
+ sc_file_add_acl_entry(file, SC_AC_OP_READ,
+ _westcos2opensc_ac(tag[0]
+ >>
+ 4),
+ tag[0 + 4] >> 4);
+ sc_file_add_acl_entry(file, SC_AC_OP_UPDATE,
+ _westcos2opensc_ac(tag[0]
+ &
+ 0x0f),
+ tag[0 + 4] & 0x0f);
+ sc_file_add_acl_entry(file,
+ SC_AC_OP_INVALIDATE,
+ _westcos2opensc_ac(tag[1]
+ >>
+ 4),
+ tag[1 + 4] >> 4);
+ }
+ }
+ }
+ return 0;
+}
+
+#define HIGH (0)
+#define LOW (1)
+static int _convertion_ac_methode(sc_file_t * file, int low,
+ unsigned int operation, u8 * buf,
+ u8 * buf_key)
+{
+ const struct sc_acl_entry *acl;
+ acl = sc_file_get_acl_entry(file, operation);
+ if (acl == NULL) {
+
+ /* par defaut always */
+ *buf = 0xff;
+ *buf_key = 0x00;
+ return 0;
+ }
+ switch (acl->method) {
+ case SC_AC_NONE:
+ if (low)
+ *buf |= 0x0f;
+
+ else
+ *buf |= 0xf0;
+ break;
+ case SC_AC_CHV: /* Card Holder Verif. */
+ if (low)
+ *buf |= 0x01;
+
+ else
+ *buf |= 0x10;
+ break;
+ case SC_AC_TERM: /* Terminal auth. */
+ return SC_ERROR_NOT_SUPPORTED;
+ case SC_AC_PRO: /* Secure Messaging */
+ return SC_ERROR_NOT_SUPPORTED;
+ case SC_AC_AUT: /* Key auth. */
+ if (low)
+ *buf |= 0x02;
+
+ else
+ *buf |= 0x20;
+ if (acl->key_ref > 15)
+ return SC_ERROR_NOT_SUPPORTED;
+ if (low)
+ *buf_key |= acl->key_ref;
+
+ else
+ *buf_key |= (acl->key_ref) << 4;
+ break;
+ case SC_AC_NEVER:
+ *buf |= 0;
+ break;
+ default:
+ return SC_ERROR_NOT_SUPPORTED;
+ }
+ return 0;
+}
+
+static int westcos_create_file(sc_card_t *card, struct sc_file *file)
+{
+ int r;
+ sc_apdu_t apdu;
+ u8 buf[12], p1 = 0, p2 = 0;
+ int buflen;
+ if (card == NULL)
+ return SC_ERROR_INVALID_ARGUMENTS;
+ if (card->ctx->debug >= 1)
+ sc_debug(card->ctx, "westcos_create_file\n");
+ memset(buf, 0, sizeof(buf));
+
+ /* clef de transport */
+ r = sc_card_ctl(card, SC_CARDCTL_WESTCOS_AUT_KEY, NULL);
+ if (r)
+ return (r);
+ buflen = sizeof(buf);
+ switch (file->type) {
+ case SC_FILE_TYPE_DF:
+ buf[0] = 0x00;
+ buf[1] = 0x01;
+ _convertion_ac_methode(file, HIGH, SC_AC_OP_SELECT, &buf[2],
+ &buf[2 + 4]);
+ _convertion_ac_methode(file, LOW, SC_AC_OP_CREATE, &buf[2],
+ &buf[2 + 4]);
+ _convertion_ac_methode(file, HIGH, SC_AC_OP_INVALIDATE,
+ &buf[3], &buf[3 + 4]);
+ buflen = 10;
+ break;
+ case SC_FILE_TYPE_INTERNAL_EF:
+ buf[0] |= 0x80;
+ case SC_FILE_TYPE_WORKING_EF:
+ switch (file->ef_structure) {
+ case SC_FILE_EF_TRANSPARENT:
+ buf[0] |= 0x20; /* pas de support transaction */
+ buf[1] |= 0;
+ _convertion_ac_methode(file, HIGH, SC_AC_OP_READ,
+ &buf[2], &buf[2 + 4]);
+ _convertion_ac_methode(file, LOW, SC_AC_OP_UPDATE,
+ &buf[2], &buf[2 + 4]);
+ _convertion_ac_methode(file, HIGH, SC_AC_OP_INVALIDATE,
+ &buf[3], &buf[3 + 4]);
+ _convertion_ac_methode(file, LOW, SC_AC_OP_ERASE,
+ &buf[3], &buf[3 + 4]);
+ buf[10] = (u8) ((file->size) / 256);
+ buf[11] = (u8) ((file->size) % 256);
+ break;
+ case SC_FILE_EF_LINEAR_FIXED:
+ buf[0] |= 0x40; /* pas de support transaction */
+ buf[1] |= 0;
+ _convertion_ac_methode(file, HIGH, SC_AC_OP_READ,
+ &buf[2], &buf[2 + 4]);
+ _convertion_ac_methode(file, LOW, SC_AC_OP_UPDATE,
+ &buf[2], &buf[2 + 4]);
+ _convertion_ac_methode(file, HIGH, SC_AC_OP_INVALIDATE,
+ &buf[3], &buf[3 + 4]);
+ buf[10] = file->record_count;
+ buf[11] = file->record_length;
+ break;
+ case SC_FILE_EF_CYCLIC:
+ buf[0] |= 0x60; /* pas de support transaction */
+ buf[1] |= 0;
+ _convertion_ac_methode(file, HIGH, SC_AC_OP_READ,
+ &buf[2], &buf[2 + 4]);
+ _convertion_ac_methode(file, LOW, SC_AC_OP_UPDATE,
+ &buf[2], &buf[2 + 4]);
+ _convertion_ac_methode(file, HIGH, SC_AC_OP_INVALIDATE,
+ &buf[3], &buf[3 + 4]);
+ buf[10] = file->record_count;
+ buf[11] = file->record_length;
+ break;
+ case SC_FILE_EF_LINEAR_VARIABLE:
+ case SC_FILE_EF_UNKNOWN:
+ case SC_FILE_EF_LINEAR_FIXED_TLV:
+ case SC_FILE_EF_LINEAR_VARIABLE_TLV:
+ case SC_FILE_EF_CYCLIC_TLV:
+ default:
+ return SC_ERROR_NOT_SUPPORTED;
+ }
+ break;
+ default:
+ return SC_ERROR_NOT_SUPPORTED;
+ }
+ if (file->shareable)
+ buf[0] |= 0x08;
+ if (file->path.len >= 2) {
+ p1 = file->path.value[file->path.len - 2];
+ p2 = file->path.value[file->path.len - 1];
+ }
+
+ else if (file->id) {
+ p1 = (file->id) / 256;
+ p2 = (file->id) % 256;
+ }
+ if (card->ctx->debug >= 3)
+ sc_debug(card->ctx, "create file %s, id %X size %d\n",
+ file->path.value, file->id, file->size);
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, p1, p2);
+ apdu.cla = 0x80;
+ apdu.lc = buflen;
+ apdu.datalen = buflen;
+ apdu.data = buf;
+ r = sc_transmit_apdu(card, &apdu);
+ if (r)
+ return (r);
+ r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+ return r;
+}
+
+static int westcos_delete_file(sc_card_t * card, const sc_path_t * path_in)
+{
+ int r;
+ sc_apdu_t apdu;
+ if (card == NULL || path_in == NULL || path_in->len < 2)
+ return SC_ERROR_INVALID_ARGUMENTS;
+ if (card->ctx->debug >= 1)
+ sc_debug(card->ctx, "westcos_delete_file\n");
+ if (path_in->len > 2) {
+ r = sc_select_file(card, path_in, NULL);
+ if (r)
+ return (r);
+ }
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xE4,
+ path_in->value[path_in->len - 2],
+ path_in->value[path_in->len - 1]);
+ apdu.cla = 0x80;
+ r = sc_transmit_apdu(card, &apdu);
+ if (r)
+ return (r);
+ r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+ if (r)
+ return (r);
+ return 0;
+}
+
+static int westcos_list_files(sc_card_t * card, u8 * buf, size_t buflen)
+{
+ int r;
+ sc_apdu_t apdu;
+ if (card == NULL)
+ return SC_ERROR_INVALID_ARGUMENTS;
+ if (card->ctx->debug >= 1)
+ sc_debug(card->ctx, "westcos_list_files\n");
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x34, 0x00, 0x00);
+ apdu.cla = 0x80;
+ apdu.le = buflen;
+ apdu.resplen = buflen;
+ apdu.resp = buf;
+ r = sc_transmit_apdu(card, &apdu);
+ if (r)
+ return (r);
+ r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+ if (r)
+ return (r);
+ return apdu.resplen;
+}
+
+static int westcos_get_crypte_challenge(sc_card_t * card, const u8 * key,
+ u8 * result, size_t * len)
+{
+ int r;
+ DES_key_schedule ks1, ks2;
+ u8 buf[8];
+ if ((*len) < sizeof(buf))
+ return SC_ERROR_INVALID_ARGUMENTS;
+ *len = 8;
+ r = sc_get_challenge(card, buf, *len);
+ if (r)
+ return r;
+ DES_set_key((const_DES_cblock *) & key[0], &ks1);
+ DES_set_key((const_DES_cblock *) & key[8], &ks2);
+ DES_ecb2_encrypt((const_DES_cblock *)buf, (DES_cblock*)result, &ks1, &ks2, DES_ENCRYPT);
+ return 0;
+}
+
+static int westcos_pin_cmd(sc_card_t * card, struct sc_pin_cmd_data *data,
+ int *tries_left)
+{
+ int r;
+ u8 buf[20]; //, result[20];
+ sc_apdu_t apdu;
+ size_t len = 0;
+ int pad = 0, use_pin_pad = 0, ins, p1 = 0;
+ if (card == NULL)
+ return SC_ERROR_INVALID_ARGUMENTS;
+ if (card->ctx->debug >= 1)
+ sc_debug(card->ctx,
+ "westcos_pin_cmd:data->pin_type=%X, data->cmd=%X\n",
+ data->pin_type, data->cmd);
+ if (tries_left)
+ *tries_left = -1;
+ switch (data->pin_type) {
+ case SC_AC_AUT:
+ len = sizeof(buf);
+ r = westcos_get_crypte_challenge(card, data->pin1.data, buf,
+ &len);
+ if (r)
+ return (r);
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x82, 0x00,
+ data->pin_reference);
+ apdu.lc = len;
+ apdu.datalen = len;
+ apdu.data = buf;
+ r = sc_transmit_apdu(card, &apdu);
+ if (r)
+ return (r);
+ return sc_check_sw(card, apdu.sw1, apdu.sw2);
+ break;
+ case SC_AC_CHV:
+ if (data->flags & SC_PIN_CMD_NEED_PADDING)
+ pad = 1;
+ if (data->flags & SC_PIN_CMD_USE_PINPAD)
+ use_pin_pad = 1;
+ data->pin1.offset = 0;
+ data->pin1.encoding = SC_PIN_ENCODING_GLP;
+ if (data->pin1.min_length == 0)
+ data->pin1.min_length = 4;
+ if (data->pin1.max_length == 0)
+ data->pin1.max_length = 12;
+ switch (data->cmd) {
+ case SC_PIN_CMD_VERIFY:
+ ins = 0x20;
+ if ((r =
+ sc_build_pin(buf, sizeof(buf), &data->pin1,
+ pad)) < 0)
+ return r;
+ len = r;
+ break;
+ case SC_PIN_CMD_CHANGE:
+ ins = 0x24;
+ if (data->pin1.len != 0 || use_pin_pad) {
+ if ((r =
+ sc_build_pin(buf, sizeof(buf),
+ &data->pin1, pad)) < 0)
+ return r;
+ len += r;
+ } else {
+
+ /* implicit test */
+ p1 = 1;
+ }
+ data->pin2.offset = data->pin1.offset + len;
+ data->pin2.encoding = SC_PIN_ENCODING_GLP;
+ if ((r =
+ sc_build_pin(buf + len, sizeof(buf) - len,
+ &data->pin2, pad)) < 0)
+ return r;
+ len += r;
+ break;
+ case SC_PIN_CMD_UNBLOCK:
+ ins = 0x2C;
+ if (data->pin1.len != 0 || use_pin_pad) {
+ if ((r =
+ sc_build_pin(buf, sizeof(buf),
+ &data->pin1, pad)) < 0)
+ return r;
+ len += r;
+ } else {
+ p1 |= 0x02;
+ }
+ if (data->pin2.len != 0 || use_pin_pad) {
+ data->pin2.offset = data->pin1.offset + len;
+ data->pin2.encoding = SC_PIN_ENCODING_GLP;
+ if ((r =
+ sc_build_pin(buf + len, sizeof(buf) - len,
+ &data->pin2, pad)) < 0)
+ return r;
+ len += r;
+ } else {
+ p1 |= 0x01;
+ }
+ break;
+ default:
+ return SC_ERROR_NOT_SUPPORTED;
+ }
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, ins, p1,
+ data->pin_reference);
+ apdu.lc = len;
+ apdu.datalen = len;
+ apdu.data = buf;
+ apdu.resplen = 0;
+ apdu.sensitive = 1;
+ if (!use_pin_pad) {
+
+ /* Transmit the APDU to the card */
+ r = sc_transmit_apdu(card, &apdu);
+
+ /* Clear the buffer - it may contain pins */
+ sc_mem_clear(buf, sizeof(buf));
+ } else {
+ data->apdu = &apdu;
+ if (card->reader
+ && card->reader->ops
+ && card->reader->ops->perform_verify) {
+ r = card->reader->ops->perform_verify(card->
+ reader,
+ card->
+ slot,
+ data);
+ } else {
+ r = SC_ERROR_NOT_SUPPORTED;
+ }
+ }
+ if (r)
+ return (r);
+ return sc_check_sw(card, apdu.sw1, apdu.sw2);
+ default:
+ return SC_ERROR_NOT_SUPPORTED;
+ }
+}
+
+static int sc_get_atr(sc_card_t * card)
+{
+ int r;
+ sc_apdu_t apdu;
+ u8 buf[sizeof(card->atr)];
+ if (card == NULL)
+ return SC_ERROR_INVALID_ARGUMENTS;
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xEC, 0x00, 0x00);
+ apdu.cla = 0x80;
+ apdu.le = 0x0d;
+ apdu.resplen = 0x0d;
+ apdu.resp = buf;
+ r = sc_transmit_apdu(card, &apdu);
+ if (r)
+ return (r);
+ r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+ if (r)
+ return (r);
+ memcpy(card->atr, buf, sizeof(card->atr));
+ card->atr_len = apdu.resplen;
+ return r;
+}
+
+static int sc_lock_phase(sc_card_t * card, u8 phase)
+{
+ int r;
+ sc_apdu_t apdu;
+ if (card == NULL)
+ return SC_ERROR_INVALID_ARGUMENTS;
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x16, phase, 0x00);
+ apdu.cla = 0x80;
+ r = sc_transmit_apdu(card, &apdu);
+ if (r)
+ return (r);
+ return sc_check_sw(card, apdu.sw1, apdu.sw2);
+}
+
+static int westcos_card_ctl(sc_card_t * card, unsigned long cmd, void *ptr)
+{
+ int r;
+ size_t buflen;
+ u8 buf[256];
+ sc_apdu_t apdu;
+ struct sc_pin_cmd_data data;
+ sc_serial_number_t *serialnr;
+ priv_data_t *priv_data = NULL;
+ if (card == NULL)
+ return SC_ERROR_INVALID_ARGUMENTS;
+ if (card->ctx->debug >= 1)
+ sc_debug(card->ctx, "westcos_card_ctl cmd = %X\n", cmd);
+ priv_data = (priv_data_t *) card->drv_data;
+ switch (cmd) {
+ case SC_CARDCTL_GET_DEFAULT_KEY:
+ return westcos_get_default_key(card,
+ (struct sc_cardctl_default_key
+ *)ptr);
+ break;
+ case SC_CARDCTL_LIFECYCLE_SET:
+ if (1) {
+ int mode = *((int *)ptr);
+ switch (mode) {
+ case SC_CARDCTRL_LIFECYCLE_ADMIN:
+ if (priv_data->flags & JAVACARD) {
+ return 0;
+ }
+ if (card->atr[10] == 0x80
+ || card->atr[10] == 0x81)
+ return 0;
+ return SC_ERROR_CARD_CMD_FAILED;
+ case SC_CARDCTRL_LIFECYCLE_USER:
+ if (card->atr[10] == 0x80) {
+ r = sc_lock_phase(card, 0x02);
+ if (r)
+ return (r);
+ r = sc_get_atr(card);
+ if (r)
+ return (r);
+ r = sc_card_ctl(card,
+ SC_CARDCTL_WESTCOS_AUT_KEY,
+ NULL);
+ if (r)
+ return (r);
+ }
+ if (card->atr[10] == 0x81) {
+ r = sc_lock_phase(card, 0x01);
+ if (r)
+ return (r);
+ r = sc_get_atr(card);
+ if (r)
+ return (r);
+ return 0;
+ }
+ return SC_ERROR_CARD_CMD_FAILED;
+ case SC_CARDCTRL_LIFECYCLE_OTHER:
+ default:
+ break;
+ }
+ }
+ break;
+ case SC_CARDCTL_GET_SERIALNR:
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xEE, 0x00,
+ 0x00);
+ apdu.cla = 0xb0;
+ apdu.le = 8;
+ apdu.resp = buf;
+ apdu.resplen = 10; /* include SW's */
+ r = sc_transmit_apdu(card, &apdu);
+ if (r)
+ return (r);
+ r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+ if (r)
+ return (r);
+ if (SC_MAX_SERIALNR < 8)
+ return SC_ERROR_NOT_SUPPORTED;
+ serialnr = (sc_serial_number_t *) ptr;
+ serialnr->len = 8;
+ memcpy(serialnr->value, buf, serialnr->len);
+ return 0;
+ case SC_CARDCTL_WESTCOS_CREATE_MF:
+ buf[0] = *((u8 *) ptr);
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x3F,
+ 0x00);
+ apdu.cla = 0x80;
+ apdu.lc = 1;
+ apdu.datalen = 1;
+ apdu.data = buf;
+ apdu.le = 0;
+ r = sc_transmit_apdu(card, &apdu);
+ if (r)
+ return (r);
+ return sc_check_sw(card, apdu.sw1, apdu.sw2);
+ case SC_CARDCTL_WESTCOS_COMMIT:
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x2C, 0x00, 0x00);
+ apdu.cla = 0x80;
+ r = sc_transmit_apdu(card, &apdu);
+ if (r)
+ return (r);
+ r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+ if (r)
+ return (r);
+ return r;
+ case SC_CARDCTL_WESTCOS_ROLLBACK:
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x24, 0x00, 0x00);
+ apdu.cla = 0x80;
+ r = sc_transmit_apdu(card, &apdu);
+ if (r)
+ return (r);
+ r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+ if (r)
+ return (r);
+ return r;
+ case SC_CARDCTL_WESTCOS_AUT_KEY:
+ if (ptr != NULL)
+ priv_data->default_key = *((sc_autkey_t *) ptr);
+ memset(&data, 0, sizeof(data));
+ data.pin_type = SC_AC_AUT;
+ data.pin_reference = priv_data->default_key.key_reference;
+ data.pin1.len = priv_data->default_key.key_len;
+ data.pin1.data = priv_data->default_key.key_value;
+ return sc_pin_cmd(card, &data, NULL);
+ case SC_CARDCTL_WESTCOS_CHANGE_KEY:
+ if (1) {
+ int lrc;
+ u8 temp[7];
+ sc_changekey_t *ck = (sc_changekey_t *) ptr;
+ sc_autkey_t master_key;
+ if (ck->master_key.key_len != 0)
+ master_key = ck->master_key;
+
+ else
+ master_key = priv_data->default_key;
+ memcpy(temp, ck->key_template, sizeof(temp));
+ westcos_compute_aetb_crc(CRC_A, ck->new_key.key_value,
+ ck->new_key.key_len, &temp[5], &temp[6]);
+ for (r = 0, temp[4] = 0xAA, lrc = 0; r < sizeof(temp);
+ r++)
+ lrc += temp[r];
+ temp[4] = (lrc % 256);
+ buflen = sizeof(buf);
+ r = westcos_get_crypte_challenge(card,
+ master_key.key_value,
+ buf, &buflen);
+ if (r)
+ return (r);
+ memcpy(&buf[buflen], temp, sizeof(temp));
+ buflen += sizeof(temp);
+ memcpy(&buf[buflen], ck->new_key.key_value,
+ ck->new_key.key_len);
+ buflen += ck->new_key.key_len;
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT,
+ 0xD8, ck->new_key.key_reference,
+ master_key.key_reference);
+ apdu.cla = 0x80;
+ apdu.lc = buflen;
+ apdu.datalen = buflen;
+ apdu.data = buf;
+ r = sc_transmit_apdu(card, &apdu);
+ if (r)
+ return (r);
+ r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+ if (r)
+ return (r);
+ return r;
+ }
+ case SC_CARDCTL_WESTCOS_SET_DEFAULT_KEY:
+ priv_data->default_key = *((sc_autkey_t *) ptr);
+ return 0;
+ case SC_CARDCTL_WESTCOS_LOAD_DATA:
+
+ /* ptr[0] = 0x01 pour generique appli, 0x81 pour appli avec pme */
+ buf[0] = *((u8 *) ptr);
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xB2, 0x80,
+ 0x14);
+ apdu.cla = 0xB0;
+ apdu.lc = 1;
+ apdu.datalen = 1;
+ apdu.data = buf;
+ r = sc_transmit_apdu(card, &apdu);
+ if (r)
+ return (r);
+ return sc_check_sw(card, apdu.sw1, apdu.sw2);
+ }
+ return SC_ERROR_NOT_SUPPORTED;
+}
+static int westcos_set_security_env(sc_card_t *card,
+ const struct sc_security_env *env,
+ int se_num)
+{
+ priv_data_t *priv_data = NULL;
+ if (card == NULL)
+ return SC_ERROR_INVALID_ARGUMENTS;
+ if (card->ctx->debug >= 1)
+ sc_debug(card->ctx, "westcos_set_security_env\n");
+ priv_data = (priv_data_t *) card->drv_data;
+ priv_data->env = *env;
+ return 0;
+}
+
+static int westcos_restore_security_env(sc_card_t *card, int se_num)
+{
+ if (card == NULL)
+ return SC_ERROR_INVALID_ARGUMENTS;
+ if (card->ctx->debug >= 1)
+ sc_debug(card->ctx, "westcos_restore_security_env\n");
+ return 0;
+}
+
+static int westcos_sign_decipher(int mode, sc_card_t *card,
+ const u8 * data, size_t data_len, u8 * out,
+ size_t outlen)
+{
+ int r;
+ int idx = 0;
+ u8 buf[180];
+ sc_file_t *keyfile = sc_file_new();
+ priv_data_t *priv_data = NULL;
+ int pad;
+
+#ifdef ENABLE_OPENSSL
+ RSA *rsa = NULL;
+ BIO *mem = BIO_new(BIO_s_mem());
+
+#endif
+ if (card == NULL)
+ return SC_ERROR_INVALID_ARGUMENTS;
+ if (card->ctx->debug >= 1)
+ sc_debug(card->ctx, "westcos_sign_decipher\n");
+ priv_data = (priv_data_t *) card->drv_data;
+ if (keyfile == NULL || mem == NULL || priv_data == NULL) {
+ r = SC_ERROR_OUT_OF_MEMORY;
+ goto out;
+ }
+#ifndef ENABLE_OPENSSL
+ r = SC_ERROR_NOT_SUPPORTED;
+
+#else
+ if ((priv_data->env.flags) & SC_ALGORITHM_RSA_PAD_PKCS1)
+ pad = RSA_PKCS1_PADDING;
+
+ else if ((priv_data->env.flags) & SC_ALGORITHM_RSA_RAW)
+ pad = RSA_NO_PADDING;
+
+ else {
+ r = SC_ERROR_INVALID_ARGUMENTS;
+ goto out;
+ }
+ r = sc_select_file(card, &(priv_data->env.file_ref), &keyfile);
+ if (r)
+ goto out;
+
+ do {
+ int alire;
+ alire = min(((keyfile->size) - idx), sizeof(buf));
+ if (alire <= 0)
+ break;
+ if (card->ctx->debug >= 5)
+ sc_debug(card->ctx, "idx = %d, alire=%d\n", idx, alire);
+ r = sc_read_binary(card, idx, buf, alire, 0);
+ if (r < 0)
+ goto out;
+ BIO_write(mem, buf, r);
+ idx += r;
+ } while (1);
+ BIO_set_mem_eof_return(mem, -1);
+ if (!d2i_RSAPrivateKey_bio(mem, &rsa)) {
+ if (card->ctx->debug >= 5)
+ sc_debug(card->ctx, "RSA clef invalide, %d\n",
+ ERR_get_error());
+ r = SC_ERROR_UNKNOWN;
+ goto out;
+ }
+
+ /* pkcs11 reroute routine cryptage vers la carte */
+ rsa->meth = RSA_PKCS1_SSLeay();
+ if (RSA_size(rsa) > outlen) {
+ if (card->ctx->debug >= 5)
+ sc_debug(card->ctx, "Buffer too small\n");
+ r = SC_ERROR_OUT_OF_MEMORY;
+ goto out;
+ }
+#if 1
+ if (mode) { /* decipher */
+ r = RSA_private_decrypt(data_len, data, out, rsa, pad);
+ if (r == -1) {
+
+#ifdef DEBUG_SSL
+ print_openssl_erreur();
+
+#endif
+ if (card->ctx->debug >= 5)
+ sc_debug(card->ctx, "Decipher error %d\n",
+ ERR_get_error());
+ r = SC_ERROR_UNKNOWN;
+ goto out;
+ }
+ }
+
+ else { /* signature */
+
+ r = RSA_private_encrypt(data_len, data, out, rsa, pad);
+ if (r == -1) {
+
+#ifdef DEBUG_SSL
+ print_openssl_erreur();
+
+#endif
+ if (card->ctx->debug >= 5)
+ sc_debug(card->ctx, "Signature error %d\n",
+ ERR_get_error());
+ r = SC_ERROR_UNKNOWN;
+ goto out;
+ }
+ }
+
+#else
+ if (RSA_sign(nid, data, data_len, out, &outlen, rsa) != 1) {
+ if (card->ctx->debug >= 5)
+ sc_debug(card->ctx, "RSA_sign error %d \n",
+ ERR_get_error());
+ r = SC_ERROR_UNKNOWN;
+ goto out;
+ }
+ r = outlen;
+
+#endif
+#endif /* ENABLE_OPENSSL */
+ out:
+#ifdef ENABLE_OPENSSL
+ if (mem)
+ BIO_free(mem);
+ if (rsa)
+ RSA_free(rsa);
+
+#endif
+ if (keyfile)
+ sc_file_free(keyfile);
+ return r;
+}
+
+static int westcos_compute_signature(sc_card_t *card, const u8 * data,
+ size_t data_len, u8 * out, size_t outlen)
+{
+ return westcos_sign_decipher(0, card, data, data_len, out, outlen);
+}
+
+static int westcos_decipher(sc_card_t *card, const u8 * crgram,
+ size_t crgram_len, u8 * out, size_t outlen)
+{
+ return westcos_sign_decipher(1, card, crgram, crgram_len, out, outlen);
+}
+
+struct sc_card_driver *sc_get_westcos_driver(void)
+{
+ if (iso_ops == NULL)
+ iso_ops = sc_get_iso7816_driver()->ops;
+ westcos_ops = *iso_ops;
+
+ westcos_ops.match_card = westcos_match_card;
+ westcos_ops.init = westcos_init;
+ westcos_ops.finish = westcos_finish;
+ /* read_binary */
+ /* write_binary */
+ /* update_binary */
+ westcos_ops.erase_binary = NULL;
+ /* read_record */
+ /* write_record */
+ /* append_record */
+ /* update_record */
+ westcos_ops.select_file = westcos_select_file;
+ /* get_response */
+ /* get_challenge */
+ westcos_ops.verify = NULL;
+ westcos_ops.logout = NULL;
+ westcos_ops.restore_security_env = westcos_restore_security_env;
+ westcos_ops.set_security_env = westcos_set_security_env;
+ westcos_ops.decipher = westcos_decipher;
+ westcos_ops.compute_signature = westcos_compute_signature;
+ westcos_ops.change_reference_data = NULL;
+ westcos_ops.reset_retry_counter = NULL;
+ westcos_ops.create_file = westcos_create_file;
+ westcos_ops.delete_file = westcos_delete_file;
+ westcos_ops.list_files = westcos_list_files;
+ westcos_ops.check_sw = westcos_check_sw;
+ westcos_ops.card_ctl = westcos_card_ctl;
+ westcos_ops.process_fci = westcos_process_fci;
+ westcos_ops.construct_fci = NULL;
+ westcos_ops.pin_cmd = westcos_pin_cmd;
+ westcos_ops.get_data = NULL;
+ westcos_ops.put_data = NULL;
+ westcos_ops.delete_record = NULL;
+
+ return &westcos_drv;
+}
+
diff --git a/src/libopensc/cardctl.h b/src/libopensc/cardctl.h
index 9483f773..657fa185 100644
--- a/src/libopensc/cardctl.h
+++ b/src/libopensc/cardctl.h
@@ -175,6 +175,19 @@ enum {
SC_CARDCTL_RTECP_INIT,
SC_CARDCTL_RTECP_INIT_END,
SC_CARDCTL_RTECP_GENERATE_KEY,
+
+ /*
+ * Westcos specific
+ */
+ SC_CARDCTL_WESTCOS_FREEZE = _CTL_PREFIX('W', 'T', 'C'),
+ SC_CARDCTL_WESTCOS_CREATE_MF,
+ SC_CARDCTL_WESTCOS_COMMIT,
+ SC_CARDCTL_WESTCOS_ROLLBACK,
+ SC_CARDCTL_WESTCOS_AUT_KEY,
+ SC_CARDCTL_WESTCOS_CHANGE_KEY,
+ SC_CARDCTL_WESTCOS_SET_DEFAULT_KEY,
+ SC_CARDCTL_WESTCOS_LOAD_DATA,
+
};
enum {
@@ -461,6 +474,22 @@ typedef struct sc_cardctl_asepcos_activate_file {
#define OP_TYPE_GENERATE 0
#define OP_TYPE_STORE 1
+/*
+ * Westcos
+ */
+
+typedef struct {
+ int key_reference;
+ int key_len; //8, 16 or 24
+ u8 key_value[24];
+}sc_autkey_t;
+
+typedef struct {
+ sc_autkey_t master_key;
+ sc_autkey_t new_key;
+ u8 key_template[7];
+}sc_changekey_t;
+
/*
* RuToken types and constants
*/
diff --git a/src/libopensc/cards.h b/src/libopensc/cards.h
index 044a19fb..465059ba 100644
--- a/src/libopensc/cards.h
+++ b/src/libopensc/cards.h
@@ -174,6 +174,7 @@ extern sc_card_driver_t *sc_get_akis_driver(void);
extern sc_card_driver_t *sc_get_entersafe_driver(void);
extern sc_card_driver_t *sc_get_rutoken_driver(void);
extern sc_card_driver_t *sc_get_rtecp_driver(void);
+extern sc_card_driver_t *sc_get_westcos_driver(void);
#ifdef __cplusplus
}
diff --git a/src/libopensc/ctx.c b/src/libopensc/ctx.c
index db9953b5..9b2fb872 100644
--- a/src/libopensc/ctx.c
+++ b/src/libopensc/ctx.c
@@ -84,6 +84,7 @@ static const struct _sc_driver_entry internal_card_drivers[] = {
#endif
{ "rutoken", (void *(*)(void)) sc_get_rutoken_driver },
{ "rutoken_ecp",(void *(*)(void)) sc_get_rtecp_driver },
+ { "westcos", (void *(*)(void)) sc_get_westcos_driver },
/* emv is not really used, not sure if it works, but it conflicts with
muscle and rutoken driver, thus has to be after them */
{ "emv", (void *(*)(void)) sc_get_emv_driver },
diff --git a/src/libopensc/p15emu-westcos.c b/src/libopensc/p15emu-westcos.c
new file mode 100644
index 00000000..69e58908
--- /dev/null
+++ b/src/libopensc/p15emu-westcos.c
@@ -0,0 +1,251 @@
+/*
+ * p15emu-westcos.c: pkcs15 emulation for westcos card
+ *
+ * Copyright (C) 2009 francois.leblanc@cev-sa.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
+#include
+#include
+
+#include "pkcs15.h"
+#include "cardctl.h"
+#include "compat_strlcpy.h"
+
+static int sc_pkcs15emu_westcos_init(sc_pkcs15_card_t * p15card)
+{
+ int i, r;
+ int modulus_length = 0, usage = 0;
+ u8 buf[256];
+ sc_card_t *card = p15card->card;
+ sc_context_t *ctx = card->ctx;
+ sc_serial_number_t serial;
+ sc_path_t path;
+ sc_file_t *file = NULL;
+ sc_format_path("3F00", &path);
+ r = sc_select_file(card, &path, &file);
+ if (r)
+ goto out;
+ if (file)
+ sc_file_free(file);
+ file = NULL;
+ if (p15card->label != NULL)
+ free(p15card->label);
+ p15card->label = strdup("westcos");
+ if (p15card->manufacturer_id != NULL)
+ free(p15card->manufacturer_id);
+ p15card->manufacturer_id = strdup("CEV");
+
+ /* get serial number */
+ r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial);
+ r = sc_bin_to_hex(serial.value, serial.len, buf, sizeof(buf), 0);
+ if (r)
+ goto out;
+ if (p15card->serial_number != NULL)
+ free(p15card->serial_number);
+ p15card->serial_number = strdup(buf);
+ p15card->version = buf[6];
+ p15card->flags = SC_PKCS15_CARD_FLAG_LOGIN_REQUIRED;
+ sc_format_path("AAAA", &path);
+ sc_ctx_suppress_errors_on(ctx);
+ r = sc_select_file(card, &path, &file);
+ sc_ctx_suppress_errors_off(ctx);
+ if (!r) {
+ for (i = 0; i < 1; i++) {
+ unsigned int flags;
+ struct sc_pkcs15_pin_info pin_info;
+ struct sc_pkcs15_object pin_obj;
+ memset(&pin_info, 0, sizeof(pin_info));
+ memset(&pin_obj, 0, sizeof(pin_obj));
+ flags = SC_PKCS15_PIN_FLAG_INITIALIZED;
+ if (i == 1) {
+ flags |=
+ SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED |
+ SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN;
+ }
+ pin_info.auth_id.len = 1;
+ pin_info.auth_id.value[0] = i + 1;
+ pin_info.reference = i;
+ pin_info.flags = flags;
+ pin_info.type = SC_PKCS15_PIN_TYPE_BCD;
+ pin_info.min_length = 4;
+ pin_info.stored_length = 8;
+ pin_info.max_length = 8;
+ pin_info.pad_char = 0xff;
+ pin_info.path = path;
+ pin_info.tries_left = -1;
+ if (i == 1)
+ strlcpy(pin_obj.label, "Unblock",
+ sizeof(pin_obj.label));
+
+ else
+ strlcpy(pin_obj.label, "User",
+ sizeof(pin_obj.label));
+ pin_obj.flags =
+ SC_PKCS15_CO_FLAG_MODIFIABLE |
+ SC_PKCS15_CO_FLAG_PRIVATE;
+ r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj,
+ &pin_info);
+ if (r)
+ goto out;
+ }
+ }
+ if (file)
+ sc_file_free(file);
+ file = NULL;
+ sc_format_path("0002", &path);
+ sc_ctx_suppress_errors_on(ctx);
+ r = sc_select_file(card, &path, &file);
+ sc_ctx_suppress_errors_off(ctx);
+ if (!r) {
+ struct sc_pkcs15_cert_info cert_info;
+ struct sc_pkcs15_object cert_obj;
+ struct sc_pkcs15_pubkey_info pubkey_info;
+ struct sc_pkcs15_object pubkey_obj;
+ struct sc_pkcs15_pubkey *pkey = NULL;
+ memset(&cert_info, 0, sizeof(cert_info));
+ memset(&cert_obj, 0, sizeof(cert_obj));
+ cert_info.id.len = 1;
+ cert_info.id.value[0] = 0x45;
+ cert_info.authority = 0;
+ cert_info.path = path;
+ sc_ctx_suppress_errors_on(ctx);
+ r = sc_pkcs15_read_certificate(p15card, &cert_info,
+ (sc_pkcs15_cert_t
+ **) (&cert_obj.data));
+ sc_ctx_suppress_errors_off(ctx);
+ if (!r) {
+ sc_pkcs15_cert_t *cert =
+ (sc_pkcs15_cert_t *) (cert_obj.data);
+ strlcpy(cert_obj.label, "User certificat",
+ sizeof(cert_obj.label));
+ cert_obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE;
+ r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj,
+ &cert_info);
+ if (r)
+ goto out;
+ pkey = &cert->key;
+ }
+ memset(&pubkey_info, 0, sizeof(pubkey_info));
+ memset(&pubkey_obj, 0, sizeof(pubkey_obj));
+ pubkey_info.id.len = 1;
+ pubkey_info.id.value[0] = 0x45;
+ pubkey_info.modulus_length = modulus_length;
+ pubkey_info.key_reference = 1;
+ pubkey_info.native = 1;
+ pubkey_info.usage =
+ SC_PKCS15_PRKEY_USAGE_VERIFY |
+ SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER |
+ SC_PKCS15_PRKEY_USAGE_ENCRYPT |
+ SC_PKCS15_PRKEY_USAGE_WRAP;
+ pubkey_info.path = path;
+ strlcpy(pubkey_obj.label, "Public Key",
+ sizeof(pubkey_obj.label));
+ pubkey_obj.auth_id.len = 1;
+ pubkey_obj.auth_id.value[0] = 1;
+ pubkey_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE;
+ pubkey_obj.type = SC_PKCS15_TYPE_PUBKEY_RSA;
+ if (pkey == NULL) {
+ pubkey_obj.data = &pubkey_info;
+ r = sc_pkcs15_read_pubkey(p15card, &pubkey_obj, &pkey);
+ if (r)
+ goto out;
+
+ //force rechargement clef et maj infos lors de sc_pkcs15emu_add_rsa_pubkey (sinon modulus = 0)
+ pubkey_obj.flags = 0;
+ }
+ if (pkey->algorithm == SC_ALGORITHM_RSA) {
+ modulus_length = (int)(pkey->u.rsa.modulus.len * 8);
+ }
+ pubkey_info.modulus_length = modulus_length;
+ pubkey_obj.data = pkey;
+ r = sc_pkcs15emu_add_rsa_pubkey(p15card, &pubkey_obj,
+ &pubkey_info);
+ if (r < 0)
+ goto out;
+ }
+ if (!usage) {
+ usage =
+ SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_DECRYPT |
+ SC_PKCS15_PRKEY_USAGE_NONREPUDIATION;
+ }
+ if (file)
+ sc_file_free(file);
+ file = NULL;
+ sc_format_path("0001", &path);
+ sc_ctx_suppress_errors_on(ctx);
+ r = sc_select_file(card, &path, &file);
+ sc_ctx_suppress_errors_off(ctx);
+ if (!r) {
+ struct sc_pkcs15_prkey_info prkey_info;
+ struct sc_pkcs15_object prkey_obj;
+ memset(&prkey_info, 0, sizeof(prkey_info));
+ memset(&prkey_obj, 0, sizeof(prkey_obj));
+ prkey_info.id.len = 1;
+ prkey_info.id.value[0] = 0x45;
+ prkey_info.usage =
+ SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_DECRYPT
+ | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION;
+ prkey_info.native = 1;
+ prkey_info.key_reference = 1;
+ prkey_info.modulus_length = modulus_length;
+ prkey_info.path = path;
+ strlcpy(prkey_obj.label, "Private Key",
+ sizeof(prkey_obj.label));
+ prkey_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE;
+ prkey_obj.auth_id.len = 1;
+ prkey_obj.auth_id.value[0] = 1;
+ r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj,
+ &prkey_info);
+ if (r < 0)
+ goto out;
+ }
+ r = 0;
+ out:if (file)
+ sc_file_free(file);
+ return r;
+}
+
+static int westcos_detect_card(sc_pkcs15_card_t * p15card)
+{
+ sc_card_t *card = p15card->card;
+ sc_context_t *ctx = card->ctx;
+ char *name = "WESTCOS";
+ if (ctx->debug >= 1)
+ sc_debug(ctx, "westcos_detect_card (%s)", card->name);
+ if (strncmp(card->name, name, strlen(name)))
+ return SC_ERROR_WRONG_CARD;
+ return SC_SUCCESS;
+}
+
+int sc_pkcs15emu_westcos_init_ex(sc_pkcs15_card_t * p15card,
+ sc_pkcs15emu_opt_t * opts)
+{
+ int r;
+ sc_card_t *card = p15card->card;
+ sc_context_t *ctx = card->ctx;
+ if (ctx->debug >= 1)
+ sc_debug(ctx, "sc_pkcs15_init_func_ex westcos\n");
+ if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK)
+ return sc_pkcs15emu_westcos_init(p15card);
+ r = westcos_detect_card(p15card);
+ if (r)
+ return SC_ERROR_WRONG_CARD;
+ return sc_pkcs15emu_westcos_init(p15card);
+}
diff --git a/src/libopensc/pkcs15-syn.c b/src/libopensc/pkcs15-syn.c
index b0546f56..801f747c 100644
--- a/src/libopensc/pkcs15-syn.c
+++ b/src/libopensc/pkcs15-syn.c
@@ -28,6 +28,8 @@
#include
#include
+extern int sc_pkcs15emu_westcos_init_ex(sc_pkcs15_card_t *p15card,
+ sc_pkcs15emu_opt_t *opts);
extern int sc_pkcs15emu_openpgp_init_ex(sc_pkcs15_card_t *,
sc_pkcs15emu_opt_t *);
extern int sc_pkcs15emu_infocamere_init_ex(sc_pkcs15_card_t *,
@@ -58,6 +60,7 @@ static struct {
const char * name;
int (*handler)(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *);
} builtin_emulators[] = {
+ { "westcos", sc_pkcs15emu_westcos_init_ex },
{ "openpgp", sc_pkcs15emu_openpgp_init_ex },
{ "infocamere", sc_pkcs15emu_infocamere_init_ex },
{ "starcert", sc_pkcs15emu_starcert_init_ex },
diff --git a/src/pkcs15init/Makefile.am b/src/pkcs15init/Makefile.am
index 5ffdbba6..7f41e86f 100644
--- a/src/pkcs15init/Makefile.am
+++ b/src/pkcs15init/Makefile.am
@@ -24,7 +24,8 @@ dist_pkgdata_DATA = \
rutoken.profile \
asepcos.profile \
entersafe.profile \
- rutoken_ecp.profile
+ rutoken_ecp.profile \
+ westcos.profile
AM_CPPFLAGS = -DSC_PKCS15_PROFILE_DIRECTORY=\"$(pkgdatadir)\"
AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(LTLIB_CFLAGS)
@@ -32,6 +33,7 @@ INCLUDES = -I$(top_srcdir)/src/common -I$(top_builddir)/src/include
libpkcs15init_la_SOURCES = \
pkcs15-lib.c profile.c keycache.c \
+ pkcs15-westcos.c \
pkcs15-gpk.c pkcs15-miocos.c pkcs15-cflex.c \
pkcs15-cardos.c pkcs15-jcop.c pkcs15-starcos.c \
pkcs15-oberthur.c pkcs15-setcos.c pkcs15-incrypto34.c \
diff --git a/src/pkcs15init/Makefile.mak b/src/pkcs15init/Makefile.mak
index 1626668e..ca23a37f 100644
--- a/src/pkcs15init/Makefile.mak
+++ b/src/pkcs15init/Makefile.mak
@@ -10,7 +10,7 @@ OBJECTS = pkcs15-lib.obj profile.obj keycache.obj \
pkcs15-cardos.obj pkcs15-jcop.obj pkcs15-starcos.obj \
pkcs15-oberthur.obj pkcs15-setcos.obj pkcs15-incrypto34.obj \
pkcs15-muscle.obj pkcs15-asepcos.obj pkcs15-rutoken.obj \
- pkcs15-entersafe.obj pkcs15-rtecp.obj \
+ pkcs15-entersafe.obj pkcs15-rtecp.obj pkcs15-westcos.obj \
versioninfo.res
all: install-headers $(TARGET)
diff --git a/src/pkcs15init/pkcs15-init.h b/src/pkcs15init/pkcs15-init.h
index 80dd83b6..8c37a186 100644
--- a/src/pkcs15init/pkcs15-init.h
+++ b/src/pkcs15init/pkcs15-init.h
@@ -404,6 +404,7 @@ extern struct sc_pkcs15init_operations *sc_pkcs15init_get_asepcos_ops(void);
extern struct sc_pkcs15init_operations *sc_pkcs15init_get_rutoken_ops(void);
extern struct sc_pkcs15init_operations *sc_pkcs15init_get_entersafe_ops(void);
extern struct sc_pkcs15init_operations *sc_pkcs15init_get_rtecp_ops(void);
+extern struct sc_pkcs15init_operations *sc_pkcs15init_get_westcos_ops(void);
#ifdef __cplusplus
}
diff --git a/src/pkcs15init/pkcs15-lib.c b/src/pkcs15init/pkcs15-lib.c
index 03dbeaa9..6bc05473 100644
--- a/src/pkcs15init/pkcs15-lib.c
+++ b/src/pkcs15init/pkcs15-lib.c
@@ -165,6 +165,7 @@ static struct profile_operations {
{ "asepcos", (void*) sc_pkcs15init_get_asepcos_ops },
{ "entersafe",(void*) sc_pkcs15init_get_entersafe_ops },
{ "rutoken_ecp", (void *) sc_pkcs15init_get_rtecp_ops },
+ { "westcos", (void *) sc_pkcs15init_get_westcos_ops },
{ NULL, NULL },
};
diff --git a/src/pkcs15init/pkcs15-westcos.c b/src/pkcs15init/pkcs15-westcos.c
new file mode 100644
index 00000000..321647d7
--- /dev/null
+++ b/src/pkcs15init/pkcs15-westcos.c
@@ -0,0 +1,464 @@
+/*
+ * pkcs15-westcos.c: pkcs15 support for westcos card
+ *
+ * Copyright (C) 2009 francois.leblanc@cev-sa.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
+#endif
+
+#include
+#include
+#include
+
+#include
+#include
+#include "pkcs15-init.h"
+#include "profile.h"
+
+#ifdef ENABLE_OPENSSL
+#include
+#include
+#include
+
+
+#include
+#include
+#endif
+
+extern int sc_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2);
+
+#if 0
+/*
+ * Get private and public key file
+ */
+static int _westcos_get_keyfiles(sc_profile_t *profile, sc_card_t *card,
+ const sc_path_t *df_path,
+ sc_file_t **prkf, sc_file_t **pukf)
+{
+ sc_path_t path = *df_path;
+ int r;
+
+ /* Get the private key file */
+ r = SC_ERROR_NOT_SUPPORTED; //sc_profile_get_file_by_path(profile, &path, prkf);
+ if (r < 0) {
+ char pbuf[SC_MAX_PATH_STRING_SIZE];
+
+ r = sc_path_print(pbuf, sizeof(pbuf), &path);
+ if (r != SC_SUCCESS)
+ pbuf[0] = '\0';
+
+ return r;
+ }
+
+ /* Get the public key file */
+ path.len -= 2;
+ sc_append_file_id(&path, 0x1012);
+ r = SC_ERROR_NOT_SUPPORTED; //sc_profile_get_file_by_path(profile, &path, pukf);
+ if (r < 0) {
+ sc_file_free(*prkf);
+ return r;
+ }
+
+ return 0;
+}
+#endif /* currently unused */
+
+static int westcos_pkcs15init_init_card(sc_profile_t *profile,
+ sc_card_t *card)
+{
+ int r;
+ struct sc_path path;
+
+ sc_format_path("3F00", &path);
+ r = sc_select_file(card, &path, NULL);
+ if(r) return (r);
+
+ return r;
+}
+
+static int westcos_pkcs15init_create_dir(sc_profile_t *profile,
+ sc_card_t *card,
+ sc_file_t *df)
+{
+ int r;
+
+ /* Create the application DF */
+ r = sc_pkcs15init_create_file(profile, card, df);
+ //if(r) return r;
+
+ r = sc_select_file(card, &df->path, NULL);
+ if(r) return r;
+
+ return 0;
+}
+
+#if 0
+/*
+* Create a PIN domain (i.e. a sub-directory holding a user PIN)
+*/
+static int westcos_pkcs15init_create_domain(sc_profile_t *profile,
+ sc_card_t *card,
+ const sc_pkcs15_id_t *id,
+ sc_file_t **ret)
+{
+ return SC_ERROR_NOT_SUPPORTED; //sc_pkcs15_create_pin_domain(profile, card, id, ret);
+}
+#endif /* currently unused */
+
+/*
+ * Select the PIN reference
+ */
+static int westcos_pkcs15_select_pin_reference(sc_profile_t *profile,
+ sc_card_t *card,
+ sc_pkcs15_pin_info_t *pin_info)
+{
+
+ if (pin_info->flags & SC_PKCS15_PIN_FLAG_SO_PIN) {
+ pin_info->reference = 1;
+ } else {
+ pin_info->reference = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Create a new PIN inside a DF
+ */
+static int westcos_pkcs15_create_pin(sc_profile_t *profile,
+ sc_card_t *card, sc_file_t *df,
+ sc_pkcs15_object_t *pin_obj,
+ const u8 *pin, size_t pin_len,
+ const u8 *puk, size_t puk_len)
+{
+ int r;
+ sc_file_t *file = sc_file_new();
+ sc_path_t path;
+
+ if(pin_len>9 || puk_len>9 || pin_len<0 || puk_len<0)
+ return SC_ERROR_INVALID_ARGUMENTS;
+
+ file->type = SC_FILE_TYPE_INTERNAL_EF;
+ file->ef_structure = SC_FILE_EF_TRANSPARENT;
+ file->shareable = 0;
+
+ file->id = 0xAAAA;
+ file->size = 37;
+
+ r = sc_file_add_acl_entry(file, SC_AC_OP_READ, SC_AC_NONE, 0);
+ if(r) return r;
+ r = sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, SC_AC_NONE, 0);
+ if(r) return r;
+ r = sc_file_add_acl_entry(file, SC_AC_OP_ERASE, SC_AC_NONE, 0);
+ if(r) return r;
+
+ r = sc_create_file(card, file);
+ if(r)
+ {
+ if(r != SC_ERROR_FILE_ALREADY_EXISTS)
+ return (r);
+
+ sc_format_path("3F005015AAAA", &path);
+ r = sc_select_file(card, &path, NULL);
+ if(r) return (r);
+ }
+
+ //r = sc_pkcs15init_create_file(profile, card, file);
+
+ if(file)
+ sc_file_free(file);
+
+ if(pin != NULL)
+ {
+ sc_changekey_t ck;
+ struct sc_pin_cmd_pin pin_cmd;
+
+ memset(&pin_cmd, 0, sizeof(pin_cmd));
+ memset(&ck, 0, sizeof(ck));
+
+ memcpy(ck.key_template, "\x1e\x00\x00\x10", 4);
+
+ pin_cmd.encoding = SC_PIN_ENCODING_GLP;
+ pin_cmd.len = pin_len;
+ pin_cmd.data = pin;
+ pin_cmd.max_length = 8;
+
+ ck.new_key.key_len = sc_build_pin(ck.new_key.key_value,
+ sizeof(ck.new_key.key_value), &pin_cmd, 1);
+ if(ck.new_key.key_len<0)
+ return SC_ERROR_CARD_CMD_FAILED;
+
+ r = sc_card_ctl(card, SC_CARDCTL_WESTCOS_CHANGE_KEY, &ck);
+ if(r) return r;
+ }
+
+ if(puk != NULL)
+ {
+ sc_changekey_t ck;
+ struct sc_pin_cmd_pin puk_cmd;
+
+ memset(&puk_cmd, 0, sizeof(puk_cmd));
+ memset(&ck, 0, sizeof(ck));
+
+ memcpy(ck.key_template, "\x1e\x00\x00\x20", 4);
+
+ puk_cmd.encoding = SC_PIN_ENCODING_GLP;
+ puk_cmd.len = puk_len;
+ puk_cmd.data = puk;
+ puk_cmd.max_length = 8;
+
+ ck.new_key.key_len = sc_build_pin(ck.new_key.key_value,
+ sizeof(ck.new_key.key_value), &puk_cmd, 1);
+ if(ck.new_key.key_len<0)
+ return SC_ERROR_CARD_CMD_FAILED;
+
+ r = sc_card_ctl(card, SC_CARDCTL_WESTCOS_CHANGE_KEY, &ck);
+ if(r) return r;
+ }
+
+ return 0;
+}
+
+/*
+ * Create a new key file
+ */
+static int westcos_pkcs15init_create_key(sc_profile_t *profile,
+ sc_card_t *card,
+ sc_pkcs15_object_t *obj)
+{
+ int r;
+ size_t size;
+ sc_file_t *keyfile = NULL;
+ sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data;
+
+ if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) {
+ return SC_ERROR_NOT_SUPPORTED;
+ }
+
+ switch (key_info->modulus_length) {
+ case 128: size = 112; break;
+ case 256: size = 184; break;
+ case 512: size = 336; break;
+ case 768: size = 480; break;
+ case 1024: size = 616; break;
+ case 1536: size = 912; break;
+ case 2048: size = 1200; break;
+ default:
+ r = SC_ERROR_INVALID_ARGUMENTS;
+ goto out;
+ }
+
+ keyfile = sc_file_new();
+ if(keyfile == NULL)
+ return SC_ERROR_OUT_OF_MEMORY;
+
+ keyfile->path = key_info->path;
+
+ keyfile->type = SC_FILE_TYPE_WORKING_EF;
+ keyfile->ef_structure = SC_FILE_EF_TRANSPARENT;
+ keyfile->shareable = 0;
+ keyfile->size = size;
+
+ r = sc_file_add_acl_entry(keyfile, SC_AC_OP_READ, SC_AC_CHV, 0);
+ if(r) goto out;
+ r = sc_file_add_acl_entry(keyfile, SC_AC_OP_UPDATE, SC_AC_CHV, 0);
+ if(r) goto out;
+ r = sc_file_add_acl_entry(keyfile, SC_AC_OP_ERASE, SC_AC_CHV, 0);
+ if(r) goto out;
+
+ r = sc_pkcs15init_create_file(profile, card, keyfile);
+ if(r)
+ {
+ if(r != SC_ERROR_FILE_ALREADY_EXISTS)
+ goto out;
+ r = 0;
+ }
+
+out:
+ if(keyfile)
+ sc_file_free(keyfile);
+
+ return r;
+}
+
+
+
+/*
+ * Store a private key
+ */
+static int westcos_pkcs15init_store_key(sc_profile_t *profile,
+ sc_card_t *card,
+ sc_pkcs15_object_t *obj,
+ sc_pkcs15_prkey_t *key)
+{
+ return SC_ERROR_NOT_SUPPORTED;
+
+#if 0
+ int r;
+ sc_file_t *keyfile;
+ sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data;
+
+ if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) {
+ return SC_ERROR_NOT_SUPPORTED;
+ }
+
+ r = SC_ERROR_NOT_SUPPORTED; //sc_profile_get_file_by_path(profile, &key_info->path, &keyfile);
+ if (r < 0) return r;
+
+ //r = sc_pkcs15init_update_file(profile, card, keyfile, &key->der.data, &key->der.len);
+
+ //sc_file_free(keyfile);
+ return r;
+#endif
+}
+
+/*
+ * Generate key
+ */
+static int westcos_pkcs15init_generate_key(sc_profile_t *profile,
+ sc_card_t *card,
+ sc_pkcs15_object_t *obj,
+ sc_pkcs15_pubkey_t *pubkey)
+{
+ int r = SC_ERROR_UNKNOWN;
+ long lg;
+ char *p;
+ sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data;
+#ifdef ENABLE_OPENSSL
+ RSA *rsa = RSA_new();
+ BIGNUM *bn = BN_new();
+ BIO *mem = BIO_new(BIO_s_mem());
+#endif
+
+#ifndef ENABLE_OPENSSL
+ r = SC_ERROR_NOT_SUPPORTED;
+#else
+ sc_file_t *prkf = NULL;
+
+ if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) {
+ return SC_ERROR_NOT_SUPPORTED;
+ }
+
+ if(/*keyfile == NULL ||*/ rsa == NULL || bn == NULL || mem == NULL)
+ {
+ r = SC_ERROR_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ /* pkcs11 re-route routine cryptage vers la carte fixe default to use openssl */
+ rsa->meth = RSA_PKCS1_SSLeay();
+
+ if(!BN_set_word(bn, RSA_F4) ||
+ !RSA_generate_key_ex(rsa, key_info->modulus_length, bn, NULL))
+ {
+ r = SC_ERROR_UNKNOWN;
+ goto out;
+ }
+
+ if(pubkey != NULL)
+ {
+ if(!i2d_RSAPublicKey_bio(mem, rsa))
+ {
+ r = SC_ERROR_UNKNOWN;
+ goto out;
+ }
+
+ lg = BIO_get_mem_data(mem, &p);
+
+ pubkey->algorithm = SC_ALGORITHM_RSA;
+
+ r = sc_pkcs15_decode_pubkey(card->ctx, pubkey, p, lg);
+ }
+
+ BIO_reset(mem);
+
+ if(!i2d_RSAPrivateKey_bio(mem, rsa))
+ {
+ r = SC_ERROR_UNKNOWN;
+ goto out;
+ }
+
+ lg = BIO_get_mem_data(mem, &p);
+
+ /* Get the private key file */
+ r = sc_profile_get_file_by_path(profile, &key_info->path, &prkf);
+ if (r < 0)
+ {
+ char pbuf[SC_MAX_PATH_STRING_SIZE];
+
+ r = sc_path_print(pbuf, sizeof(pbuf), &key_info->path);
+ if (r != SC_SUCCESS)
+ pbuf[0] = '\0';
+
+ return r;
+ }
+
+ r = sc_pkcs15init_update_file(profile, card, prkf, p, lg);
+ if(r) goto out;
+
+out:
+ if(mem)
+ BIO_free(mem);
+ if(bn)
+ BN_free(bn);
+ if(rsa)
+ RSA_free(rsa);
+ if(prkf)
+ sc_file_free(prkf);
+#endif
+
+ return r;
+}
+
+static int westcos_pkcs15init_finalize_card(sc_card_t *card)
+{
+ int r;
+
+ /* be sure authentificate card */
+ r = sc_card_ctl(card, SC_CARDCTL_WESTCOS_AUT_KEY, NULL);
+ if(r) return (r);
+
+ return sc_pkcs15init_set_lifecycle(card, SC_CARDCTRL_LIFECYCLE_USER);
+}
+
+static struct sc_pkcs15init_operations sc_pkcs15init_westcos_operations = {
+ NULL, /* erase_card */
+ westcos_pkcs15init_init_card, /* init_card */
+ westcos_pkcs15init_create_dir, /* create_dir */
+ NULL, /* create_domain */
+ westcos_pkcs15_select_pin_reference,/* select_pin_reference */
+ westcos_pkcs15_create_pin, /* create_pin */
+ NULL, /* select_key_reference */
+ westcos_pkcs15init_create_key, /* create_key */
+ westcos_pkcs15init_store_key, /* store_key */
+ westcos_pkcs15init_generate_key, /* generate_key */
+ NULL, NULL, /* encode private/public key */
+ westcos_pkcs15init_finalize_card, /* finalize_card */
+ NULL,NULL,NULL,NULL, /* old style app */
+ NULL, /* old_generate_key */
+ NULL /* delete_object */
+};
+
+struct sc_pkcs15init_operations* sc_pkcs15init_get_westcos_ops(void)
+{
+ return &sc_pkcs15init_westcos_operations;
+}
+
+
+
diff --git a/src/pkcs15init/westcos.profile b/src/pkcs15init/westcos.profile
new file mode 100644
index 00000000..957912fd
--- /dev/null
+++ b/src/pkcs15init/westcos.profile
@@ -0,0 +1,183 @@
+
+cardinfo {
+ label = "westcos";
+ manufacturer = "CEV";
+
+ max-pin-length = 8;
+ min-pin-length = 4;
+ pin-encoding = BCD;
+ pin-pad-char = 0xff;
+
+}
+
+# Default settings.
+# This option block will always be processed.
+option default {
+ macros {
+ protected = *=$PIN, READ=NONE;
+ unprotected = *=NONE;
+ private = *=$PIN;
+ so-pin-flags = local, initialized, needs-padding; #, soPin;
+ so-min-pin-length = 6;
+ so-pin-attempts = 2;
+ so-auth-id = 1; #FF;
+ so-puk-attempts = 4;
+ so-min-puk-length = 6;
+ unusedspace-size = 128;
+ odf-size = 256;
+ aodf-size = 256;
+ cdf-size = 512;
+ prkdf-size = 256;
+ pukdf-size = 256;
+ dodf-size = 256;
+ }
+}
+
+PIN so-pin {
+ auth-id = 1;
+ reference = 1;
+ attempts = 3;
+ min-length = 4;
+ max-length = 8;
+ flags = local, initialized, needs-padding;
+}
+PIN so-puk {
+ auth-id = 2;
+ reference = 2;
+ attempts = 10;
+ min-length = 4;
+ max-length = 8;
+ flags = local, initialized, needs-padding;
+}
+PIN user-pin {
+ auth-id = 1;
+ reference = 1;
+ attempts = 3;
+ min-length = 4;
+ max-length = 8;
+ flags = local, initialized, needs-padding;
+}
+PIN user-puk {
+ auth-id = 2;
+ reference = 2;
+ attempts = 10;
+ min-length = 4;
+ max-length = 8;
+ flags = local, initialized, needs-padding;
+}
+
+filesystem {
+ DF MF {
+ path = 3F00;
+ type = DF;
+
+ # This is the DIR file
+ EF DIR {
+ type = EF;
+ file-id = 2F00;
+ size = 128;
+ acl = $unprotected;
+ }
+
+ # 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 = $unprotected;
+ size = 5000;
+
+ EF PKCS15-ODF {
+ file-id = 5031;
+ size = $odf-size;
+ acl = $unprotected;
+ }
+
+ EF PKCS15-TokenInfo {
+ file-id = 5032;
+ acl = $unprotected;
+ }
+
+ 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;
+ }
+
+
+ # This template defines files for keys, certificates etc.
+ #
+ # When instantiating the template, each file id will be
+ # combined with the last octet of the object's pkcs15 id
+ # to form a unique file ID.
+ template key-domain {
+ EF private-key {
+ file-id = 0100;
+ structure = transparent;
+ acl = *=NEVER, READ=$PIN, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN;
+ }
+
+ EF public-key {
+ file-id = 0200;
+ structure = transparent;
+ acl = *=NEVER, READ=NONE, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN;
+ }
+
+ # Certificate template
+ EF certificate {
+ file-id = 0300;
+ structure = transparent;
+ acl = *=NEVER, READ=NONE, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN;
+ }
+
+ # data objects are stored in transparent EFs.
+ EF data {
+ file-id = 0400;
+ structure = transparent;
+ acl = *=NEVER, READ=NONE, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN;
+ }
+
+ # private data objects are stored in transparent EFs.
+ EF privdata {
+ file-id = 0500;
+ structure = transparent;
+ acl = *=NEVER, READ=$PIN, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN;
+ }
+ }
+
+ }
+ }
+}
+
+
+
diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am
index cac0e06c..20af2088 100644
--- a/src/tools/Makefile.am
+++ b/src/tools/Makefile.am
@@ -9,7 +9,7 @@ noinst_HEADERS = util.h
bin_PROGRAMS = opensc-tool opensc-explorer pkcs15-tool pkcs15-crypt \
pkcs11-tool cardos-tool eidenv rutoken-tool
if ENABLE_OPENSSL
-bin_PROGRAMS += cryptoflex-tool pkcs15-init netkey-tool piv-tool
+bin_PROGRAMS += cryptoflex-tool pkcs15-init netkey-tool piv-tool westcos-tool
endif
dist_bin_SCRIPTS = cardos-info
if WIN32
@@ -44,6 +44,8 @@ netkey_tool_SOURCES = netkey-tool.c
netkey_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS)
rutoken_tool_SOURCES = rutoken-tool.c util.c
rutoken_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS)
+westcos_tool_SOURCES = westcos-tool.c
+westcos_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS)
if WIN32
opensc_tool_SOURCES += versioninfo.rc
@@ -58,6 +60,7 @@ cardos_tool_SOURCES += versioninfo.rc
eidenv_SOURCES += versioninfo.rc
netkey_tool_SOURCES += versioninfo.rc
rutoken_tool_SOURCES += versioninfo.rc
+westcos_tool_SOURCES += versioninfo.rc
else
dist_noinst_DATA = versioninfo.rc
endif
diff --git a/src/tools/Makefile.mak b/src/tools/Makefile.mak
index ef2c8c66..e4325874 100644
--- a/src/tools/Makefile.mak
+++ b/src/tools/Makefile.mak
@@ -5,7 +5,7 @@ TOPDIR = ..\..
TARGETS = opensc-tool.exe opensc-explorer.exe pkcs15-tool.exe pkcs15-crypt.exe \
pkcs11-tool.exe cardos-info.exe eidenv.exe rutoken-tool.exe \
- netkey-tool.exe \
+ netkey-tool.exe westcos-tool.exe \
$(PROGRAMS_OPENSSL)
all: $(TARGETS)
diff --git a/src/tools/westcos-tool.c b/src/tools/westcos-tool.c
new file mode 100644
index 00000000..3ddc2471
--- /dev/null
+++ b/src/tools/westcos-tool.c
@@ -0,0 +1,942 @@
+/*
+ * westcos-tool.exe: tool for westcos card
+ *
+ * Copyright (C) 2009 francois.leblanc@cev-sa.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
+#endif
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+static char *version ="0.0.6";
+
+static char *nom_card = "WESTCOS";
+
+static int finalise = 0;
+static int verbose = 0;
+static int install_pin = 0;
+static int remplace = 0;
+
+static char *pin = NULL;
+static char *puk = NULL;
+static char *cert = NULL;
+
+static int keylen = 0;
+
+static int no_lecteur = -1;
+
+static int new_pin = 0;
+static int debloque = 0;
+
+static char *get_filename = NULL;
+static char *get_path = NULL;
+static char *put_filename = NULL;
+static char *put_path = NULL;
+
+static int do_convert_bignum(sc_pkcs15_bignum_t *dst, BIGNUM *src)
+{
+ if (src == 0) return 0;
+ dst->len = BN_num_bytes(src);
+ dst->data = (u8 *) malloc(dst->len);
+ BN_bn2bin(src, dst->data);
+ return 1;
+}
+
+static int charge = 0;
+static void print_openssl_erreur(void)
+{
+ long r;
+
+ if (!charge)
+ {
+ ERR_load_crypto_strings();
+ charge = 1;
+ }
+
+ while ((r = ERR_get_error()) != 0)
+ fprintf(stderr, "%s\n", ERR_error_string(r, NULL));
+}
+
+static verify_pin(sc_card_t *card, int pin_reference, char *pin_value)
+{
+ int r, tries_left = -1;
+ struct sc_pin_cmd_data data;
+
+ memset(&data, 0, sizeof(data));
+ data.cmd = SC_PIN_CMD_VERIFY;
+
+ data.pin_type = SC_AC_CHV;
+
+ data.pin_reference = pin_reference;
+
+ data.flags = SC_PIN_CMD_NEED_PADDING;
+
+ if (card->slot->capabilities & SC_SLOT_CAP_PIN_PAD)
+ {
+ printf("Please enter PIN on the reader's pin pad.\n");
+ data.pin1.prompt = "Please enter PIN";
+ data.flags |= SC_PIN_CMD_USE_PINPAD;
+ }
+ else
+ {
+ if(pin_value == NULL)
+ {
+ return SC_ERROR_INVALID_ARGUMENTS;
+ }
+
+ data.pin1.data = pin_value;
+ data.pin1.len = strlen(pin_value);
+ }
+
+ r = sc_pin_cmd(card, &data, &tries_left);
+
+ if (r)
+ {
+ if (r == SC_ERROR_PIN_CODE_INCORRECT)
+ {
+ if (tries_left >= 0)
+ printf("Error %d attemps left.\n", tries_left);
+ else
+ printf("Wrong pin.\n");
+ }
+ else
+ fprintf(stderr, "The pin can be verify: %s\n", sc_strerror(r));
+ return -1;
+ }
+ printf("Pin correct.\n");
+ return 0;
+}
+
+static change_pin(sc_card_t *card,
+ int pin_reference,
+ char *pin_value1,
+ char *pin_value2)
+{
+ int r, tries_left = -1;
+ struct sc_pin_cmd_data data;
+
+ memset(&data, 0, sizeof(data));
+ data.cmd = SC_PIN_CMD_CHANGE;
+
+ data.pin_type = SC_AC_CHV;
+
+ data.pin_reference = pin_reference;
+
+ data.flags = SC_PIN_CMD_NEED_PADDING;
+
+ if (card->slot->capabilities & SC_SLOT_CAP_PIN_PAD)
+ {
+ printf("Please enter PIN on the reader's pin pad.\n");
+ data.pin1.prompt = "Please enter PIN";
+ data.flags |= SC_PIN_CMD_USE_PINPAD;
+ }
+ else
+ {
+ if(pin_value1 == NULL || pin_value2 == NULL)
+ {
+ return SC_ERROR_INVALID_ARGUMENTS;
+ }
+
+ data.pin1.data = pin_value1;
+ data.pin1.len = strlen(pin_value1);
+
+ data.pin2.data = pin_value2;
+ data.pin2.len = strlen(pin_value2);
+
+ }
+
+ r = sc_pin_cmd(card, &data, &tries_left);
+
+ if (r)
+ {
+ if (r == SC_ERROR_PIN_CODE_INCORRECT)
+ {
+ if (tries_left >= 0)
+ printf("Error %d attemps left.\n", tries_left);
+ else
+ printf("Wrong pin.\n");
+ }
+ else
+ fprintf(stderr, "Can't change pin: %s\n",
+ sc_strerror(r));
+ return -1;
+ }
+ printf("Pin changed.\n");
+ return 0;
+}
+
+static debloque_pin(sc_card_t *card,
+ int pin_reference,
+ char *puk_value,
+ char *pin_value)
+{
+ int r, tries_left = -1;
+ struct sc_pin_cmd_data data;
+
+ memset(&data, 0, sizeof(data));
+ data.cmd = SC_PIN_CMD_UNBLOCK;
+
+ data.pin_type = SC_AC_CHV;
+
+ data.pin_reference = pin_reference;
+
+ data.flags = SC_PIN_CMD_NEED_PADDING;
+
+ if (card->slot->capabilities & SC_SLOT_CAP_PIN_PAD)
+ {
+ printf("Please enter PIN on the reader's pin pad.\n");
+ data.pin1.prompt = "Please enter PIN";
+ data.flags |= SC_PIN_CMD_USE_PINPAD;
+ }
+ else
+ {
+ if(pin == NULL || puk == NULL)
+ {
+ return SC_ERROR_INVALID_ARGUMENTS;
+ }
+
+ data.pin1.data = puk_value;
+ data.pin1.len = strlen(puk_value);
+
+ data.pin2.data = pin_value;
+ data.pin2.len = strlen(pin_value);
+
+ }
+
+ r = sc_pin_cmd(card, &data, &tries_left);
+
+ if (r)
+ {
+ if (r == SC_ERROR_PIN_CODE_INCORRECT)
+ {
+ if (tries_left >= 0)
+ printf("Error %d attemps left.\n", tries_left);
+ else
+ printf("Wrong pin.\n");
+ }
+ else
+ fprintf(stderr, "Can't unblock pin: %s\n",
+ sc_strerror(r));
+ return -1;
+ }
+ printf("Code debloque.\n");
+ return 0;
+}
+
+static int cert2der(X509 *cert, u8 **value)
+{
+ int len;
+ u8 *p;
+ len = i2d_X509(cert, NULL);
+ p = *value = (u8*)malloc(len);
+ i2d_X509(cert, &p);
+ return len;
+}
+
+static int creation_fichier_cert(sc_card_t *card)
+{
+ int r;
+ int size;
+ sc_path_t path;
+ sc_file_t *file = NULL;
+ sc_context_t *ctx = card->ctx;
+
+ sc_format_path("3F00", &path);
+ r = sc_select_file(card, &path, &file);
+ if(r) goto out;
+
+ size = (file->size) - 32;
+
+ if(file)
+ {
+ sc_file_free(file);
+ file = NULL;
+ }
+
+ sc_format_path("0002", &path);
+ sc_ctx_suppress_errors_on(ctx);
+ r = sc_select_file(card, &path, NULL);
+ sc_ctx_suppress_errors_off(ctx);
+ if(r)
+ {
+ if(r != SC_ERROR_FILE_NOT_FOUND) goto out;
+
+ file = sc_file_new();
+ if(file == NULL)
+ {
+ fprintf(stderr, "memory error.\n");
+ goto out;
+ }
+
+ file->type = SC_FILE_TYPE_WORKING_EF;
+ file->ef_structure = SC_FILE_EF_TRANSPARENT;
+ file->shareable = 0;
+
+ file->size = size;
+
+ r = sc_file_add_acl_entry(file, SC_AC_OP_READ, SC_AC_NONE, 0);
+ if(r) goto out;
+ r = sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, SC_AC_CHV, 0);
+ if(r) goto out;
+ r = sc_file_add_acl_entry(file, SC_AC_OP_ERASE, SC_AC_CHV, 0);
+ if(r) goto out;
+
+ file->path = path;
+ r = sc_create_file(card, file);
+ if(r) goto out;
+ }
+
+out:
+ if(file)
+ sc_file_free(file);
+
+ return r;
+}
+
+void usage(void)
+{
+printf("Tools for westcos card.\n");
+printf("version %s.\n\n", version);
+printf("\t -G Generate key 1536 default.\n");
+printf("\t -L [length] Key length 512,1024,1536.\n");
+printf("\t -i Install pin.\n");
+printf("\t -pin [value] Pin.\n");
+printf("\t -puk [value] Puk.\n");
+printf("\t -n Change pin (new pin in puk option).\n");
+printf("\t -u Unblock pin.\n");
+printf("\t -cert [file] Write certificate (in pem format).\n");
+printf("\t -F Finalize card "\
+ "(!!! MANDATORY FOR SECURITY !!!).\n");
+printf("\t -r [n] Use reader number [n]"\
+ " (default: autodetect).\n");
+printf("\t -gf [path] Read file [path].\n");
+printf("\t -pf [path] Write file [path].\n");
+printf("\t -v verbose.\n");
+printf("\t -h This message.\n");
+exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+ int r;
+ int i = 1;
+ u8 *p;
+ int card_presente = 0;
+ sc_context_param_t ctx_param;
+ sc_reader_t *lecteur = NULL;
+ sc_card_t *card = NULL;
+ sc_context_t *ctx = NULL;
+ sc_file_t *file = NULL;
+ sc_path_t path;
+ RSA *rsa = RSA_new();
+ BIGNUM *bn = BN_new();
+ BIO *mem = BIO_new(BIO_s_mem());
+
+ if(rsa == NULL || bn == NULL || mem == NULL)
+ {
+ fprintf(stderr,"Not enougth memory.\n");
+ goto out;
+ }
+
+ while(i 1)
+ ctx->debug = verbose-1;
+
+ if(no_lecteur == -1)
+ {
+ for(i = 0; i=0)
+ {
+ printf("card->name = %s\n", card->name);
+ if(strncmp(card->name, nom_card, strlen(nom_card)) == 0)
+ {
+ card_presente = 1;
+ break;
+ }
+ sc_disconnect_card(card,0);
+ card = NULL;
+ }
+ }
+ }
+ }
+ else
+ {
+ if(no_lecteur < sc_ctx_get_reader_count(ctx))
+ {
+ lecteur = sc_ctx_get_reader(ctx, no_lecteur);
+ r = sc_connect_card(lecteur, 0, &card);
+ if(r>=0)
+ {
+ card_presente = 1;
+ }
+ else
+ {
+ sc_disconnect_card(card,0);
+ }
+ }
+ }
+
+ if(!card_presente) goto out;
+
+ sc_lock(card);
+
+ sc_format_path("3F00", &path);
+ r = sc_select_file(card, &path, NULL);
+ if(r) goto out;
+
+ if(install_pin)
+ {
+ sc_format_path("AAAA", &path);
+ sc_ctx_suppress_errors_on(ctx);
+ r = sc_select_file(card, &path, NULL);
+ sc_ctx_suppress_errors_off(ctx);
+ if(r)
+ {
+ if(r != SC_ERROR_FILE_NOT_FOUND) goto out;
+
+ file = sc_file_new();
+ if(file == NULL)
+ {
+ fprintf(stderr, "Not enougth memory.\n");
+ goto out;
+ }
+
+ file->type = SC_FILE_TYPE_INTERNAL_EF;
+ file->ef_structure = SC_FILE_EF_TRANSPARENT;
+ file->shareable = 0;
+
+ file->id = 0xAAAA;
+ file->size = 37;
+
+ r = sc_file_add_acl_entry(file, SC_AC_OP_READ, SC_AC_NONE, 0);
+ if(r) goto out;
+ r = sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, SC_AC_NONE, 0);
+ if(r) goto out;
+ r = sc_file_add_acl_entry(file, SC_AC_OP_ERASE, SC_AC_NONE, 0);
+ if(r) goto out;
+
+ //sc_format_path("3F00AAAA", &(file->path));
+ file->path = path;
+ r = sc_create_file(card, file);
+ if(r) goto out;
+ }
+
+ if(pin != NULL)
+ {
+ sc_changekey_t ck;
+ struct sc_pin_cmd_pin pin_cmd;
+
+ memset(&pin_cmd, 0, sizeof(pin_cmd));
+ memset(&ck, 0, sizeof(ck));
+
+ memcpy(ck.key_template, "\x1e\x00\x00\x10", 4);
+
+ pin_cmd.encoding = SC_PIN_ENCODING_GLP;
+ pin_cmd.len = strlen(pin);
+ pin_cmd.data = pin;
+ pin_cmd.max_length = 8;
+
+ ck.new_key.key_len = sc_build_pin(ck.new_key.key_value,
+ sizeof(ck.new_key.key_value), &pin_cmd, 1);
+ if(ck.new_key.key_len<0)
+ goto out;
+
+ r = sc_card_ctl(card, SC_CARDCTL_WESTCOS_CHANGE_KEY, &ck);
+ if(r) goto out;
+ }
+
+ if(puk != NULL)
+ {
+ sc_changekey_t ck;
+ struct sc_pin_cmd_pin puk_cmd;
+
+ memset(&puk_cmd, 0, sizeof(puk_cmd));
+ memset(&ck, 0, sizeof(ck));
+
+ memcpy(ck.key_template, "\x1e\x00\x00\x20", 4);
+
+ puk_cmd.encoding = SC_PIN_ENCODING_GLP;
+ puk_cmd.len = strlen(puk);
+ puk_cmd.data = puk;
+ puk_cmd.max_length = 8;
+
+ ck.new_key.key_len = sc_build_pin(ck.new_key.key_value,
+ sizeof(ck.new_key.key_value), &puk_cmd, 1);
+ if(ck.new_key.key_len<0)
+ goto out;
+
+ r = sc_card_ctl(card, SC_CARDCTL_WESTCOS_CHANGE_KEY, &ck);
+ if(r) goto out;
+ }
+ }
+
+ if(new_pin)
+ {
+ if(change_pin(card, 0, pin, puk))
+ printf("Wrong pin.\n");
+ goto out;
+ }
+
+ if(debloque)
+ {
+ if(debloque_pin(card, 0, puk, pin))
+ printf("Error unblocking pin.\n");
+ goto out;
+ }
+
+ printf("verify pin.\n");
+ {
+ if(verify_pin(card, 0, pin))
+ {
+ printf("Wrong pin.\n");
+ goto out;
+ }
+ }
+
+ if(keylen)
+ {
+ int lg;
+ struct sc_pkcs15_pubkey key;
+ struct sc_pkcs15_pubkey_rsa *dst = &(key.u.rsa);
+
+ memset(&key, 0, sizeof(key));
+ key.algorithm = SC_ALGORITHM_RSA;
+
+ printf("Generate key of length %d.\n", keylen);
+
+ if(!BN_set_word(bn, RSA_F4) ||
+ !RSA_generate_key_ex(rsa, keylen, bn, NULL))
+ {
+ fprintf(stderr,
+ "RSA_generate_key_ex return %d\n", ERR_get_error());
+ goto out;
+ }
+
+ rsa->meth = RSA_PKCS1_SSLeay();
+
+ if(!i2d_RSAPrivateKey_bio(mem, rsa))
+ {
+ fprintf(stderr,
+ "i2d_RSAPrivateKey_bio return %d\n", ERR_get_error());
+ goto out;
+ }
+
+ lg = BIO_get_mem_data(mem, &p);
+
+ sc_format_path("0001", &path);
+ sc_ctx_suppress_errors_on(ctx);
+ r = sc_select_file(card, &path, NULL);
+ sc_ctx_suppress_errors_off(ctx);
+ if(r)
+ {
+ if(r != SC_ERROR_FILE_NOT_FOUND) goto out;
+
+ file = sc_file_new();
+ if(file == NULL)
+ {
+ fprintf(stderr, "Not enougth memory.\n");
+ goto out;
+ }
+
+ file->type = SC_FILE_TYPE_WORKING_EF;
+ file->ef_structure = SC_FILE_EF_TRANSPARENT;
+ file->shareable = 0;
+
+ file->size = ((lg/4)+1)*4;
+
+ r = sc_file_add_acl_entry(file, SC_AC_OP_READ, SC_AC_CHV, 0);
+ if(r) goto out;
+ r = sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, SC_AC_CHV, 0);
+ if(r) goto out;
+ r = sc_file_add_acl_entry(file, SC_AC_OP_ERASE, SC_AC_CHV, 0);
+ if(r) goto out;
+
+ file->path = path;
+
+ printf("File key creation %s, size %d.\n", file->path.value,
+ file->size);
+
+ r = sc_create_file(card, file);
+ if(r) goto out;
+ }
+ else
+ {
+ if(!remplace)
+ {
+ fprintf(stderr,
+ "Key file already exist,"\
+ " use -R to replace it.\n");
+ goto out;
+ }
+ }
+
+ printf("Private key length is %d\n", lg);
+
+ printf("Write private key.\n");
+ r = sc_update_binary(card,0,p,lg,0);
+ if(r<0) goto out;
+ printf("Private key correctly written.\n");
+
+ r = creation_fichier_cert(card);
+ if(r) goto out;
+
+ if (!do_convert_bignum(&dst->modulus, rsa->n)
+ || !do_convert_bignum(&dst->exponent, rsa->e))
+ goto out;
+
+ r = sc_pkcs15_encode_pubkey(ctx, &key, &p, &lg);
+ if(r) goto out;
+
+ printf("Public key length %d\n", lg);
+
+ sc_format_path("3F000002", &path);
+ r = sc_select_file(card, &path, NULL);
+ if(r) goto out;
+
+ printf("Write public key.\n");
+ r = sc_update_binary(card,0,p,lg,0);
+ if(r<0) goto out;
+ printf("Public key correctly written.\n");
+
+ }
+
+ if(cert)
+ {
+ BIO *bio;
+ X509 *xp;
+
+ bio = BIO_new(BIO_s_file());
+ if (BIO_read_filename(bio, cert) <= 0)
+ {
+ BIO_free(bio);
+ fprintf(stderr, "Can't open file %s.\n", cert);
+ goto out;
+ }
+ xp = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+ BIO_free(bio);
+ if (xp == NULL)
+ {
+ print_openssl_erreur();
+ goto out;
+ }
+ else
+ {
+ int lg = cert2der(xp, &p);
+
+ sc_format_path("0002", &path);
+ r = sc_select_file(card, &path, NULL);
+ if(r) goto out;
+
+ /* FIXME: verifier taille fichier compatible... */
+ printf("Write certificate %s.\n", cert);
+
+ r = sc_update_binary(card,0,p,lg,0);
+ if(r<0)
+ {
+ if(p) free(p);
+ goto out;
+ }
+ if(xp) X509_free(xp);
+ if(p) free(p);
+
+ printf("Certificate correctly written.\n");
+ }
+ }
+
+ if(finalise)
+ {
+ int mode = SC_CARDCTRL_LIFECYCLE_USER;
+
+ if(card->atr[10] != 0x82)
+ {
+ sc_format_path("0001", &path);
+ r = sc_select_file(card, &path, NULL);
+ if(r)
+ {
+ printf("This card don't have private key"\
+ " and can't be finalize.\n");
+ goto out;
+ }
+ printf("Finalize card...\n");
+ if(sc_card_ctl(card, SC_CARDCTL_WESTCOS_AUT_KEY, NULL) ||
+ sc_card_ctl(card, SC_CARDCTL_LIFECYCLE_SET, &mode))
+ {
+ printf("Error finalizing card,"\
+ " card isn't secure.\n");
+ goto out;
+ }
+ }
+ printf("Card correctly finalized.\n");
+ }
+
+ if(get_filename)
+ {
+ FILE *fp;
+ u8 *b;
+
+ if(file)
+ {
+ sc_file_free(file);
+ file = NULL;
+ }
+
+ sc_format_path(get_filename, &path);
+ r = sc_select_file(card, &path, &file);
+ if(r)
+ {
+ printf("Error file not found.\n");
+ goto out;
+ }
+
+ b = (u8*)malloc(file->size);
+ if(b == NULL)
+ {
+ fprintf(stderr, "Not enougth memory.\n");
+ goto out;
+ }
+
+ r = sc_read_binary(card, 0, b, file->size, 0);
+ if(r<0)
+ {
+ printf("Error reading file.\n");
+ goto out;
+ }
+
+ fp = fopen(get_filename, "wb");
+ fwrite(b, 1, file->size, fp);
+ fclose(fp);
+
+ free(b);
+ }
+
+ if(put_filename)
+ {
+ FILE *fp;
+ u8 *b;
+
+ if(file)
+ {
+ sc_file_free(file);
+ file = NULL;
+ }
+
+ sc_format_path(put_filename, &path);
+ r = sc_select_file(card, &path, &file);
+ if(r)
+ {
+ printf("File not found.\n");
+ goto out;
+ }
+
+ b = (u8*)malloc(file->size);
+ if(b == NULL)
+ {
+ fprintf(stderr, "Not enougth memory.\n");
+ goto out;
+ }
+
+ memset(b, 0, file->size);
+
+ fp = fopen(put_filename, "rb");
+ fread(b, 1, file->size, fp);
+ fclose(fp);
+
+ r = sc_update_binary(card, 0, b, file->size, 0);
+ if(r<0)
+ {
+ free(b);
+ printf("Error writing file.\n");
+ goto out;
+ }
+
+ free(b);
+ }
+
+out:
+
+ if(mem)
+ BIO_free(mem);
+ if(bn)
+ BN_free(bn);
+ if(rsa)
+ RSA_free(rsa);
+
+ if(file)
+ sc_file_free(file);
+
+ if (card)
+ {
+ sc_unlock(card);
+ sc_disconnect_card(card, 0);
+ }
+
+ if (ctx)
+ sc_release_context(ctx);
+
+}
+