2002-04-10 23:10:47 +00:00
|
|
|
/*
|
2012-12-02 18:21:08 +00:00
|
|
|
* card-cardos.c: Support for CardOS (from Siemens or Atos) based cards and
|
|
|
|
* tokens (for example Aladdin eToken PRO, Eutron CryptoIdentity IT-SEC)
|
2002-04-10 23:10:47 +00:00
|
|
|
*
|
2005-12-28 19:38:55 +00:00
|
|
|
* Copyright (c) 2005 Nils Larsch <nils@larsch.net>
|
2002-04-10 23:10:47 +00:00
|
|
|
* Copyright (C) 2002 Andreas Jellinghaus <aj@dungeon.inka.de>
|
2006-12-19 21:32:31 +00:00
|
|
|
* Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
|
2002-04-10 23:10:47 +00:00
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
2015-04-22 21:55:33 +00:00
|
|
|
#if HAVE_CONFIG_H
|
2010-03-04 08:14:36 +00:00
|
|
|
#include "config.h"
|
2015-04-22 21:55:33 +00:00
|
|
|
#endif
|
2010-03-04 08:14:36 +00:00
|
|
|
|
2002-04-10 23:10:47 +00:00
|
|
|
#include <ctype.h>
|
2002-04-19 14:23:31 +00:00
|
|
|
#include <string.h>
|
2002-04-10 23:10:47 +00:00
|
|
|
|
2010-03-04 08:14:36 +00:00
|
|
|
#include "internal.h"
|
|
|
|
#include "asn1.h"
|
|
|
|
#include "cardctl.h"
|
2005-12-28 19:38:55 +00:00
|
|
|
|
2002-04-17 13:36:35 +00:00
|
|
|
static const struct sc_card_operations *iso_ops = NULL;
|
|
|
|
|
2007-06-21 10:07:01 +00:00
|
|
|
static struct sc_card_operations cardos_ops;
|
2006-01-23 21:43:01 +00:00
|
|
|
static struct sc_card_driver cardos_drv = {
|
2003-04-03 09:53:06 +00:00
|
|
|
"Siemens CardOS",
|
2006-01-23 21:43:01 +00:00
|
|
|
"cardos",
|
|
|
|
&cardos_ops,
|
2005-09-07 08:33:55 +00:00
|
|
|
NULL, 0, NULL
|
2002-04-10 23:10:47 +00:00
|
|
|
};
|
|
|
|
|
2006-01-23 21:43:01 +00:00
|
|
|
static struct sc_atr_table cardos_atrs[] = {
|
2005-02-04 20:29:35 +00:00
|
|
|
/* 4.0 */
|
2006-01-23 21:43:01 +00:00
|
|
|
{ "3b:e2:00:ff:c1:10:31:fe:55:c8:02:9c", NULL, NULL, SC_CARD_TYPE_CARDOS_GENERIC, 0, NULL },
|
2005-02-13 18:24:19 +00:00
|
|
|
/* Italian eID card, postecert */
|
2009-10-03 10:13:45 +00:00
|
|
|
{ "3b:e9:00:ff:c1:10:31:fe:55:00:64:05:00:c8:02:31:80:00:47", NULL, NULL, SC_CARD_TYPE_CARDOS_CIE_V1, 0, NULL },
|
2005-02-13 18:24:19 +00:00
|
|
|
/* Italian eID card, infocamere */
|
2006-01-23 21:43:01 +00:00
|
|
|
{ "3b:fb:98:00:ff:c1:10:31:fe:55:00:64:05:20:47:03:31:80:00:90:00:f3", NULL, NULL, SC_CARD_TYPE_CARDOS_GENERIC, 0, NULL },
|
2005-04-16 13:36:30 +00:00
|
|
|
/* Another Italian InfocamereCard */
|
2006-01-23 21:43:01 +00:00
|
|
|
{ "3b:fc:98:00:ff:c1:10:31:fe:55:c8:03:49:6e:66:6f:63:61:6d:65:72:65:28", NULL, NULL, SC_CARD_TYPE_CARDOS_GENERIC, 0, NULL },
|
|
|
|
{ "3b:f4:98:00:ff:c1:10:31:fe:55:4d:34:63:76:b4", NULL, NULL, SC_CARD_TYPE_CARDOS_GENERIC, 0, NULL},
|
2007-06-25 18:01:26 +00:00
|
|
|
/* cardos m4.2 and above */
|
2007-07-07 11:29:12 +00:00
|
|
|
{ "3b:f2:18:00:ff:c1:0a:31:fe:55:c8:06:8a", "ff:ff:0f:ff:00:ff:00:ff:ff:00:00:00:00", NULL, SC_CARD_TYPE_CARDOS_M4_2, 0, NULL },
|
2010-04-21 14:38:23 +00:00
|
|
|
/* CardOS 4.4 */
|
|
|
|
{ "3b:d2:18:02:c1:0a:31:fe:58:c8:0d:51", NULL, NULL, SC_CARD_TYPE_CARDOS_M4_4, 0, NULL},
|
2012-12-02 18:21:08 +00:00
|
|
|
/* CardOS v5.0 */
|
|
|
|
{ "3b:d2:18:00:81:31:fe:58:c9:01:14", NULL, NULL, SC_CARD_TYPE_CARDOS_V5_0, 0, NULL},
|
2005-09-07 08:33:55 +00:00
|
|
|
{ NULL, NULL, NULL, 0, 0, NULL }
|
2003-01-20 11:40:38 +00:00
|
|
|
};
|
2002-04-10 23:10:47 +00:00
|
|
|
|
2012-11-09 09:57:37 +00:00
|
|
|
static unsigned int algorithm_ids_in_tokeninfo[SC_MAX_SUPPORTED_ALGORITHMS];
|
|
|
|
static unsigned int algorithm_ids_in_tokeninfo_count=0;
|
|
|
|
|
2006-01-23 21:43:01 +00:00
|
|
|
static int cardos_match_card(sc_card_t *card)
|
2002-04-10 23:10:47 +00:00
|
|
|
{
|
2011-01-07 17:18:58 +00:00
|
|
|
unsigned char atr[SC_MAX_ATR_SIZE];
|
2003-01-20 11:40:38 +00:00
|
|
|
int i;
|
|
|
|
|
2006-01-23 21:43:01 +00:00
|
|
|
i = _sc_match_atr(card, cardos_atrs, &card->type);
|
2005-02-04 20:29:35 +00:00
|
|
|
if (i < 0)
|
|
|
|
return 0;
|
2011-01-07 17:18:58 +00:00
|
|
|
|
|
|
|
memcpy(atr, card->atr.value, sizeof(atr));
|
|
|
|
|
2009-10-03 10:13:45 +00:00
|
|
|
/* Do not change card type for CIE! */
|
|
|
|
if (card->type == SC_CARD_TYPE_CARDOS_CIE_V1)
|
|
|
|
return 1;
|
2010-04-21 14:38:23 +00:00
|
|
|
if (card->type == SC_CARD_TYPE_CARDOS_M4_4)
|
|
|
|
return 1;
|
2012-12-02 18:21:08 +00:00
|
|
|
if (card->type == SC_CARD_TYPE_CARDOS_V5_0)
|
|
|
|
return 1;
|
2007-06-25 18:01:26 +00:00
|
|
|
if (card->type == SC_CARD_TYPE_CARDOS_M4_2) {
|
|
|
|
int rv;
|
|
|
|
sc_apdu_t apdu;
|
|
|
|
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
|
|
|
|
/* first check some additional ATR bytes */
|
2011-01-07 17:18:58 +00:00
|
|
|
if ((atr[4] != 0xff && atr[4] != 0x02) ||
|
|
|
|
(atr[6] != 0x10 && atr[6] != 0x0a) ||
|
|
|
|
(atr[9] != 0x55 && atr[9] != 0x58))
|
2007-06-25 18:01:26 +00:00
|
|
|
return 0;
|
|
|
|
/* get the os version using GET DATA and compare it with
|
|
|
|
* version in the ATR */
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "checking cardos version ...");
|
2007-06-25 18:01:26 +00:00
|
|
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x82);
|
|
|
|
apdu.resp = rbuf;
|
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
apdu.le = 256;
|
|
|
|
apdu.lc = 0;
|
|
|
|
rv = sc_transmit_apdu(card, &apdu);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "APDU transmit failed");
|
2007-06-25 18:01:26 +00:00
|
|
|
if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00)
|
|
|
|
return 0;
|
2011-01-07 17:18:58 +00:00
|
|
|
if (apdu.resp[0] != atr[10] ||
|
|
|
|
apdu.resp[1] != atr[11])
|
2007-06-25 18:01:26 +00:00
|
|
|
/* version mismatch */
|
|
|
|
return 0;
|
2011-01-07 17:18:58 +00:00
|
|
|
if (atr[11] <= 0x04) {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "found cardos m4.01");
|
2007-06-25 18:01:26 +00:00
|
|
|
card->type = SC_CARD_TYPE_CARDOS_M4_01;
|
2011-01-07 17:18:58 +00:00
|
|
|
} else if (atr[11] == 0x08) {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "found cardos v4.3b");
|
2007-06-25 18:01:26 +00:00
|
|
|
card->type = SC_CARD_TYPE_CARDOS_M4_3;
|
2011-01-07 17:18:58 +00:00
|
|
|
} else if (atr[11] == 0x09) {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "found cardos v4.2b");
|
2007-08-08 20:06:53 +00:00
|
|
|
card->type = SC_CARD_TYPE_CARDOS_M4_2B;
|
2012-12-02 18:21:08 +00:00
|
|
|
} else if (atr[11] >= 0x0B) {
|
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "found cardos v4.2c or higher");
|
|
|
|
card->type = SC_CARD_TYPE_CARDOS_M4_2C;
|
2007-06-25 18:01:26 +00:00
|
|
|
} else {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "found cardos m4.2");
|
2007-06-25 18:01:26 +00:00
|
|
|
}
|
|
|
|
}
|
2005-02-04 20:29:35 +00:00
|
|
|
return 1;
|
2002-04-10 23:10:47 +00:00
|
|
|
}
|
|
|
|
|
2005-12-28 19:38:55 +00:00
|
|
|
static int cardos_have_2048bit_package(sc_card_t *card)
|
|
|
|
{
|
|
|
|
sc_apdu_t apdu;
|
|
|
|
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
|
|
|
|
int r;
|
|
|
|
const u8 *p = rbuf, *q;
|
|
|
|
size_t len, tlen = 0, ilen = 0;
|
|
|
|
|
|
|
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x88);
|
|
|
|
apdu.resp = rbuf;
|
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
apdu.lc = 0;
|
|
|
|
apdu.le = 256;
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
|
2005-12-28 19:38:55 +00:00
|
|
|
|
|
|
|
if ((len = apdu.resplen) == 0)
|
|
|
|
/* looks like no package has been installed */
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
while (len != 0) {
|
|
|
|
p = sc_asn1_find_tag(card->ctx, p, len, 0xe1, &tlen);
|
|
|
|
if (p == NULL)
|
|
|
|
return 0;
|
|
|
|
q = sc_asn1_find_tag(card->ctx, p, tlen, 0x01, &ilen);
|
|
|
|
if (q == NULL || ilen != 4)
|
|
|
|
return 0;
|
|
|
|
if (q[0] == 0x1c)
|
|
|
|
return 1;
|
|
|
|
p += tlen;
|
|
|
|
len -= tlen + 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-01-23 21:43:01 +00:00
|
|
|
static int cardos_init(sc_card_t *card)
|
2002-04-10 23:10:47 +00:00
|
|
|
{
|
2011-04-07 18:16:40 +00:00
|
|
|
unsigned long flags, rsa_2048 = 0;
|
2002-06-07 20:28:11 +00:00
|
|
|
|
2003-05-28 20:52:33 +00:00
|
|
|
card->name = "CardOS M4";
|
2002-04-10 23:10:47 +00:00
|
|
|
card->cla = 0x00;
|
|
|
|
|
2002-06-07 20:28:11 +00:00
|
|
|
/* Set up algorithm info. */
|
2002-11-08 14:14:05 +00:00
|
|
|
flags = SC_ALGORITHM_NEED_USAGE
|
2003-04-14 10:33:07 +00:00
|
|
|
| SC_ALGORITHM_RSA_RAW
|
2003-04-14 15:19:53 +00:00
|
|
|
| SC_ALGORITHM_RSA_HASH_NONE
|
2002-06-07 20:28:11 +00:00
|
|
|
| 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);
|
|
|
|
|
2005-12-28 19:38:55 +00:00
|
|
|
if (card->type == SC_CARD_TYPE_CARDOS_M4_2) {
|
|
|
|
int r = cardos_have_2048bit_package(card);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (r == 1)
|
2011-04-07 18:16:40 +00:00
|
|
|
rsa_2048 = 1;
|
2005-12-28 19:38:55 +00:00
|
|
|
card->caps |= SC_CARD_CAP_APDU_EXT;
|
2007-08-08 20:06:53 +00:00
|
|
|
} else if (card->type == SC_CARD_TYPE_CARDOS_M4_3
|
2007-12-19 09:58:29 +00:00
|
|
|
|| card->type == SC_CARD_TYPE_CARDOS_M4_2B
|
2010-04-21 14:38:23 +00:00
|
|
|
|| card->type == SC_CARD_TYPE_CARDOS_M4_2C
|
2012-12-02 18:21:08 +00:00
|
|
|
|| card->type == SC_CARD_TYPE_CARDOS_M4_4
|
|
|
|
|| card->type == SC_CARD_TYPE_CARDOS_V5_0) {
|
2011-04-07 18:16:40 +00:00
|
|
|
rsa_2048 = 1;
|
2006-07-08 12:31:33 +00:00
|
|
|
card->caps |= SC_CARD_CAP_APDU_EXT;
|
2005-12-28 19:38:55 +00:00
|
|
|
}
|
|
|
|
|
2011-04-07 18:16:40 +00:00
|
|
|
if (rsa_2048 == 1) {
|
2005-12-28 19:38:55 +00:00
|
|
|
_sc_card_add_rsa_alg(card, 1280, flags, 0);
|
|
|
|
_sc_card_add_rsa_alg(card, 1536, flags, 0);
|
|
|
|
_sc_card_add_rsa_alg(card, 1792, flags, 0);
|
|
|
|
_sc_card_add_rsa_alg(card, 2048, flags, 0);
|
|
|
|
}
|
|
|
|
|
2002-04-10 23:10:47 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-01-23 21:43:01 +00:00
|
|
|
static const struct sc_card_error cardos_errors[] = {
|
2002-04-10 23:10:47 +00:00
|
|
|
/* some error inside the card */
|
|
|
|
/* i.e. nothing you can do */
|
|
|
|
{ 0x6581, SC_ERROR_MEMORY_FAILURE, "EEPROM error; command aborted"},
|
|
|
|
{ 0x6fff, SC_ERROR_CARD_CMD_FAILED, "internal assertion error"},
|
|
|
|
{ 0x6700, SC_ERROR_WRONG_LENGTH, "LC invalid"},
|
|
|
|
{ 0x6985, SC_ERROR_CARD_CMD_FAILED, "no random number available"},
|
|
|
|
{ 0x6f81, SC_ERROR_CARD_CMD_FAILED, "file invalid, maybe checksum error"},
|
|
|
|
{ 0x6f82, SC_ERROR_CARD_CMD_FAILED, "not enough memory in xram"},
|
|
|
|
{ 0x6f84, SC_ERROR_CARD_CMD_FAILED, "general protection fault"},
|
|
|
|
|
|
|
|
/* the card doesn't now thic combination of ins+cla+p1+p2 */
|
|
|
|
/* i.e. command will never work */
|
|
|
|
{ 0x6881, SC_ERROR_NO_CARD_SUPPORT, "logical channel not supported"},
|
|
|
|
{ 0x6a86, SC_ERROR_INCORRECT_PARAMETERS,"p1/p2 invalid"},
|
|
|
|
{ 0x6d00, SC_ERROR_INS_NOT_SUPPORTED, "ins invalid"},
|
|
|
|
{ 0x6e00, SC_ERROR_CLASS_NOT_SUPPORTED, "class invalid (hi nibble)"},
|
|
|
|
|
|
|
|
/* known command, but incorrectly used */
|
|
|
|
/* i.e. command could work, but you need to change something */
|
|
|
|
{ 0x6981, SC_ERROR_CARD_CMD_FAILED, "command cannot be used for file structure"},
|
|
|
|
{ 0x6a80, SC_ERROR_INCORRECT_PARAMETERS,"invalid parameters in data field"},
|
|
|
|
{ 0x6a81, SC_ERROR_NOT_SUPPORTED, "function/mode not supported"},
|
|
|
|
{ 0x6a85, SC_ERROR_INCORRECT_PARAMETERS,"lc does not fit the tlv structure"},
|
|
|
|
{ 0x6986, SC_ERROR_INCORRECT_PARAMETERS,"no current ef selected"},
|
|
|
|
{ 0x6a87, SC_ERROR_INCORRECT_PARAMETERS,"lc does not fit p1/p2"},
|
|
|
|
{ 0x6c00, SC_ERROR_WRONG_LENGTH, "le does not fit the data to be sent"},
|
|
|
|
{ 0x6f83, SC_ERROR_CARD_CMD_FAILED, "command must not be used in transaction"},
|
|
|
|
|
|
|
|
/* (something) not found */
|
|
|
|
{ 0x6987, SC_ERROR_INCORRECT_PARAMETERS,"key object for sm not found"},
|
|
|
|
{ 0x6f86, SC_ERROR_CARD_CMD_FAILED, "key object not found"},
|
|
|
|
{ 0x6a82, SC_ERROR_FILE_NOT_FOUND, "file not found"},
|
|
|
|
{ 0x6a83, SC_ERROR_RECORD_NOT_FOUND, "record not found"},
|
|
|
|
{ 0x6a88, SC_ERROR_CARD_CMD_FAILED, "object not found"},
|
|
|
|
|
|
|
|
/* (something) invalid */
|
|
|
|
{ 0x6884, SC_ERROR_CARD_CMD_FAILED, "chaining error"},
|
|
|
|
{ 0x6984, SC_ERROR_CARD_CMD_FAILED, "bs object has invalid format"},
|
|
|
|
{ 0x6988, SC_ERROR_INCORRECT_PARAMETERS,"key object used for sm has invalid format"},
|
|
|
|
|
|
|
|
/* (something) deactivated */
|
|
|
|
{ 0x6283, SC_ERROR_CARD_CMD_FAILED, "file is deactivated" },
|
|
|
|
{ 0x6983, SC_ERROR_AUTH_METHOD_BLOCKED, "bs object blocked"},
|
|
|
|
|
|
|
|
/* access denied */
|
|
|
|
{ 0x6300, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED,"authentication failed"},
|
|
|
|
{ 0x6982, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED,"required access right not granted"},
|
|
|
|
|
|
|
|
/* other errors */
|
|
|
|
{ 0x6a84, SC_ERROR_CARD_CMD_FAILED, "not enough memory"},
|
|
|
|
|
|
|
|
/* command ok, execution failed */
|
|
|
|
{ 0x6f00, SC_ERROR_CARD_CMD_FAILED, "technical error (see eToken developers guide)"},
|
|
|
|
|
|
|
|
/* no error, maybe a note */
|
2010-12-16 05:04:37 +00:00
|
|
|
{ 0x9000, SC_SUCCESS, NULL},
|
|
|
|
{ 0x9001, SC_SUCCESS, "success, but eeprom weakness detected"},
|
|
|
|
{ 0x9850, SC_SUCCESS, "over/underflow useing in/decrease"}
|
2002-04-10 23:10:47 +00:00
|
|
|
};
|
|
|
|
|
2006-01-23 21:43:01 +00:00
|
|
|
static int cardos_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2)
|
2002-04-10 23:10:47 +00:00
|
|
|
{
|
2006-01-23 21:43:01 +00:00
|
|
|
const int err_count = sizeof(cardos_errors)/sizeof(cardos_errors[0]);
|
2002-04-10 23:10:47 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < err_count; i++) {
|
2006-01-23 21:43:01 +00:00
|
|
|
if (cardos_errors[i].SWs == ((sw1 << 8) | sw2)) {
|
|
|
|
if ( cardos_errors[i].errorstr )
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s\n",
|
2006-01-23 21:43:01 +00:00
|
|
|
cardos_errors[i].errorstr);
|
|
|
|
return cardos_errors[i].errorno;
|
2002-04-10 23:10:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unknown SWs; SW1=%02X, SW2=%02X\n", sw1, sw2);
|
2002-04-10 23:10:47 +00:00
|
|
|
return SC_ERROR_CARD_CMD_FAILED;
|
|
|
|
}
|
|
|
|
|
2006-01-23 21:43:01 +00:00
|
|
|
static int cardos_list_files(sc_card_t *card, u8 *buf, size_t buflen)
|
2002-04-10 23:10:47 +00:00
|
|
|
{
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_apdu_t apdu;
|
2005-12-28 19:38:55 +00:00
|
|
|
u8 rbuf[256], offset = 0;
|
|
|
|
const u8 *p = rbuf, *q;
|
|
|
|
int r;
|
|
|
|
size_t fids = 0, len;
|
2002-04-10 23:10:47 +00:00
|
|
|
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
2002-06-04 08:50:17 +00:00
|
|
|
|
2002-04-17 13:36:35 +00:00
|
|
|
/* 0x16: DIRECTORY */
|
|
|
|
/* 0x02: list both DF and EF */
|
|
|
|
|
2002-04-10 23:10:47 +00:00
|
|
|
get_next_part:
|
|
|
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x16, 0x02, offset);
|
|
|
|
apdu.cla = 0x80;
|
|
|
|
apdu.le = 256;
|
|
|
|
apdu.resplen = 256;
|
|
|
|
apdu.resp = rbuf;
|
2002-06-04 08:50:17 +00:00
|
|
|
|
2002-04-10 23:10:47 +00:00
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
|
2002-06-04 08:50:17 +00:00
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "DIRECTORY command returned error");
|
2002-06-04 08:50:17 +00:00
|
|
|
|
2002-04-10 23:10:47 +00:00
|
|
|
if (apdu.resplen > 256) {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "directory listing > 256 bytes, cutting");
|
2002-04-10 23:10:47 +00:00
|
|
|
}
|
2005-12-28 19:38:55 +00:00
|
|
|
|
|
|
|
len = apdu.resplen;
|
|
|
|
while (len != 0) {
|
|
|
|
size_t tlen = 0, ilen = 0;
|
2002-04-10 23:10:47 +00:00
|
|
|
/* is there a file informatin block (0x6f) ? */
|
2005-12-28 19:38:55 +00:00
|
|
|
p = sc_asn1_find_tag(card->ctx, p, len, 0x6f, &tlen);
|
|
|
|
if (p == NULL) {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "directory tag missing");
|
2005-12-28 19:38:55 +00:00
|
|
|
return SC_ERROR_INTERNAL;
|
2002-04-10 23:10:47 +00:00
|
|
|
}
|
2005-12-28 19:38:55 +00:00
|
|
|
if (tlen == 0)
|
|
|
|
/* empty directory */
|
2002-04-10 23:10:47 +00:00
|
|
|
break;
|
2005-12-28 19:38:55 +00:00
|
|
|
q = sc_asn1_find_tag(card->ctx, p, tlen, 0x86, &ilen);
|
|
|
|
if (q == NULL || ilen != 2) {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "error parsing file id TLV object");
|
2005-12-28 19:38:55 +00:00
|
|
|
return SC_ERROR_INTERNAL;
|
2002-04-10 23:10:47 +00:00
|
|
|
}
|
2005-12-28 19:38:55 +00:00
|
|
|
/* put file id in buf */
|
|
|
|
if (buflen >= 2) {
|
|
|
|
buf[fids++] = q[0];
|
|
|
|
buf[fids++] = q[1];
|
|
|
|
buflen -= 2;
|
|
|
|
} else
|
|
|
|
/* not enought space left in buffer => break */
|
2002-04-10 23:10:47 +00:00
|
|
|
break;
|
2005-12-28 19:38:55 +00:00
|
|
|
/* extract next offset */
|
|
|
|
q = sc_asn1_find_tag(card->ctx, p, tlen, 0x8a, &ilen);
|
|
|
|
if (q != NULL && ilen == 1) {
|
|
|
|
offset = (u8)ilen;
|
|
|
|
if (offset != 0)
|
|
|
|
goto get_next_part;
|
2002-04-10 23:10:47 +00:00
|
|
|
}
|
2005-12-28 19:38:55 +00:00
|
|
|
len -= tlen + 2;
|
|
|
|
p += tlen;
|
2002-04-10 23:10:47 +00:00
|
|
|
}
|
|
|
|
|
2002-06-04 08:50:17 +00:00
|
|
|
r = fids;
|
|
|
|
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
|
2002-04-10 23:10:47 +00:00
|
|
|
}
|
|
|
|
|
2005-03-08 20:59:35 +00:00
|
|
|
static void add_acl_entry(sc_file_t *file, int op, u8 byte)
|
2002-04-17 13:36:35 +00:00
|
|
|
{
|
2002-04-22 23:01:57 +00:00
|
|
|
unsigned int method, key_ref = SC_AC_KEY_REF_NONE;
|
|
|
|
|
|
|
|
switch (byte) {
|
|
|
|
case 0x00:
|
|
|
|
method = SC_AC_NONE;
|
|
|
|
break;
|
|
|
|
case 0xFF:
|
|
|
|
method = SC_AC_NEVER;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (byte > 0x7F) {
|
|
|
|
method = SC_AC_UNKNOWN;
|
|
|
|
} else {
|
|
|
|
method = SC_AC_CHV;
|
|
|
|
key_ref = byte;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
sc_file_add_acl_entry(file, op, method, key_ref);
|
|
|
|
}
|
|
|
|
|
2005-03-08 20:59:35 +00:00
|
|
|
static int acl_to_byte(const sc_acl_entry_t *e)
|
2002-04-22 23:01:57 +00:00
|
|
|
{
|
|
|
|
if (e != NULL) {
|
|
|
|
switch (e->method) {
|
|
|
|
case SC_AC_NONE:
|
|
|
|
return 0x00;
|
|
|
|
case SC_AC_NEVER:
|
|
|
|
return 0xFF;
|
|
|
|
case SC_AC_CHV:
|
|
|
|
case SC_AC_TERM:
|
|
|
|
case SC_AC_AUT:
|
|
|
|
if (e->key_ref == SC_AC_KEY_REF_NONE)
|
|
|
|
return -1;
|
2005-08-02 21:13:06 +00:00
|
|
|
if (e->key_ref > 0x7F)
|
2002-04-22 23:01:57 +00:00
|
|
|
return -1;
|
|
|
|
return e->key_ref;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0x00;
|
2002-04-17 13:36:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static const int df_acl[9] = {
|
|
|
|
-1, /* LCYCLE (life cycle change) */
|
2003-10-21 08:31:45 +00:00
|
|
|
SC_AC_OP_UPDATE, /* UPDATE Objects */
|
2002-04-17 13:36:35 +00:00
|
|
|
-1, /* APPEND Objects */
|
|
|
|
|
|
|
|
SC_AC_OP_INVALIDATE, /* DF */
|
|
|
|
SC_AC_OP_REHABILITATE, /* DF */
|
|
|
|
SC_AC_OP_DELETE, /* DF */
|
|
|
|
|
2008-07-31 13:25:16 +00:00
|
|
|
SC_AC_OP_UPDATE, /* ADMIN DF */
|
2002-04-17 13:36:35 +00:00
|
|
|
SC_AC_OP_CREATE, /* Files */
|
|
|
|
-1 /* Reserved */
|
|
|
|
};
|
|
|
|
static const int ef_acl[9] = {
|
|
|
|
SC_AC_OP_READ, /* Data */
|
|
|
|
SC_AC_OP_UPDATE, /* Data (write file content) */
|
|
|
|
SC_AC_OP_WRITE, /* */
|
|
|
|
|
|
|
|
SC_AC_OP_INVALIDATE, /* EF */
|
|
|
|
SC_AC_OP_REHABILITATE, /* EF */
|
2005-12-28 19:38:55 +00:00
|
|
|
SC_AC_OP_DELETE, /* (delete) EF */
|
2002-04-17 13:36:35 +00:00
|
|
|
|
2002-06-04 08:50:17 +00:00
|
|
|
/* XXX: ADMIN should be an ACL type of its own, or mapped
|
|
|
|
* to erase */
|
2008-07-31 13:25:16 +00:00
|
|
|
SC_AC_OP_UPDATE, /* ADMIN EF (modify meta information?) */
|
2002-04-17 13:36:35 +00:00
|
|
|
-1, /* INC (-> cylic fixed files) */
|
|
|
|
-1 /* DEC */
|
|
|
|
};
|
|
|
|
|
2005-03-08 20:59:35 +00:00
|
|
|
static void parse_sec_attr(sc_file_t *file, const u8 *buf, size_t len)
|
2002-04-17 13:36:35 +00:00
|
|
|
{
|
2003-05-30 08:54:42 +00:00
|
|
|
size_t i;
|
2002-04-17 13:36:35 +00:00
|
|
|
const int *idx;
|
|
|
|
|
|
|
|
idx = (file->type == SC_FILE_TYPE_DF) ? df_acl : ef_acl;
|
2002-04-22 23:01:57 +00:00
|
|
|
|
|
|
|
/* acl defaults to 0xFF if unspecified */
|
2002-04-17 13:36:35 +00:00
|
|
|
for (i = 0; i < 9; i++)
|
|
|
|
if (idx[i] != -1)
|
2002-06-14 12:52:56 +00:00
|
|
|
add_acl_entry(file, idx[i], (u8)((i < len) ? buf[i] : 0xFF));
|
2002-04-17 13:36:35 +00:00
|
|
|
}
|
|
|
|
|
2006-01-23 21:43:01 +00:00
|
|
|
static int cardos_select_file(sc_card_t *card,
|
2005-03-08 20:59:35 +00:00
|
|
|
const sc_path_t *in_path,
|
|
|
|
sc_file_t **file)
|
2002-04-17 13:36:35 +00:00
|
|
|
{
|
|
|
|
int r;
|
|
|
|
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
2002-04-17 13:36:35 +00:00
|
|
|
r = iso_ops->select_file(card, in_path, file);
|
2002-06-04 08:50:17 +00:00
|
|
|
if (r >= 0 && file)
|
2002-04-17 13:36:35 +00:00
|
|
|
parse_sec_attr((*file), (*file)->sec_attr, (*file)->sec_attr_len);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
|
2002-04-17 13:36:35 +00:00
|
|
|
}
|
|
|
|
|
2005-12-28 19:38:55 +00:00
|
|
|
static int cardos_acl_to_bytes(sc_card_t *card, const sc_file_t *file,
|
|
|
|
u8 *buf, size_t *outlen)
|
2002-04-17 13:36:35 +00:00
|
|
|
{
|
2005-12-28 19:38:55 +00:00
|
|
|
int i, byte;
|
2002-04-22 23:01:57 +00:00
|
|
|
const int *idx;
|
2002-04-17 13:36:35 +00:00
|
|
|
|
2005-12-28 19:38:55 +00:00
|
|
|
if (buf == NULL || *outlen < 9)
|
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
2002-06-04 08:50:17 +00:00
|
|
|
|
2005-12-28 19:38:55 +00:00
|
|
|
idx = (file->type == SC_FILE_TYPE_DF) ? df_acl : ef_acl;
|
|
|
|
for (i = 0; i < 9; i++) {
|
|
|
|
if (idx[i] < 0)
|
|
|
|
byte = 0x00;
|
|
|
|
else
|
|
|
|
byte = acl_to_byte(sc_file_get_acl_entry(file, idx[i]));
|
|
|
|
if (byte < 0) {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid ACL\n");
|
2005-12-28 19:38:55 +00:00
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
2002-06-04 08:50:17 +00:00
|
|
|
}
|
2005-12-28 19:38:55 +00:00
|
|
|
buf[i] = byte;
|
2002-06-04 08:50:17 +00:00
|
|
|
}
|
2005-12-28 19:38:55 +00:00
|
|
|
*outlen = 9;
|
|
|
|
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cardos_set_file_attributes(sc_card_t *card, sc_file_t *file)
|
|
|
|
{
|
|
|
|
int r;
|
2002-06-04 08:50:17 +00:00
|
|
|
|
2002-04-17 13:36:35 +00:00
|
|
|
if (file->type_attr_len == 0) {
|
2005-12-28 19:38:55 +00:00
|
|
|
u8 type[3];
|
|
|
|
|
2002-06-17 10:55:58 +00:00
|
|
|
memset(type, 0, sizeof(type));
|
2002-04-17 13:36:35 +00:00
|
|
|
type[0] = 0x00;
|
|
|
|
switch (file->type) {
|
|
|
|
case SC_FILE_TYPE_WORKING_EF:
|
|
|
|
break;
|
|
|
|
case SC_FILE_TYPE_DF:
|
|
|
|
type[0] = 0x38;
|
|
|
|
break;
|
|
|
|
default:
|
2005-12-28 19:38:55 +00:00
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
2002-04-17 13:36:35 +00:00
|
|
|
}
|
2002-04-22 23:01:57 +00:00
|
|
|
if (file->type != SC_FILE_TYPE_DF) {
|
|
|
|
switch (file->ef_structure) {
|
|
|
|
case SC_FILE_EF_LINEAR_FIXED_TLV:
|
|
|
|
case SC_FILE_EF_LINEAR_VARIABLE:
|
|
|
|
case SC_FILE_EF_CYCLIC_TLV:
|
2005-12-28 19:38:55 +00:00
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
2002-06-17 10:55:58 +00:00
|
|
|
/* No idea what this means, but it
|
|
|
|
* seems to be required for key
|
|
|
|
* generation. */
|
|
|
|
case SC_FILE_EF_LINEAR_VARIABLE_TLV:
|
|
|
|
type[1] = 0xff;
|
2002-04-22 23:01:57 +00:00
|
|
|
default:
|
|
|
|
type[0] |= file->ef_structure & 7;
|
|
|
|
break;
|
|
|
|
}
|
2002-04-17 13:36:35 +00:00
|
|
|
}
|
|
|
|
r = sc_file_set_type_attr(file, type, sizeof(type));
|
2005-12-28 19:38:55 +00:00
|
|
|
if (r != SC_SUCCESS)
|
|
|
|
return r;
|
2002-04-17 13:36:35 +00:00
|
|
|
}
|
|
|
|
if (file->prop_attr_len == 0) {
|
2005-12-28 19:38:55 +00:00
|
|
|
u8 status[3];
|
|
|
|
|
2002-04-17 13:36:35 +00:00
|
|
|
status[0] = 0x01;
|
|
|
|
if (file->type == SC_FILE_TYPE_DF) {
|
2002-06-04 08:50:17 +00:00
|
|
|
status[1] = file->size >> 8;
|
|
|
|
status[2] = file->size;
|
2002-04-17 13:36:35 +00:00
|
|
|
} else {
|
|
|
|
status[1] = status[2] = 0x00; /* not used */
|
|
|
|
}
|
|
|
|
r = sc_file_set_prop_attr(file, status, sizeof(status));
|
2005-12-28 19:38:55 +00:00
|
|
|
if (r != SC_SUCCESS)
|
|
|
|
return r;
|
2002-04-17 13:36:35 +00:00
|
|
|
}
|
|
|
|
if (file->sec_attr_len == 0) {
|
2005-12-28 19:38:55 +00:00
|
|
|
u8 acl[9];
|
|
|
|
size_t blen = sizeof(acl);
|
|
|
|
|
|
|
|
r = cardos_acl_to_bytes(card, file, acl, &blen);
|
|
|
|
if (r != SC_SUCCESS)
|
|
|
|
return r;
|
|
|
|
r = sc_file_set_sec_attr(file, acl, blen);
|
|
|
|
if (r != SC_SUCCESS)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* newer versions of cardos seems to prefer the FCP */
|
|
|
|
static int cardos_construct_fcp(sc_card_t *card, const sc_file_t *file,
|
|
|
|
u8 *out, size_t *outlen)
|
|
|
|
{
|
|
|
|
u8 buf[64], *p = out;
|
|
|
|
size_t inlen = *outlen, len;
|
|
|
|
int r;
|
|
|
|
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL);
|
2005-12-28 19:38:55 +00:00
|
|
|
|
|
|
|
if (out == NULL || inlen < 64)
|
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
/* add FCP tag */
|
|
|
|
*p++ = 0x62;
|
|
|
|
/* we will add the length later */
|
|
|
|
p++;
|
|
|
|
|
|
|
|
/* set the length */
|
|
|
|
buf[0] = (file->size >> 8) & 0xff;
|
|
|
|
buf[1] = file->size & 0xff;
|
|
|
|
if (file->type == SC_FILE_TYPE_DF)
|
|
|
|
r = sc_asn1_put_tag(0x81, buf, 2, p, 4, &p);
|
|
|
|
else
|
|
|
|
r = sc_asn1_put_tag(0x80, buf, 2, p, 4, &p);
|
|
|
|
if (r != SC_SUCCESS)
|
|
|
|
return r;
|
|
|
|
/* set file type */
|
|
|
|
if (file->shareable != 0)
|
|
|
|
buf[0] = 0x40;
|
|
|
|
else
|
|
|
|
buf[0] = 0x00;
|
|
|
|
if (file->type == SC_FILE_TYPE_WORKING_EF) {
|
|
|
|
switch (file->ef_structure) {
|
|
|
|
case SC_FILE_EF_TRANSPARENT:
|
|
|
|
buf[0] |= 0x01;
|
|
|
|
break;
|
|
|
|
case SC_FILE_EF_LINEAR_VARIABLE_TLV:
|
|
|
|
buf[0] |= 0x05;
|
|
|
|
break;
|
|
|
|
case SC_FILE_EF_LINEAR_FIXED:
|
|
|
|
buf[0] |= 0x02;
|
|
|
|
buf[1] |= 0x21;
|
|
|
|
buf[2] |= 0x00;
|
|
|
|
buf[3] |= (u8) file->record_length;
|
|
|
|
buf[4] |= (u8) file->record_count;
|
|
|
|
break;
|
|
|
|
case SC_FILE_EF_CYCLIC:
|
|
|
|
buf[0] |= 0x06;
|
|
|
|
buf[1] |= 0x21;
|
|
|
|
buf[2] |= 0x00;
|
|
|
|
buf[3] |= (u8) file->record_length;
|
|
|
|
buf[4] |= (u8) file->record_count;
|
|
|
|
break;
|
|
|
|
default:
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unknown EF type: %u", file->type);
|
2005-12-28 19:38:55 +00:00
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
2002-04-22 23:01:57 +00:00
|
|
|
}
|
2005-12-28 19:38:55 +00:00
|
|
|
if (file->ef_structure == SC_FILE_EF_CYCLIC ||
|
|
|
|
file->ef_structure == SC_FILE_EF_LINEAR_FIXED)
|
|
|
|
r = sc_asn1_put_tag(0x82, buf, 5, p, 8, &p);
|
|
|
|
else
|
|
|
|
r = sc_asn1_put_tag(0x82, buf, 1, p, 8, &p);
|
|
|
|
} else if (file->type == SC_FILE_TYPE_DF) {
|
|
|
|
buf[0] |= 0x38;
|
|
|
|
r = sc_asn1_put_tag(0x82, buf, 1, p, 8, &p);
|
|
|
|
} else
|
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
|
|
if (r != SC_SUCCESS)
|
|
|
|
return r;
|
|
|
|
/* set file id */
|
|
|
|
buf[0] = (file->id >> 8) & 0xff;
|
|
|
|
buf[1] = file->id & 0xff;
|
|
|
|
r = sc_asn1_put_tag(0x83, buf, 2, p, 8, &p);
|
|
|
|
if (r != SC_SUCCESS)
|
|
|
|
return r;
|
|
|
|
/* set aid (for DF only) */
|
|
|
|
if (file->type == SC_FILE_TYPE_DF && file->namelen != 0) {
|
|
|
|
r = sc_asn1_put_tag(0x84, file->name, file->namelen, p, 20, &p);
|
|
|
|
if (r != SC_SUCCESS)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
/* set proprietary file attributes */
|
|
|
|
buf[0] = 0x00; /* use default values */
|
|
|
|
if (file->type == SC_FILE_TYPE_DF)
|
|
|
|
r = sc_asn1_put_tag(0x85, buf, 1, p, 8, &p);
|
|
|
|
else {
|
|
|
|
buf[1] = 0x00;
|
|
|
|
buf[2] = 0x00;
|
|
|
|
r = sc_asn1_put_tag(0x85, buf, 1, p, 8, &p);
|
2002-04-17 13:36:35 +00:00
|
|
|
}
|
2005-12-28 19:38:55 +00:00
|
|
|
if (r != SC_SUCCESS)
|
|
|
|
return r;
|
|
|
|
/* set ACs */
|
|
|
|
len = 9;
|
|
|
|
r = cardos_acl_to_bytes(card, file, buf, &len);
|
|
|
|
if (r != SC_SUCCESS)
|
|
|
|
return r;
|
|
|
|
r = sc_asn1_put_tag(0x86, buf, len, p, 18, &p);
|
|
|
|
if (r != SC_SUCCESS)
|
|
|
|
return r;
|
|
|
|
/* finally set the length of the FCP */
|
|
|
|
out[1] = p - out - 2;
|
|
|
|
|
|
|
|
*outlen = p - out;
|
|
|
|
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cardos_create_file(sc_card_t *card, sc_file_t *file)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
2005-12-28 19:38:55 +00:00
|
|
|
|
2006-06-17 12:24:04 +00:00
|
|
|
if (card->type == SC_CARD_TYPE_CARDOS_GENERIC ||
|
|
|
|
card->type == SC_CARD_TYPE_CARDOS_M4_01) {
|
2005-12-28 19:38:55 +00:00
|
|
|
r = cardos_set_file_attributes(card, file);
|
|
|
|
if (r != SC_SUCCESS)
|
|
|
|
return r;
|
|
|
|
return iso_ops->create_file(card, file);
|
2006-07-13 20:37:05 +00:00
|
|
|
} else if (card->type == SC_CARD_TYPE_CARDOS_M4_2 ||
|
2007-08-08 20:06:53 +00:00
|
|
|
card->type == SC_CARD_TYPE_CARDOS_M4_3 ||
|
2007-12-19 09:58:29 +00:00
|
|
|
card->type == SC_CARD_TYPE_CARDOS_M4_2B ||
|
2010-04-21 14:38:23 +00:00
|
|
|
card->type == SC_CARD_TYPE_CARDOS_M4_2C ||
|
|
|
|
card->type == SC_CARD_TYPE_CARDOS_M4_4) {
|
2005-12-28 19:38:55 +00:00
|
|
|
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
|
|
|
|
size_t len = sizeof(sbuf);
|
|
|
|
sc_apdu_t apdu;
|
|
|
|
|
|
|
|
r = cardos_construct_fcp(card, file, sbuf, &len);
|
|
|
|
if (r < 0) {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unable to create FCP");
|
2005-12-28 19:38:55 +00:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, 0x00);
|
|
|
|
apdu.lc = len;
|
|
|
|
apdu.datalen = len;
|
|
|
|
apdu.data = sbuf;
|
2002-06-04 08:50:17 +00:00
|
|
|
|
2005-12-28 19:38:55 +00:00
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
|
2002-06-06 09:17:52 +00:00
|
|
|
|
2005-12-28 19:38:55 +00:00
|
|
|
return sc_check_sw(card, apdu.sw1, apdu.sw2);
|
|
|
|
} else
|
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
2002-04-17 13:36:35 +00:00
|
|
|
}
|
|
|
|
|
2002-06-11 18:14:25 +00:00
|
|
|
/*
|
|
|
|
* Restore the indicated SE
|
|
|
|
*/
|
|
|
|
static int
|
2006-01-23 21:43:01 +00:00
|
|
|
cardos_restore_security_env(sc_card_t *card, int se_num)
|
2002-06-11 18:14:25 +00:00
|
|
|
{
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_apdu_t apdu;
|
2002-06-11 18:14:25 +00:00
|
|
|
int r;
|
|
|
|
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
2002-06-11 18:14:25 +00:00
|
|
|
|
2009-10-03 10:13:45 +00:00
|
|
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x22, 0, se_num);
|
|
|
|
apdu.p1 = (card->type == SC_CARD_TYPE_CARDOS_CIE_V1 ? 0xF3 : 0x03);
|
2002-06-11 18:14:25 +00:00
|
|
|
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
|
2002-06-11 18:14:25 +00:00
|
|
|
|
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error");
|
2002-06-11 18:14:25 +00:00
|
|
|
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
|
2002-06-11 18:14:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the security context
|
|
|
|
* Things get a little messy here. It seems you cannot do any
|
|
|
|
* crypto without a security environment - but there isn't really
|
|
|
|
* a way to specify the security environment in PKCS15.
|
|
|
|
* What I'm doing here (for now) is to assume that for a key
|
|
|
|
* object with ID 0xNN there is always a corresponding SE object
|
|
|
|
* with the same ID.
|
|
|
|
* XXX Need to find out how the Aladdin drivers do it.
|
|
|
|
*/
|
|
|
|
static int
|
2006-01-23 21:43:01 +00:00
|
|
|
cardos_set_security_env(sc_card_t *card,
|
2005-03-08 20:59:35 +00:00
|
|
|
const sc_security_env_t *env,
|
2002-06-11 18:14:25 +00:00
|
|
|
int se_num)
|
|
|
|
{
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_apdu_t apdu;
|
2002-06-11 18:14:25 +00:00
|
|
|
u8 data[3];
|
|
|
|
int key_id, r;
|
|
|
|
|
|
|
|
assert(card != NULL && env != NULL);
|
|
|
|
|
2012-12-16 10:35:23 +00:00
|
|
|
if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT) || env->key_ref_len != 1) {
|
|
|
|
sc_log(card->ctx, "No or invalid key reference\n");
|
2002-06-11 18:14:25 +00:00
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
}
|
|
|
|
key_id = env->key_ref[0];
|
|
|
|
|
2009-10-03 10:13:45 +00:00
|
|
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0, 0);
|
|
|
|
if (card->type == SC_CARD_TYPE_CARDOS_CIE_V1) {
|
|
|
|
cardos_restore_security_env(card, 0x30);
|
|
|
|
apdu.p1 = 0xF1;
|
|
|
|
} else {
|
|
|
|
apdu.p1 = 0x01;
|
|
|
|
}
|
2002-06-11 18:14:25 +00:00
|
|
|
switch (env->operation) {
|
|
|
|
case SC_SEC_OPERATION_DECIPHER:
|
|
|
|
apdu.p2 = 0xB8;
|
|
|
|
break;
|
|
|
|
case SC_SEC_OPERATION_SIGN:
|
|
|
|
apdu.p2 = 0xB6;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
}
|
|
|
|
|
|
|
|
data[0] = 0x83;
|
|
|
|
data[1] = 0x01;
|
|
|
|
data[2] = key_id;
|
|
|
|
apdu.lc = apdu.datalen = 3;
|
|
|
|
apdu.data = data;
|
|
|
|
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
|
2002-06-11 18:14:25 +00:00
|
|
|
|
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error");
|
2002-06-11 18:14:25 +00:00
|
|
|
|
2012-12-16 10:35:23 +00:00
|
|
|
do {
|
|
|
|
const struct sc_supported_algo_info* algorithm_info = env->supported_algos;
|
|
|
|
int i=0;
|
|
|
|
int algorithm_id_count = 0;
|
2012-11-09 09:57:37 +00:00
|
|
|
|
2012-12-16 10:35:23 +00:00
|
|
|
for(i=0;i<SC_MAX_SUPPORTED_ALGORITHMS;++i) {
|
|
|
|
struct sc_supported_algo_info alg = algorithm_info[i];
|
2012-11-09 09:57:37 +00:00
|
|
|
|
2012-12-16 10:35:23 +00:00
|
|
|
if(alg.operations & SC_PKCS15_ALGO_OP_COMPUTE_SIGNATURE) {
|
|
|
|
unsigned int algorithm_id = alg.algo_ref;
|
|
|
|
|
|
|
|
sc_log(card->ctx, "is signature");
|
|
|
|
sc_log(card->ctx, "Adding ID %d at index %d", algorithm_id, algorithm_id_count);
|
|
|
|
algorithm_ids_in_tokeninfo[algorithm_id_count++] = algorithm_id;
|
|
|
|
}
|
|
|
|
sc_log(card->ctx, "reference=%d, mechanism=%d, operations=%d, algo_ref=%d",
|
|
|
|
alg.reference, alg.mechanism, alg.operations, alg.algo_ref);
|
|
|
|
}
|
|
|
|
algorithm_ids_in_tokeninfo_count = algorithm_id_count;
|
|
|
|
} while (0);
|
|
|
|
|
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
2002-06-11 18:14:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Compute digital signature
|
|
|
|
*/
|
2003-07-14 16:55:54 +00:00
|
|
|
|
|
|
|
/* internal function to do the actual signature computation */
|
2002-06-11 18:14:25 +00:00
|
|
|
static int
|
2005-03-08 20:59:35 +00:00
|
|
|
do_compute_signature(sc_card_t *card, const u8 *data, size_t datalen,
|
2003-07-14 16:55:54 +00:00
|
|
|
u8 *out, size_t outlen)
|
2002-06-11 18:14:25 +00:00
|
|
|
{
|
|
|
|
int r;
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_apdu_t apdu;
|
2002-06-11 18:14:25 +00:00
|
|
|
|
|
|
|
/* INS: 0x2A PERFORM SECURITY OPERATION
|
|
|
|
* P1: 0x9E Resp: Digital Signature
|
|
|
|
* P2: 0x9A Cmd: Input for Digital Signature */
|
2005-12-28 19:38:55 +00:00
|
|
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x2A, 0x9E, 0x9A);
|
|
|
|
apdu.resp = out;
|
|
|
|
apdu.le = outlen;
|
|
|
|
apdu.resplen = outlen;
|
2002-06-11 18:14:25 +00:00
|
|
|
|
2005-12-28 19:38:55 +00:00
|
|
|
apdu.data = data;
|
|
|
|
apdu.lc = datalen;
|
2002-06-11 18:14:25 +00:00
|
|
|
apdu.datalen = datalen;
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
|
2002-06-11 18:14:25 +00:00
|
|
|
|
2005-12-28 19:38:55 +00:00
|
|
|
if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00)
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, apdu.resplen);
|
2005-12-28 19:38:55 +00:00
|
|
|
else
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2));
|
2002-06-11 18:14:25 +00:00
|
|
|
}
|
|
|
|
|
2003-07-14 16:55:54 +00:00
|
|
|
static int
|
2006-01-23 21:43:01 +00:00
|
|
|
cardos_compute_signature(sc_card_t *card, const u8 *data, size_t datalen,
|
2003-07-14 16:55:54 +00:00
|
|
|
u8 *out, size_t outlen)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
u8 buf[SC_MAX_APDU_BUFFER_SIZE];
|
|
|
|
size_t buf_len = sizeof(buf), tmp_len = buf_len;
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_context_t *ctx;
|
2012-12-16 10:35:23 +00:00
|
|
|
int do_rsa_pure_sig = 0;
|
|
|
|
int do_rsa_sig = 0;
|
|
|
|
|
2003-07-14 16:55:54 +00:00
|
|
|
|
2012-12-16 10:35:23 +00:00
|
|
|
assert(card != NULL && data != NULL && out != NULL);
|
2003-07-14 16:55:54 +00:00
|
|
|
ctx = card->ctx;
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE);
|
2002-06-11 18:14:25 +00:00
|
|
|
|
2005-12-28 19:38:55 +00:00
|
|
|
if (datalen > SC_MAX_APDU_BUFFER_SIZE)
|
2012-12-16 10:35:23 +00:00
|
|
|
LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
|
2003-07-14 16:55:54 +00:00
|
|
|
if (outlen < datalen)
|
2012-12-16 10:35:23 +00:00
|
|
|
LOG_FUNC_RETURN(ctx, SC_ERROR_BUFFER_TOO_SMALL);
|
2010-08-27 08:24:39 +00:00
|
|
|
outlen = datalen;
|
2003-07-14 16:55:54 +00:00
|
|
|
|
2012-12-16 10:35:23 +00:00
|
|
|
/* There are two ways to create a signature, depending on the way,
|
2012-11-09 09:57:37 +00:00
|
|
|
* the key was created: RSA_SIG and RSA_PURE_SIG.
|
|
|
|
* We can use the following reasoning, to determine the correct operation:
|
|
|
|
* 1. We check for several caps flags (as set in card->caps), to pervent generating
|
|
|
|
* invalid signatures with duplicated hash prefixes with some cards
|
2012-12-16 10:35:23 +00:00
|
|
|
* 2. Use the information from AlgorithmInfo of the TokenInfo file.
|
2012-11-09 09:57:37 +00:00
|
|
|
* This information is parsed in set_security_env and stored in a static variable.
|
|
|
|
* The problem is, that that information is only available for the whole token and not
|
|
|
|
for a specific key, so if both operations are present, we can only do trial and error
|
2007-12-07 09:46:21 +00:00
|
|
|
*
|
2012-11-09 09:57:37 +00:00
|
|
|
* The Algorithm IDs for RSA_SIG are 0x86 and 0x88, those for RSA_PURE_SIG 0x8c and 0x8a
|
|
|
|
* (According to http://www.opensc-project.org/pipermail/opensc-devel/2010-September/014912.html
|
|
|
|
* and www.crysys.hu/infsec/M40_Manual_E_2001_10.pdf)
|
2003-07-14 16:55:54 +00:00
|
|
|
*/
|
2007-12-07 09:46:21 +00:00
|
|
|
|
2013-05-25 02:22:28 +00:00
|
|
|
if (card->caps & SC_CARD_CAP_ONLY_RAW_HASH_STRIPPED){
|
2012-12-16 10:35:23 +00:00
|
|
|
sc_log(ctx, "Forcing RAW_HASH_STRIPPED");
|
|
|
|
do_rsa_sig = 1;
|
|
|
|
}
|
|
|
|
else if (card->caps & SC_CARD_CAP_ONLY_RAW_HASH){
|
|
|
|
sc_log(ctx, "Forcing RAW_HASH");
|
|
|
|
do_rsa_sig = 1;
|
|
|
|
}
|
|
|
|
else {
|
2013-05-25 02:22:28 +00:00
|
|
|
/* check the the algorithmIDs from the AlgorithmInfo */
|
2014-02-25 08:07:09 +00:00
|
|
|
size_t i;
|
2012-12-16 10:35:23 +00:00
|
|
|
for(i=0; i<algorithm_ids_in_tokeninfo_count;++i){
|
|
|
|
unsigned int id = algorithm_ids_in_tokeninfo[i];
|
|
|
|
if(id == 0x86 || id == 0x88)
|
|
|
|
do_rsa_sig = 1;
|
|
|
|
else if(id == 0x8C || id == 0x8A)
|
|
|
|
do_rsa_pure_sig = 1;
|
2007-02-02 22:15:14 +00:00
|
|
|
}
|
2003-07-14 16:55:54 +00:00
|
|
|
}
|
2007-12-07 09:46:21 +00:00
|
|
|
|
2013-05-25 02:22:28 +00:00
|
|
|
/* check if any operation was selected */
|
2012-12-16 10:35:23 +00:00
|
|
|
if(do_rsa_sig == 0 && do_rsa_pure_sig == 0) {
|
2013-05-25 02:22:28 +00:00
|
|
|
/* no operation selected. we just have to try both, for the lack of any better reasoning */
|
|
|
|
sc_log(ctx, "I was unable to determine, whether this key can be used with RSA_SIG or RSA_PURE_SIG. I will just try both.");
|
2012-12-16 10:35:23 +00:00
|
|
|
do_rsa_sig = 1;
|
|
|
|
do_rsa_pure_sig = 1;
|
2007-12-07 09:46:21 +00:00
|
|
|
}
|
|
|
|
|
2012-11-09 09:57:37 +00:00
|
|
|
if(do_rsa_pure_sig == 1){
|
2012-12-16 10:35:23 +00:00
|
|
|
sc_log(ctx, "trying RSA_PURE_SIG (padded DigestInfo)");
|
2007-12-07 09:46:21 +00:00
|
|
|
r = do_compute_signature(card, data, datalen, out, outlen);
|
|
|
|
if (r >= SC_SUCCESS)
|
2012-12-16 10:35:23 +00:00
|
|
|
LOG_FUNC_RETURN(ctx, r);
|
|
|
|
}
|
|
|
|
|
2012-11-09 09:57:37 +00:00
|
|
|
if(do_rsa_sig == 1){
|
2012-12-16 10:35:23 +00:00
|
|
|
sc_log(ctx, "trying RSA_SIG (just the DigestInfo)");
|
|
|
|
/* remove padding: first try pkcs1 bt01 padding */
|
2013-05-22 10:10:26 +00:00
|
|
|
r = sc_pkcs1_strip_01_padding(ctx, data, datalen, buf, &tmp_len);
|
2012-12-16 10:35:23 +00:00
|
|
|
if (r != SC_SUCCESS) {
|
|
|
|
const u8 *p = data;
|
|
|
|
/* no pkcs1 bt01 padding => let's try zero padding
|
|
|
|
* This can only work if the data tbs doesn't have a
|
|
|
|
* leading 0 byte. */
|
|
|
|
tmp_len = buf_len;
|
|
|
|
while (*p == 0 && tmp_len != 0) {
|
|
|
|
++p;
|
|
|
|
--tmp_len;
|
|
|
|
}
|
|
|
|
memcpy(buf, p, tmp_len);
|
|
|
|
}
|
|
|
|
if (!(card->caps & (SC_CARD_CAP_ONLY_RAW_HASH_STRIPPED | SC_CARD_CAP_ONLY_RAW_HASH)) || card->caps & SC_CARD_CAP_ONLY_RAW_HASH ) {
|
|
|
|
sc_log(ctx, "trying to sign raw hash value with prefix");
|
|
|
|
r = do_compute_signature(card, buf, tmp_len, out, outlen);
|
|
|
|
if (r >= SC_SUCCESS)
|
|
|
|
LOG_FUNC_RETURN(ctx, r);
|
|
|
|
}
|
|
|
|
if (card->caps & SC_CARD_CAP_ONLY_RAW_HASH) {
|
|
|
|
sc_log(ctx, "Failed to sign raw hash value with prefix when forcing");
|
|
|
|
LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
|
|
|
|
}
|
|
|
|
sc_log(ctx, "trying to sign stripped raw hash value (card is responsible for prefix)");
|
|
|
|
r = sc_pkcs1_strip_digest_info_prefix(NULL,buf,tmp_len,buf,&buf_len);
|
|
|
|
if (r != SC_SUCCESS)
|
|
|
|
LOG_FUNC_RETURN(ctx, r);
|
|
|
|
return do_compute_signature(card, buf, buf_len, out, outlen);
|
2007-12-07 09:46:21 +00:00
|
|
|
}
|
2013-05-25 02:22:28 +00:00
|
|
|
|
|
|
|
LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL);
|
2003-07-14 16:55:54 +00:00
|
|
|
}
|
2002-06-11 18:14:25 +00:00
|
|
|
|
2003-04-11 11:47:41 +00:00
|
|
|
static int
|
2006-01-23 21:43:01 +00:00
|
|
|
cardos_lifecycle_get(sc_card_t *card, int *mode)
|
2003-04-11 11:47:41 +00:00
|
|
|
{
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_apdu_t apdu;
|
2003-04-11 11:47:41 +00:00
|
|
|
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
|
|
|
|
int r;
|
|
|
|
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
2003-04-11 11:47:41 +00:00
|
|
|
|
2005-12-28 19:38:55 +00:00
|
|
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x83);
|
2003-04-11 11:47:41 +00:00
|
|
|
apdu.cla = 0x00;
|
|
|
|
apdu.le = 256;
|
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
apdu.resp = rbuf;
|
|
|
|
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
|
2003-04-11 11:47:41 +00:00
|
|
|
|
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error");
|
2003-04-11 11:47:41 +00:00
|
|
|
|
|
|
|
if (apdu.resplen < 1) {
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Lifecycle byte not in response");
|
2003-04-11 11:47:41 +00:00
|
|
|
}
|
|
|
|
|
2003-09-03 18:55:53 +00:00
|
|
|
r = SC_SUCCESS;
|
|
|
|
switch (rbuf[0]) {
|
|
|
|
case 0x10:
|
2003-04-11 11:47:41 +00:00
|
|
|
*mode = SC_CARDCTRL_LIFECYCLE_USER;
|
2003-09-03 18:55:53 +00:00
|
|
|
break;
|
|
|
|
case 0x20:
|
|
|
|
*mode = SC_CARDCTRL_LIFECYCLE_ADMIN;
|
|
|
|
break;
|
|
|
|
case 0x34: /* MANUFACTURING */
|
|
|
|
*mode = SC_CARDCTRL_LIFECYCLE_OTHER;
|
|
|
|
break;
|
|
|
|
default:
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unknown lifecycle byte %d", rbuf[0]);
|
2003-09-03 18:55:53 +00:00
|
|
|
r = SC_ERROR_INTERNAL;
|
2003-04-11 11:47:41 +00:00
|
|
|
}
|
|
|
|
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
|
2003-04-11 11:47:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2006-01-23 21:43:01 +00:00
|
|
|
cardos_lifecycle_set(sc_card_t *card, int *mode)
|
2003-04-11 11:47:41 +00:00
|
|
|
{
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_apdu_t apdu;
|
2003-04-11 11:47:41 +00:00
|
|
|
int r;
|
|
|
|
|
|
|
|
int current;
|
|
|
|
int target;
|
|
|
|
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
2003-04-11 11:47:41 +00:00
|
|
|
|
|
|
|
target = *mode;
|
|
|
|
|
2006-01-23 21:43:01 +00:00
|
|
|
r = cardos_lifecycle_get(card, ¤t);
|
2003-04-11 11:47:41 +00:00
|
|
|
|
|
|
|
if (r != SC_SUCCESS)
|
|
|
|
return r;
|
|
|
|
|
2003-09-03 18:55:53 +00:00
|
|
|
if (current == target || current == SC_CARDCTRL_LIFECYCLE_OTHER)
|
2003-04-11 11:47:41 +00:00
|
|
|
return SC_SUCCESS;
|
|
|
|
|
|
|
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x10, 0, 0);
|
|
|
|
apdu.cla = 0x80;
|
|
|
|
apdu.le = 0;
|
|
|
|
apdu.resplen = 0;
|
|
|
|
apdu.resp = NULL;
|
|
|
|
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
|
2003-04-11 11:47:41 +00:00
|
|
|
|
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error");
|
2003-04-11 11:47:41 +00:00
|
|
|
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
|
2003-04-11 11:47:41 +00:00
|
|
|
}
|
|
|
|
|
2002-06-04 08:50:17 +00:00
|
|
|
static int
|
2006-01-23 21:43:01 +00:00
|
|
|
cardos_put_data_oci(sc_card_t *card,
|
|
|
|
struct sc_cardctl_cardos_obj_info *args)
|
2002-06-04 08:50:17 +00:00
|
|
|
{
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_apdu_t apdu;
|
2002-06-04 08:50:17 +00:00
|
|
|
int r;
|
|
|
|
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
2002-06-11 18:14:25 +00:00
|
|
|
|
2002-06-04 08:50:17 +00:00
|
|
|
memset(&apdu, 0, sizeof(apdu));
|
|
|
|
apdu.cse = SC_APDU_CASE_3_SHORT;
|
|
|
|
apdu.cla = 0x00;
|
|
|
|
apdu.ins = 0xda;
|
|
|
|
apdu.p1 = 0x01;
|
|
|
|
apdu.p2 = 0x6e;
|
|
|
|
apdu.lc = args->len;
|
|
|
|
apdu.data = args->data;
|
|
|
|
apdu.datalen = args->len;
|
|
|
|
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
|
2002-06-04 08:50:17 +00:00
|
|
|
|
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error");
|
2002-06-04 08:50:17 +00:00
|
|
|
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
|
2002-06-11 18:14:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2006-01-23 21:43:01 +00:00
|
|
|
cardos_put_data_seci(sc_card_t *card,
|
|
|
|
struct sc_cardctl_cardos_obj_info *args)
|
2002-06-11 18:14:25 +00:00
|
|
|
{
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_apdu_t apdu;
|
2002-06-11 18:14:25 +00:00
|
|
|
int r;
|
|
|
|
|
|
|
|
memset(&apdu, 0, sizeof(apdu));
|
|
|
|
apdu.cse = SC_APDU_CASE_3_SHORT;
|
|
|
|
apdu.cla = 0x00;
|
|
|
|
apdu.ins = 0xda;
|
|
|
|
apdu.p1 = 0x01;
|
|
|
|
apdu.p2 = 0x6d;
|
|
|
|
apdu.lc = args->len;
|
|
|
|
apdu.data = args->data;
|
|
|
|
apdu.datalen = args->len;
|
|
|
|
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
|
2002-06-11 18:14:25 +00:00
|
|
|
|
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error");
|
2002-06-11 18:14:25 +00:00
|
|
|
|
2002-06-04 08:50:17 +00:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2002-06-06 09:17:52 +00:00
|
|
|
static int
|
2006-01-23 21:43:01 +00:00
|
|
|
cardos_generate_key(sc_card_t *card,
|
|
|
|
struct sc_cardctl_cardos_genkey_info *args)
|
2002-06-06 09:17:52 +00:00
|
|
|
{
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_apdu_t apdu;
|
2002-06-06 09:17:52 +00:00
|
|
|
u8 data[8];
|
|
|
|
int r;
|
|
|
|
|
|
|
|
data[0] = 0x20; /* store as PSO object */
|
|
|
|
data[1] = args->key_id;
|
|
|
|
data[2] = args->fid >> 8;
|
|
|
|
data[3] = args->fid & 0xff;
|
|
|
|
data[4] = 0; /* additional Rabin Miller tests */
|
2002-06-11 18:14:25 +00:00
|
|
|
data[5] = 0x10; /* length difference between p, q (bits) */
|
2002-06-06 09:17:52 +00:00
|
|
|
data[6] = 0; /* default length of exponent, MSB */
|
2002-06-11 18:14:25 +00:00
|
|
|
data[7] = 0x20; /* default length of exponent, LSB */
|
2002-06-06 09:17:52 +00:00
|
|
|
|
|
|
|
memset(&apdu, 0, sizeof(apdu));
|
|
|
|
apdu.cse = SC_APDU_CASE_3_SHORT;
|
|
|
|
apdu.cla = 0x00;
|
|
|
|
apdu.ins = 0x46;
|
|
|
|
apdu.p1 = 0x00;
|
2002-06-11 18:14:25 +00:00
|
|
|
apdu.p2 = 0x00;
|
2002-06-06 09:17:52 +00:00
|
|
|
apdu.data= data;
|
|
|
|
apdu.datalen = apdu.lc = sizeof(data);
|
|
|
|
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
|
2002-06-06 09:17:52 +00:00
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "GENERATE_KEY failed");
|
2002-06-06 09:17:52 +00:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2005-12-28 19:38:55 +00:00
|
|
|
static int cardos_get_serialnr(sc_card_t *card, sc_serial_number_t *serial)
|
2004-07-19 16:51:41 +00:00
|
|
|
{
|
|
|
|
int r;
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_apdu_t apdu;
|
2004-07-19 16:51:41 +00:00
|
|
|
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
|
|
|
|
|
|
|
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x81);
|
|
|
|
apdu.resp = rbuf;
|
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
apdu.le = 256;
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
|
2004-07-19 16:51:41 +00:00
|
|
|
if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00)
|
|
|
|
return SC_ERROR_INTERNAL;
|
|
|
|
if (apdu.resplen != 32) {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unexpected response to GET DATA serial"
|
2004-07-19 16:51:41 +00:00
|
|
|
" number\n");
|
|
|
|
return SC_ERROR_INTERNAL;
|
|
|
|
}
|
|
|
|
/* cache serial number */
|
|
|
|
memcpy(card->serialnr.value, &rbuf[10], 6);
|
|
|
|
card->serialnr.len = 6;
|
|
|
|
/* copy and return serial number */
|
|
|
|
memcpy(serial, &card->serialnr, sizeof(*serial));
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2002-06-04 08:50:17 +00:00
|
|
|
static int
|
2006-01-23 21:43:01 +00:00
|
|
|
cardos_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr)
|
2002-06-04 08:50:17 +00:00
|
|
|
{
|
|
|
|
switch (cmd) {
|
2006-01-23 21:43:01 +00:00
|
|
|
case SC_CARDCTL_CARDOS_PUT_DATA_FCI:
|
2002-06-04 08:50:17 +00:00
|
|
|
break;
|
2006-01-23 21:43:01 +00:00
|
|
|
case SC_CARDCTL_CARDOS_PUT_DATA_OCI:
|
|
|
|
return cardos_put_data_oci(card,
|
|
|
|
(struct sc_cardctl_cardos_obj_info *) ptr);
|
2002-06-11 18:14:25 +00:00
|
|
|
break;
|
2006-01-23 21:43:01 +00:00
|
|
|
case SC_CARDCTL_CARDOS_PUT_DATA_SECI:
|
|
|
|
return cardos_put_data_seci(card,
|
|
|
|
(struct sc_cardctl_cardos_obj_info *) ptr);
|
2002-06-04 08:50:17 +00:00
|
|
|
break;
|
2006-01-23 21:43:01 +00:00
|
|
|
case SC_CARDCTL_CARDOS_GENERATE_KEY:
|
|
|
|
return cardos_generate_key(card,
|
|
|
|
(struct sc_cardctl_cardos_genkey_info *) ptr);
|
2003-04-11 11:47:41 +00:00
|
|
|
case SC_CARDCTL_LIFECYCLE_GET:
|
2006-01-23 21:43:01 +00:00
|
|
|
return cardos_lifecycle_get(card, (int *) ptr);
|
2003-04-11 11:47:41 +00:00
|
|
|
case SC_CARDCTL_LIFECYCLE_SET:
|
2006-01-23 21:43:01 +00:00
|
|
|
return cardos_lifecycle_set(card, (int *) ptr);
|
2004-07-19 16:51:41 +00:00
|
|
|
case SC_CARDCTL_GET_SERIALNR:
|
2005-12-28 19:38:55 +00:00
|
|
|
return cardos_get_serialnr(card, (sc_serial_number_t *)ptr);
|
2002-06-04 08:50:17 +00:00
|
|
|
}
|
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
|
2002-12-23 18:47:27 +00:00
|
|
|
/*
|
|
|
|
* The 0x80 thing tells the card it's okay to search parent
|
|
|
|
* directories as well for the referenced object.
|
|
|
|
* Unfortunately, it doesn't seem to work without this flag :-/
|
|
|
|
*/
|
|
|
|
static int
|
2013-08-03 19:46:44 +00:00
|
|
|
cardos_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data,
|
2002-12-23 18:47:27 +00:00
|
|
|
int *tries_left)
|
|
|
|
{
|
2013-08-03 19:46:44 +00:00
|
|
|
struct sc_context *ctx = card->ctx;
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
LOG_FUNC_CALLED(card->ctx);
|
|
|
|
|
2002-12-23 18:47:27 +00:00
|
|
|
data->flags |= SC_PIN_CMD_NEED_PADDING;
|
|
|
|
data->pin_reference |= 0x80;
|
2013-08-03 19:46:44 +00:00
|
|
|
|
|
|
|
sc_log(ctx, "PIN_CMD(cmd:%i, ref:%i)", data->cmd, data->pin_reference);
|
|
|
|
sc_log(ctx, "PIN1(max:%i, min:%i)", data->pin1.max_length, data->pin1.min_length);
|
|
|
|
sc_log(ctx, "PIN2(max:%i, min:%i)", data->pin2.max_length, data->pin2.min_length);
|
|
|
|
|
2002-12-23 18:47:27 +00:00
|
|
|
/* FIXME: the following values depend on what pin length was
|
|
|
|
* used when creating the BS objects */
|
2003-12-19 09:56:57 +00:00
|
|
|
if (data->pin1.max_length == 0)
|
|
|
|
data->pin1.max_length = 8;
|
|
|
|
if (data->pin2.max_length == 0)
|
|
|
|
data->pin2.max_length = 8;
|
2013-08-03 19:46:44 +00:00
|
|
|
|
|
|
|
rv = iso_ops->pin_cmd(card, data, tries_left);
|
|
|
|
LOG_FUNC_RETURN(ctx, rv);
|
2002-12-23 18:47:27 +00:00
|
|
|
}
|
|
|
|
|
2013-08-03 19:46:44 +00:00
|
|
|
|
|
|
|
static int
|
|
|
|
cardos_logout(sc_card_t *card)
|
2005-12-28 19:38:55 +00:00
|
|
|
{
|
2006-06-17 12:24:04 +00:00
|
|
|
if (card->type == SC_CARD_TYPE_CARDOS_M4_01 ||
|
|
|
|
card->type == SC_CARD_TYPE_CARDOS_M4_2) {
|
2005-12-28 19:38:55 +00:00
|
|
|
sc_apdu_t apdu;
|
|
|
|
int r;
|
|
|
|
sc_path_t path;
|
|
|
|
|
|
|
|
sc_format_path("3F00", &path);
|
|
|
|
r = sc_select_file(card, &path, NULL);
|
|
|
|
if (r != SC_SUCCESS)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xEA, 0x00, 0x00);
|
|
|
|
apdu.cla = 0x80;
|
|
|
|
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
|
2005-12-28 19:38:55 +00:00
|
|
|
|
|
|
|
return sc_check_sw(card, apdu.sw1, apdu.sw2);
|
|
|
|
} else
|
2006-06-17 12:24:04 +00:00
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
2005-12-28 19:38:55 +00:00
|
|
|
}
|
|
|
|
|
2002-04-17 13:36:35 +00:00
|
|
|
/* eToken R2 supports WRITE_BINARY, PRO Tokens support UPDATE_BINARY */
|
|
|
|
|
2003-02-20 12:51:07 +00:00
|
|
|
static struct sc_card_driver * sc_get_driver(void)
|
2002-04-10 23:10:47 +00:00
|
|
|
{
|
2002-04-17 13:36:35 +00:00
|
|
|
if (iso_ops == NULL)
|
|
|
|
iso_ops = sc_get_iso7816_driver()->ops;
|
2006-01-23 21:43:01 +00:00
|
|
|
cardos_ops = *iso_ops;
|
|
|
|
cardos_ops.match_card = cardos_match_card;
|
|
|
|
cardos_ops.init = cardos_init;
|
|
|
|
cardos_ops.select_file = cardos_select_file;
|
|
|
|
cardos_ops.create_file = cardos_create_file;
|
|
|
|
cardos_ops.set_security_env = cardos_set_security_env;
|
|
|
|
cardos_ops.restore_security_env = cardos_restore_security_env;
|
|
|
|
cardos_ops.compute_signature = cardos_compute_signature;
|
|
|
|
|
|
|
|
cardos_ops.list_files = cardos_list_files;
|
|
|
|
cardos_ops.check_sw = cardos_check_sw;
|
|
|
|
cardos_ops.card_ctl = cardos_card_ctl;
|
|
|
|
cardos_ops.pin_cmd = cardos_pin_cmd;
|
|
|
|
cardos_ops.logout = cardos_logout;
|
|
|
|
|
|
|
|
return &cardos_drv;
|
2002-04-10 23:10:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#if 1
|
2006-01-23 21:43:01 +00:00
|
|
|
struct sc_card_driver * sc_get_cardos_driver(void)
|
2002-04-10 23:10:47 +00:00
|
|
|
{
|
|
|
|
return sc_get_driver();
|
|
|
|
}
|
|
|
|
#endif
|