2003-04-11 13:55:22 +00:00
|
|
|
/*
|
2008-09-18 17:44:54 +00:00
|
|
|
* cardos-tool.c: Tool and Info about Card OS based tokens
|
2003-04-11 13:55:22 +00:00
|
|
|
*
|
2008-09-18 17:44:54 +00:00
|
|
|
* Copyright (C) 2008 Andreas Jellinghaus <aj@dungeon.inka.de>
|
|
|
|
* Copyright (C) 2007 Jean-Pierre Szikora <jean-pierre.szikora@uclouvain.be>
|
2003-04-11 13:55:22 +00:00
|
|
|
* Copyright (C) 2003 Andreas Jellinghaus <aj@dungeon.inka.de>
|
2006-12-19 21:35:42 +00:00
|
|
|
* Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
|
2003-04-11 13:55:22 +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
|
|
|
|
*/
|
|
|
|
|
2010-03-04 08:14:36 +00:00
|
|
|
#include "config.h"
|
|
|
|
|
2003-04-11 13:55:22 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <sys/stat.h>
|
2008-09-18 17:44:54 +00:00
|
|
|
|
|
|
|
#ifdef ENABLE_OPENSSL
|
|
|
|
#include <openssl/des.h>
|
2009-12-01 21:10:06 +00:00
|
|
|
#include <openssl/sha.h>
|
2008-09-18 17:44:54 +00:00
|
|
|
#endif
|
|
|
|
|
2010-03-04 08:14:36 +00:00
|
|
|
#include "libopensc/opensc.h"
|
2020-01-06 17:03:19 +00:00
|
|
|
#include "libopensc/cards.h"
|
2003-04-11 13:55:22 +00:00
|
|
|
#include "util.h"
|
|
|
|
|
2008-09-18 17:44:54 +00:00
|
|
|
static const char *app_name = "cardos-tool";
|
2003-04-11 13:55:22 +00:00
|
|
|
|
2010-07-26 11:18:00 +00:00
|
|
|
static int opt_wait = 0;
|
2007-06-21 12:58:57 +00:00
|
|
|
static int verbose = 0;
|
2010-01-24 15:29:47 +00:00
|
|
|
static char *opt_reader = NULL;
|
2003-04-11 13:55:22 +00:00
|
|
|
|
2007-06-29 13:19:19 +00:00
|
|
|
static const struct option options[] = {
|
2010-07-26 11:18:00 +00:00
|
|
|
{"help", 0, NULL, 'h'},
|
2008-09-18 17:44:54 +00:00
|
|
|
{"info", 0, NULL, 'i'},
|
|
|
|
{"format", 0, NULL, 'f'},
|
|
|
|
{"startkey", 1, NULL, 's'},
|
2009-12-01 21:10:06 +00:00
|
|
|
{"change-startkey", 1, NULL, 'S'},
|
2007-06-21 12:58:57 +00:00
|
|
|
{"reader", 1, NULL, 'r'},
|
|
|
|
{"wait", 0, NULL, 'w'},
|
|
|
|
{"verbose", 0, NULL, 'v'},
|
|
|
|
{NULL, 0, NULL, 0}
|
2003-04-11 13:55:22 +00:00
|
|
|
};
|
|
|
|
|
2007-06-29 13:19:19 +00:00
|
|
|
static const char *option_help[] = {
|
2010-07-26 11:18:00 +00:00
|
|
|
"Print this help message",
|
2008-09-18 17:44:54 +00:00
|
|
|
"Print information about this card",
|
|
|
|
"Format this card erasing all content",
|
|
|
|
"Specify startkey for format",
|
2009-12-02 11:10:16 +00:00
|
|
|
"Change Startkey with given APDU command",
|
2003-04-11 13:55:22 +00:00
|
|
|
"Uses reader number <arg> [0]",
|
|
|
|
"Wait for a card to be inserted",
|
2004-06-13 20:13:12 +00:00
|
|
|
"Verbose operation. Use several times to enable debug output.",
|
2003-04-11 13:55:22 +00:00
|
|
|
};
|
|
|
|
|
2007-06-21 12:58:57 +00:00
|
|
|
static sc_context_t *ctx = NULL;
|
|
|
|
static sc_card_t *card = NULL;
|
2003-04-11 13:55:22 +00:00
|
|
|
|
2014-11-10 17:24:56 +00:00
|
|
|
static int check_apdu(const sc_apdu_t *apdu)
|
|
|
|
{
|
2014-12-07 22:51:25 +00:00
|
|
|
if (apdu->sw1 != 0x90 || apdu->sw2 != 0x00) {
|
|
|
|
fprintf(stderr, "Some error occurred. Use '-v' several times to enable debug output.");
|
|
|
|
return 1;
|
2014-11-10 17:24:56 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-06-21 13:38:16 +00:00
|
|
|
static int cardos_info(void)
|
2003-04-11 13:55:22 +00:00
|
|
|
{
|
2005-03-09 00:04:44 +00:00
|
|
|
sc_apdu_t apdu;
|
2003-04-11 13:55:22 +00:00
|
|
|
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
|
2012-12-02 18:21:08 +00:00
|
|
|
int is_cardos5 = 0;
|
2003-04-11 13:55:22 +00:00
|
|
|
int r;
|
|
|
|
|
2007-08-08 20:06:53 +00:00
|
|
|
if (verbose) {
|
|
|
|
printf("Card ATR:\n");
|
2012-04-02 22:00:56 +00:00
|
|
|
util_hex_dump_asc(stdout, card->atr.value, card->atr.len, -1);
|
2007-08-08 20:06:53 +00:00
|
|
|
} else {
|
|
|
|
char tmp[SC_MAX_ATR_SIZE*3];
|
2011-01-07 17:18:58 +00:00
|
|
|
sc_bin_to_hex(card->atr.value, card->atr.len, tmp, sizeof(tmp) - 1, ':');
|
2007-08-08 20:06:53 +00:00
|
|
|
fprintf(stdout,"%s\n",tmp);
|
|
|
|
}
|
|
|
|
|
2003-04-11 13:55:22 +00:00
|
|
|
memset(&apdu, 0, sizeof(apdu));
|
|
|
|
apdu.cla = 0x00;
|
|
|
|
apdu.ins = 0xca;
|
|
|
|
apdu.p1 = 0x01;
|
|
|
|
apdu.p2 = 0x80;
|
|
|
|
apdu.resp = rbuf;
|
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
apdu.lc = 0;
|
|
|
|
apdu.le = 256;
|
|
|
|
apdu.cse = SC_APDU_CASE_2_SHORT;
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
if (check_apdu(&apdu))
|
2003-04-11 13:55:22 +00:00
|
|
|
return 1;
|
|
|
|
printf("Info : %s\n", apdu.resp);
|
|
|
|
|
2012-12-02 18:21:08 +00:00
|
|
|
apdu.p2 = 0x82;
|
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
if (check_apdu(&apdu))
|
2012-12-02 18:21:08 +00:00
|
|
|
return 1;
|
|
|
|
if (apdu.resp[0] == 0xc9)
|
|
|
|
is_cardos5 = 1;
|
2014-11-10 17:24:56 +00:00
|
|
|
|
2003-04-11 13:55:22 +00:00
|
|
|
apdu.p2 = 0x81;
|
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
if (check_apdu(&apdu))
|
2003-04-11 13:55:22 +00:00
|
|
|
return 1;
|
2012-12-02 18:21:08 +00:00
|
|
|
if (is_cardos5) {
|
2014-11-10 17:24:56 +00:00
|
|
|
printf("Serial number: %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
2012-12-02 18:21:08 +00:00
|
|
|
apdu.resp[0], apdu.resp[1], apdu.resp[2], apdu.resp[3],
|
|
|
|
apdu.resp[4], apdu.resp[5], apdu.resp[6], apdu.resp[7]);
|
|
|
|
} else {
|
|
|
|
printf("Chip type: %d\n", apdu.resp[8]);
|
|
|
|
printf("Serial number: %02x %02x %02x %02x %02x %02x\n",
|
|
|
|
apdu.resp[10], apdu.resp[11], apdu.resp[12],
|
|
|
|
apdu.resp[13], apdu.resp[14], apdu.resp[15]);
|
|
|
|
printf("Full prom dump:\n");
|
|
|
|
if (apdu.resplen)
|
|
|
|
util_hex_dump_asc(stdout, apdu.resp, apdu.resplen, -1);
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
|
2003-04-11 13:55:22 +00:00
|
|
|
apdu.p2 = 0x82;
|
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
if (check_apdu(&apdu))
|
2003-04-11 13:55:22 +00:00
|
|
|
return 1;
|
|
|
|
printf("OS Version: %d.%d", apdu.resp[0], apdu.resp[1]);
|
2005-11-01 22:34:22 +00:00
|
|
|
if (apdu.resp[0] == 0xc8 && apdu.resp[1] == 0x02) {
|
2003-04-11 13:55:22 +00:00
|
|
|
printf(" (that's CardOS M4.0)\n");
|
2005-11-01 22:34:22 +00:00
|
|
|
} else if (apdu.resp[0] == 0xc8 && apdu.resp[1] == 0x03) {
|
2003-04-11 13:55:22 +00:00
|
|
|
printf(" (that's CardOS M4.01)\n");
|
2005-11-01 22:34:22 +00:00
|
|
|
} else if (apdu.resp[0] == 0xc8 && apdu.resp[1] == 0x04) {
|
|
|
|
printf(" (that's CardOS M4.01a)\n");
|
2005-12-16 20:52:01 +00:00
|
|
|
} else if (apdu.resp[0] == 0xc8 && apdu.resp[1] == 0x06) {
|
|
|
|
printf(" (that's CardOS M4.2)\n");
|
2006-01-26 19:02:02 +00:00
|
|
|
} else if (apdu.resp[0] == 0xc8 && apdu.resp[1] == 0x07) {
|
|
|
|
printf(" (that's CardOS M4.3)\n");
|
2005-11-01 22:34:22 +00:00
|
|
|
} else if (apdu.resp[0] == 0xc8 && apdu.resp[1] == 0x08) {
|
2008-09-18 17:44:54 +00:00
|
|
|
printf(" (that's CardOS M4.3B)\n");
|
2007-08-08 20:06:53 +00:00
|
|
|
} else if (apdu.resp[0] == 0xc8 && apdu.resp[1] == 0x09) {
|
2008-09-18 17:44:54 +00:00
|
|
|
printf(" (that's CardOS M4.2B)\n");
|
2007-12-19 09:58:29 +00:00
|
|
|
} else if (apdu.resp[0] == 0xc8 && apdu.resp[1] == 0x0B) {
|
2012-04-02 22:00:56 +00:00
|
|
|
printf(" (that's CardOS M4.2C)\n");
|
2010-04-21 14:38:23 +00:00
|
|
|
} else if (apdu.resp[0] == 0xc8 && apdu.resp[1] == 0x0D) {
|
2012-04-02 22:00:56 +00:00
|
|
|
printf(" (that's CardOS M4.4)\n");
|
2012-12-02 18:21:08 +00:00
|
|
|
} else if (apdu.resp[0] == 0xc9 && apdu.resp[1] == 0x01) {
|
|
|
|
printf(" (that's CardOS V5.0)\n");
|
2017-08-22 12:10:02 +00:00
|
|
|
} else if (apdu.resp[0] == 0xc9 &&
|
|
|
|
(apdu.resp[1] == 0x02 || apdu.resp[1] == 0x03)) {
|
|
|
|
printf(" (that's CardOS V5.3)\n");
|
2003-04-11 13:55:22 +00:00
|
|
|
} else {
|
|
|
|
printf(" (unknown Version)\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
apdu.p2 = 0x83;
|
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
if (check_apdu(&apdu))
|
2003-04-11 13:55:22 +00:00
|
|
|
return 1;
|
2003-04-15 20:06:19 +00:00
|
|
|
|
|
|
|
printf("Current life cycle: ");
|
|
|
|
if (rbuf[0] == 0x34) {
|
|
|
|
printf("%d (manufacturing)\n", rbuf[0]);
|
|
|
|
} else if (rbuf[0] == 0x26) {
|
2012-12-02 18:21:08 +00:00
|
|
|
if (is_cardos5)
|
|
|
|
printf("%d (physinit)\n", rbuf[0]);
|
|
|
|
else
|
|
|
|
printf("%d (initialization)\n", rbuf[0]);
|
|
|
|
} else if (rbuf[0] == 0x23) {
|
|
|
|
printf("%d (physpers)\n", rbuf[0]);
|
2003-04-15 20:06:19 +00:00
|
|
|
} else if (rbuf[0] == 0x24) {
|
|
|
|
printf("%d (personalization)\n", rbuf[0]);
|
|
|
|
} else if (rbuf[0] == 0x20) {
|
|
|
|
printf("%d (administration)\n", rbuf[0]);
|
|
|
|
} else if (rbuf[0] == 0x10) {
|
|
|
|
printf("%d (operational)\n", rbuf[0]);
|
2008-09-18 17:44:54 +00:00
|
|
|
} else if (rbuf[0] == 0x29) {
|
|
|
|
printf("%d (erase in progress)\n", rbuf[0]);
|
2012-12-02 18:21:08 +00:00
|
|
|
} else if (rbuf[0] == 0x3F) {
|
|
|
|
printf("%d (death)\n", rbuf[0]);
|
2003-04-15 20:06:19 +00:00
|
|
|
} else {
|
|
|
|
printf("%d (unknown)\n", rbuf[0]);
|
|
|
|
}
|
2003-04-11 13:55:22 +00:00
|
|
|
|
|
|
|
apdu.p2 = 0x84;
|
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
if (check_apdu(&apdu))
|
2003-04-11 13:55:22 +00:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
printf("Security Status of current DF:\n");
|
2008-03-06 16:06:59 +00:00
|
|
|
util_hex_dump_asc(stdout, apdu.resp, apdu.resplen, -1);
|
2003-04-11 13:55:22 +00:00
|
|
|
|
|
|
|
apdu.p2 = 0x85;
|
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
if (check_apdu(&apdu))
|
2003-04-11 13:55:22 +00:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
printf("Free memory : %d\n", rbuf[0]<<8|rbuf[1]);
|
|
|
|
|
|
|
|
apdu.p2 = 0x86;
|
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
if (check_apdu(&apdu))
|
2003-04-11 13:55:22 +00:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (rbuf[0] == 0x00) {
|
|
|
|
printf("ATR Status: 0x%d ROM-ATR\n",rbuf[0]);
|
2012-12-03 09:19:53 +00:00
|
|
|
} else if (rbuf[0] == 0x80) {
|
2003-04-11 13:55:22 +00:00
|
|
|
printf("ATR Status: 0x%d EEPROM-ATR\n",rbuf[0]);
|
|
|
|
} else {
|
|
|
|
printf("ATR Status: 0x%d unknown\n",rbuf[0]);
|
|
|
|
}
|
2012-04-02 22:00:56 +00:00
|
|
|
|
2003-04-15 20:06:19 +00:00
|
|
|
apdu.p2 = 0x88;
|
2003-04-11 13:55:22 +00:00
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
if (check_apdu(&apdu))
|
2003-04-11 13:55:22 +00:00
|
|
|
return 1;
|
|
|
|
|
2003-04-15 20:06:19 +00:00
|
|
|
printf("Packages installed:\n");
|
2008-03-06 16:06:59 +00:00
|
|
|
util_hex_dump_asc(stdout, apdu.resp, apdu.resplen, -1);
|
2003-04-11 13:55:22 +00:00
|
|
|
|
2003-04-15 20:06:19 +00:00
|
|
|
apdu.p2 = 0x89;
|
2003-04-11 13:55:22 +00:00
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
if (check_apdu(&apdu))
|
2003-04-11 13:55:22 +00:00
|
|
|
return 1;
|
|
|
|
|
2012-12-02 18:21:08 +00:00
|
|
|
if (is_cardos5)
|
|
|
|
printf("Ram size: %d, Eeprom size: %d, cpu type: %x, chip config: %d, chip manufacturer: %d\n",
|
2014-11-10 17:24:56 +00:00
|
|
|
rbuf[0]<<8|rbuf[1], rbuf[2]<<8|rbuf[3], rbuf[4], rbuf[6], rbuf[7]);
|
2012-12-02 18:21:08 +00:00
|
|
|
else
|
|
|
|
printf("Ram size: %d, Eeprom size: %d, cpu type: %x, chip config: %d\n",
|
2003-04-15 20:06:19 +00:00
|
|
|
rbuf[0]<<8|rbuf[1], rbuf[2]<<8|rbuf[3], rbuf[4], rbuf[5]);
|
2003-04-11 13:55:22 +00:00
|
|
|
|
2003-04-15 20:06:19 +00:00
|
|
|
apdu.p2 = 0x8a;
|
2003-04-11 13:55:22 +00:00
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
if (check_apdu(&apdu))
|
2003-04-11 13:55:22 +00:00
|
|
|
return 1;
|
|
|
|
|
2012-12-02 18:21:08 +00:00
|
|
|
if (is_cardos5)
|
|
|
|
printf("Free eeprom memory: %d\n", rbuf[0]<<24|rbuf[1]<<16|rbuf[2]<<8|rbuf[3]);
|
|
|
|
else
|
|
|
|
printf("Free eeprom memory: %d\n", rbuf[0]<<8|rbuf[1]);
|
2003-04-11 13:55:22 +00:00
|
|
|
|
2013-06-21 08:46:35 +00:00
|
|
|
apdu.p2 = 0x8d;
|
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
if (check_apdu(&apdu))
|
2013-06-21 08:46:35 +00:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
printf("Current Maximum Data Field Length: %d\n", rbuf[0]<<8|rbuf[1]);
|
|
|
|
|
2012-12-02 18:21:08 +00:00
|
|
|
if (is_cardos5) {
|
|
|
|
apdu.p2 = 0x8B;
|
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
if (check_apdu(&apdu))
|
2012-12-02 18:21:08 +00:00
|
|
|
return 1;
|
2014-11-10 17:24:56 +00:00
|
|
|
|
2012-12-02 18:21:08 +00:00
|
|
|
printf("Complete chip production data:\n");
|
|
|
|
util_hex_dump_asc(stdout, apdu.resp, apdu.resplen, -1);
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
|
2003-04-15 20:06:19 +00:00
|
|
|
apdu.p2 = 0x96;
|
2003-04-11 13:55:22 +00:00
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
if (check_apdu(&apdu))
|
2003-04-11 13:55:22 +00:00
|
|
|
return 1;
|
|
|
|
|
2005-12-16 20:52:01 +00:00
|
|
|
printf("System keys: PackageLoadKey (version 0x%02x, retries %d)\n",
|
2003-04-15 20:06:19 +00:00
|
|
|
rbuf[0], rbuf[1]);
|
2005-12-16 20:52:01 +00:00
|
|
|
printf("System keys: StartKey (version 0x%02x, retries %d)\n",
|
2003-04-15 20:06:19 +00:00
|
|
|
rbuf[2], rbuf[3]);
|
2003-04-11 13:55:22 +00:00
|
|
|
|
2003-04-15 20:06:19 +00:00
|
|
|
apdu.p2 = 0x87;
|
2003-04-11 13:55:22 +00:00
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
if (check_apdu(&apdu))
|
2003-04-11 13:55:22 +00:00
|
|
|
return 1;
|
|
|
|
|
2003-04-15 20:06:19 +00:00
|
|
|
printf("Path to current DF:\n");
|
2008-03-06 16:06:59 +00:00
|
|
|
util_hex_dump_asc(stdout, apdu.resp, apdu.resplen, -1);
|
2003-04-11 13:55:22 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-09-18 17:44:54 +00:00
|
|
|
#ifdef ENABLE_OPENSSL
|
2009-12-01 21:10:06 +00:00
|
|
|
static int cardos_sm4h(const unsigned char *in, size_t inlen, unsigned char
|
|
|
|
*out, size_t outlen, const unsigned char *key, size_t keylen) {
|
2008-09-18 17:44:54 +00:00
|
|
|
/* using a buffer with an APDU, build an SM 4h APDU for cardos */
|
|
|
|
|
|
|
|
int plain_lc; /* data size in orig APDU */
|
2009-11-13 11:21:09 +00:00
|
|
|
unsigned int mac_input_len, enc_input_len;
|
2008-09-18 17:44:54 +00:00
|
|
|
unsigned char *mac_input, *enc_input;
|
|
|
|
DES_key_schedule ks_a, ks_b;
|
|
|
|
DES_cblock des_in,des_out;
|
2010-01-24 15:29:47 +00:00
|
|
|
unsigned int i,j;
|
2008-09-18 17:44:54 +00:00
|
|
|
|
|
|
|
if (keylen != 16) {
|
2016-09-28 17:35:13 +00:00
|
|
|
printf("key has wrong size, need 16 bytes, got %"SC_FORMAT_LEN_SIZE_T"d. aborting.\n",
|
|
|
|
keylen);
|
2008-09-18 17:44:54 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (inlen < 4)
|
|
|
|
return 0; /* failed, apdu too short */
|
2008-09-22 14:36:46 +00:00
|
|
|
if (inlen <= 5)
|
2008-09-18 17:44:54 +00:00
|
|
|
plain_lc = 0;
|
2008-09-22 14:36:46 +00:00
|
|
|
if (inlen > 5)
|
2008-09-18 17:44:54 +00:00
|
|
|
plain_lc = in[4];
|
2012-04-02 22:00:56 +00:00
|
|
|
|
2008-09-18 17:44:54 +00:00
|
|
|
/* 4 + plain_lc plus 0..7 bytes of padding */
|
|
|
|
mac_input_len = 4 + plain_lc;
|
|
|
|
while (mac_input_len % 8) mac_input_len++;
|
|
|
|
|
|
|
|
mac_input = calloc(1,mac_input_len);
|
|
|
|
if (!mac_input) {
|
2008-09-19 10:21:14 +00:00
|
|
|
printf("out of memory, aborting\n");
|
2008-09-18 17:44:54 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
mac_input[0] = in[1]; /* ins */
|
|
|
|
mac_input[1] = in[2]; /* p1 */
|
|
|
|
mac_input[2] = in[3]; /* p2 */
|
|
|
|
mac_input[3] = plain_lc + 8;
|
|
|
|
if (plain_lc) /* copy data from in APDU */
|
|
|
|
memcpy(&mac_input[4],&in[5],plain_lc);
|
|
|
|
/* calloc already did the ansi padding: rest is 00 */
|
2012-04-02 22:00:56 +00:00
|
|
|
|
2008-09-18 17:44:54 +00:00
|
|
|
/* prepare des key using first 8 bytes of key */
|
|
|
|
DES_set_key((const_DES_cblock*) &key[0], &ks_a);
|
|
|
|
/* prepare des key using second 8 bytes of key */
|
|
|
|
DES_set_key((const_DES_cblock*) &key[8], &ks_b);
|
|
|
|
|
|
|
|
/* first block: XOR with IV and encrypt with key A IV is 8 bytes 00 */
|
|
|
|
for (i=0; i < 8; i++) des_in[i] = mac_input[i]^00;
|
2010-03-29 12:55:13 +00:00
|
|
|
DES_ecb_encrypt(&des_in, &des_out, &ks_a, 1);
|
2008-09-18 17:44:54 +00:00
|
|
|
|
|
|
|
/* all other blocks: XOR with prev. result and encrypt with key A */
|
|
|
|
for (j=1; j < (mac_input_len / 8); j++) {
|
|
|
|
for (i=0; i < 8; i++) des_in[i] = mac_input[i+j*8]^des_out[i];
|
2010-03-29 12:55:13 +00:00
|
|
|
DES_ecb_encrypt(&des_in, &des_out, &ks_a, 1);
|
2008-09-18 17:44:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* now decrypt with key B and encrypt with key A again */
|
|
|
|
/* (a noop if key A and B are the same, e.g. 8 bytes ff */
|
|
|
|
for (i=0; i < 8; i++) des_in[i] = des_out[i];
|
2010-03-29 12:55:13 +00:00
|
|
|
DES_ecb_encrypt(&des_in, &des_out, &ks_b, 0);
|
2008-09-18 17:44:54 +00:00
|
|
|
for (i=0; i < 8; i++) des_in[i] = des_out[i];
|
2010-03-29 12:55:13 +00:00
|
|
|
DES_ecb_encrypt(&des_in, &des_out, &ks_a, 1);
|
2008-09-18 17:44:54 +00:00
|
|
|
|
|
|
|
/* now we want to enc:
|
|
|
|
* orig APDU data plus mac (8 bytes) plus iso padding (1-8 bytes) */
|
|
|
|
enc_input_len = plain_lc + 8 + 1;
|
|
|
|
while (enc_input_len % 8) enc_input_len++;
|
|
|
|
|
|
|
|
enc_input = calloc(1,enc_input_len);
|
|
|
|
if (!enc_input) {
|
2009-05-12 14:35:49 +00:00
|
|
|
free(mac_input);
|
2008-09-19 10:21:14 +00:00
|
|
|
printf("out of memory, aborting\n");
|
2008-09-18 17:44:54 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2012-04-02 22:00:56 +00:00
|
|
|
if (plain_lc)
|
2008-09-18 17:44:54 +00:00
|
|
|
memcpy(&enc_input[0],&in[5],plain_lc);
|
|
|
|
for (i=0; i < 8; i++) enc_input[i+plain_lc] = des_out[i];
|
|
|
|
enc_input[plain_lc+8] = 0x80; /* iso padding */
|
2018-04-14 17:38:34 +00:00
|
|
|
/* calloc already cleared the remaining bytes to 00 */
|
2008-09-18 17:44:54 +00:00
|
|
|
|
|
|
|
if (outlen < 5 + enc_input_len) {
|
2009-05-12 14:35:49 +00:00
|
|
|
free(mac_input);
|
|
|
|
free(enc_input);
|
2008-09-18 17:44:54 +00:00
|
|
|
printf("output buffer too small, aborting.\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
out[0] = in[0]; /* cla */
|
|
|
|
out[1] = in[1]; /* ins */
|
|
|
|
out[2] = in[2]; /* p1 */
|
|
|
|
out[3] = in[3]; /* p2 */
|
|
|
|
out[4] = enc_input_len; /* lc */
|
|
|
|
|
|
|
|
/* encrypt first block */
|
|
|
|
|
|
|
|
/* xor data and IV (8 bytes 00) to get input data */
|
|
|
|
for (i=0; i < 8; i++) des_in[i] = enc_input[i] ^ 00;
|
2012-04-02 22:00:56 +00:00
|
|
|
|
2018-04-14 17:38:34 +00:00
|
|
|
/* encrypt with des2 (triple des, but using keys A-B-A) */
|
2010-03-29 12:55:13 +00:00
|
|
|
DES_ecb2_encrypt(&des_in, &des_out, &ks_a, &ks_b, 1);
|
2008-09-18 17:44:54 +00:00
|
|
|
|
|
|
|
/* copy encrypted bytes into output */
|
|
|
|
for (i=0; i < 8; i++) out[5+i] = des_out[i];
|
|
|
|
|
2018-04-14 17:38:34 +00:00
|
|
|
/* encrypt other blocks (usually one) */
|
2008-09-18 17:44:54 +00:00
|
|
|
for (j=1; j < (enc_input_len / 8); j++) {
|
|
|
|
/* xor data and prev. result to get input data */
|
|
|
|
for (i=0; i < 8; i++) des_in[i] = enc_input[i+j*8] ^ des_out[i];
|
2012-04-02 22:00:56 +00:00
|
|
|
|
2018-04-14 17:38:34 +00:00
|
|
|
/* encrypt with des2 (triple des, but using keys A-B-A) */
|
2010-03-29 12:55:13 +00:00
|
|
|
DES_ecb2_encrypt(&des_in, &des_out, &ks_a, &ks_b, 1);
|
2008-09-18 17:44:54 +00:00
|
|
|
|
|
|
|
/* copy encrypted bytes into output */
|
|
|
|
for (i=0; i < 8; i++) out[5+8*j+i] = des_out[i];
|
|
|
|
}
|
2008-09-22 14:21:01 +00:00
|
|
|
if (verbose) {
|
|
|
|
printf ("Unencrypted APDU:\n");
|
|
|
|
util_hex_dump_asc(stdout, in, inlen, -1);
|
|
|
|
printf ("Encrypted APDU:\n");
|
|
|
|
util_hex_dump_asc(stdout, out, out[4] + 5, -1);
|
|
|
|
printf ("\n");
|
|
|
|
}
|
2009-05-12 14:35:49 +00:00
|
|
|
free(mac_input);
|
|
|
|
free(enc_input);
|
2008-09-18 17:44:54 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-12-01 21:10:06 +00:00
|
|
|
#ifdef ENABLE_OPENSSL
|
2010-04-01 11:21:57 +00:00
|
|
|
static int cardos_format(const char *opt_startkey)
|
2008-09-18 17:44:54 +00:00
|
|
|
{
|
|
|
|
unsigned const char startkey[] = {
|
|
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
2012-04-02 22:00:56 +00:00
|
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
2008-09-18 17:44:54 +00:00
|
|
|
sc_apdu_t apdu;
|
|
|
|
u8 rbuf[256];
|
|
|
|
int r;
|
2010-04-01 11:21:57 +00:00
|
|
|
|
|
|
|
if (opt_startkey) {
|
|
|
|
fprintf(stderr, "startkey option not implemented yet, aborting!\n");
|
|
|
|
return 1;
|
|
|
|
/* TODO: instead validate/parse opt_startkey into startkey */
|
|
|
|
/* format would be ii:vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv */
|
|
|
|
/* with "ii" the startkey index as hex number and */
|
|
|
|
/* "vv" the 16 byte value in hex (32 chars) */
|
|
|
|
}
|
2012-04-02 22:00:56 +00:00
|
|
|
|
2008-09-22 14:21:01 +00:00
|
|
|
if (verbose) {
|
|
|
|
printf ("StartKey:\n");
|
|
|
|
util_hex_dump_asc(stdout, startkey, 16, -1);
|
|
|
|
}
|
2012-04-02 22:00:56 +00:00
|
|
|
|
2008-09-22 14:21:01 +00:00
|
|
|
/* use GET DATA for version - 00 ca 01 82
|
|
|
|
* returns e.g. c8 09 for 4.2B
|
|
|
|
*/
|
|
|
|
|
|
|
|
memset(&apdu, 0, sizeof(apdu));
|
|
|
|
apdu.cla = 0x00;
|
|
|
|
apdu.ins = 0xca;
|
|
|
|
apdu.p1 = 0x01;
|
|
|
|
apdu.p2 = 0x82;
|
|
|
|
apdu.resp = rbuf;
|
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
apdu.lc = 0;
|
|
|
|
apdu.le = 256;
|
|
|
|
apdu.cse = SC_APDU_CASE_2_SHORT;
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
if (check_apdu(&apdu))
|
2008-09-22 14:21:01 +00:00
|
|
|
return 1;
|
|
|
|
if (apdu.resplen != 0x02) {
|
|
|
|
printf("did not receive version info, aborting\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if ((rbuf[0] != 0xc8 || rbuf[1] != 0x09) && /* M4.2B */
|
|
|
|
(rbuf[0] != 0xc8 || rbuf[1] != 0x08) && /* M4.3B */
|
2010-04-21 14:38:23 +00:00
|
|
|
(rbuf[0] != 0xc8 || rbuf[1] != 0x0B) && /* M4.2C */
|
|
|
|
(rbuf[0] != 0xc8 || rbuf[1] != 0x0D)) { /* M4.4 */
|
|
|
|
printf("currently only CardOS M4.2B, M4.2C, M4.3B and M4.4 are supported, aborting\n");
|
2008-09-22 14:21:01 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* GET DATA for startkey index - 00 ca 01 96
|
|
|
|
* returns 6 bytes PackageLoadKey.Version, PackageLoadKey.Retry
|
|
|
|
* Startkey.Version, Startkey.Retry, 2 internal data byes */
|
|
|
|
|
|
|
|
memset(&apdu, 0, sizeof(apdu));
|
|
|
|
apdu.cla = 0x00;
|
|
|
|
apdu.ins = 0xca;
|
|
|
|
apdu.p1 = 0x01;
|
|
|
|
apdu.p2 = 0x96;
|
|
|
|
apdu.resp = rbuf;
|
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
apdu.lc = 0;
|
|
|
|
apdu.le = 256;
|
|
|
|
apdu.cse = SC_APDU_CASE_2_SHORT;
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
if (check_apdu(&apdu))
|
2008-09-22 14:21:01 +00:00
|
|
|
return 1;
|
|
|
|
if (apdu.resplen < 0x04) {
|
2016-09-28 17:35:13 +00:00
|
|
|
printf("expected 4-6 bytes form GET DATA for startkey data, but got only %"SC_FORMAT_LEN_SIZE_T"u\n",
|
|
|
|
apdu.resplen);
|
2008-09-22 14:21:01 +00:00
|
|
|
printf("aborting\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2009-12-01 11:47:19 +00:00
|
|
|
if (apdu.resp[2] != 0xff) {
|
|
|
|
printf("startkey version is 0x%02x, currently we support only 0xff\n", (int) apdu.resp[2]);
|
2008-09-22 14:21:01 +00:00
|
|
|
printf("aborting\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2009-12-01 11:47:19 +00:00
|
|
|
if (apdu.resp[3] < 5) {
|
|
|
|
printf("startkey has only %d tries left. to be safe: aborting\n", apdu.resp[3]);
|
2008-09-22 14:21:01 +00:00
|
|
|
return 1;
|
2012-04-02 22:00:56 +00:00
|
|
|
}
|
2008-09-18 17:44:54 +00:00
|
|
|
|
|
|
|
/* first run GET DATA for lifecycle 00 CA 01 83
|
|
|
|
* returns 34 MANUFACTURING 20 ADMINISTRATION 10 OPERATIONAL
|
2014-11-10 17:24:56 +00:00
|
|
|
* 26 INITIALIZATION, 23 PERSONALIZATION 3f DEATH
|
2008-09-18 17:44:54 +00:00
|
|
|
* 29 ERASE IN PROGRESS
|
|
|
|
* */
|
|
|
|
|
|
|
|
memset(&apdu, 0, sizeof(apdu));
|
|
|
|
apdu.cla = 0x00;
|
|
|
|
apdu.ins = 0xca;
|
|
|
|
apdu.p1 = 0x01;
|
|
|
|
apdu.p2 = 0x83;
|
|
|
|
apdu.resp = rbuf;
|
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
apdu.lc = 0;
|
|
|
|
apdu.le = 256;
|
|
|
|
apdu.cse = SC_APDU_CASE_2_SHORT;
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
if (check_apdu(&apdu))
|
2008-09-18 17:44:54 +00:00
|
|
|
return 1;
|
2008-09-22 14:21:01 +00:00
|
|
|
|
2008-09-18 17:44:54 +00:00
|
|
|
if (apdu.resp[0] == 0x34) {
|
|
|
|
printf("card in manufacturing state\n");
|
2008-09-22 14:21:01 +00:00
|
|
|
goto erase_state;
|
2008-09-18 17:44:54 +00:00
|
|
|
|
2008-09-22 07:01:34 +00:00
|
|
|
/* we can leave manufacturing mode with FORMAT,
|
2008-09-18 17:44:54 +00:00
|
|
|
* but before we do that, we need to change the secret
|
|
|
|
* siemens start key to the default 0xff start key.
|
2014-11-10 17:24:56 +00:00
|
|
|
* we know the APDU for that, but it is secret and
|
2008-09-18 17:44:54 +00:00
|
|
|
* siemens so far didn't allow us to publish it.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
if (apdu.resp[0] != 0x10 && apdu.resp[0] != 0x20) {
|
|
|
|
printf("card is in unknown state 0x%02x, aborting\n",
|
|
|
|
(int) apdu.resp[0]);
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* we should handle ERASE IN PROGRESS (29) too */
|
|
|
|
}
|
2012-04-02 22:00:56 +00:00
|
|
|
|
2008-09-18 17:44:54 +00:00
|
|
|
if (apdu.resp[0] == 0x20) {
|
|
|
|
printf("card in administrative state, ok\n");
|
|
|
|
goto admin_state;
|
|
|
|
}
|
|
|
|
printf("card in operational state, need to switch to admin state\n");
|
|
|
|
|
2014-11-10 17:24:56 +00:00
|
|
|
/* PHASE CONTROL 80 10 00 00 */
|
2008-09-18 17:44:54 +00:00
|
|
|
|
|
|
|
memset(&apdu, 0, sizeof(apdu));
|
|
|
|
apdu.cla = 0x80;
|
|
|
|
apdu.ins = 0x10;
|
|
|
|
apdu.p1 = 0x00;
|
|
|
|
apdu.p2 = 0x00;
|
|
|
|
apdu.resp = 00;
|
|
|
|
apdu.lc = 0;
|
|
|
|
apdu.le = 00;
|
|
|
|
apdu.cse = SC_APDU_CASE_1;
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
if (check_apdu(&apdu))
|
2008-09-18 17:44:54 +00:00
|
|
|
return 1;
|
|
|
|
|
2014-11-10 17:24:56 +00:00
|
|
|
/* use GET DATA for lifecycle once more */
|
2008-09-18 17:44:54 +00:00
|
|
|
|
|
|
|
memset(&apdu, 0, sizeof(apdu));
|
|
|
|
apdu.cla = 0x00;
|
|
|
|
apdu.ins = 0xca;
|
|
|
|
apdu.p1 = 0x01;
|
|
|
|
apdu.p2 = 0x83;
|
|
|
|
apdu.resp = rbuf;
|
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
apdu.lc = 0;
|
|
|
|
apdu.le = 256;
|
|
|
|
apdu.cse = SC_APDU_CASE_2_SHORT;
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
if (check_apdu(&apdu))
|
2008-09-18 17:44:54 +00:00
|
|
|
return 1;
|
|
|
|
if (apdu.resp[0] != 0x20) {
|
|
|
|
printf("card not in administrative state, failed\n");
|
2008-09-19 10:21:14 +00:00
|
|
|
printf("aborting\n");
|
2008-09-18 17:44:54 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
admin_state:
|
|
|
|
/* use GET DATA for packages - 00 ca 01 88
|
2012-04-02 22:00:56 +00:00
|
|
|
* returns e1 LEN MM 04 ID ID ID ID 8f 01 SS
|
2008-09-18 17:44:54 +00:00
|
|
|
* MM = Manufacturing ID (01 .. 3f = Siemens
|
|
|
|
* ID ID ID ID = Id of the package
|
|
|
|
* SS = License state (01 enabled, 00 disabled
|
|
|
|
*/
|
|
|
|
|
|
|
|
memset(&apdu, 0, sizeof(apdu));
|
|
|
|
apdu.cla = 0x00;
|
|
|
|
apdu.ins = 0xca;
|
|
|
|
apdu.p1 = 0x01;
|
|
|
|
apdu.p2 = 0x88;
|
|
|
|
apdu.resp = rbuf;
|
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
apdu.lc = 0;
|
|
|
|
apdu.le = 256;
|
|
|
|
apdu.cse = SC_APDU_CASE_2_SHORT;
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
if (check_apdu(&apdu))
|
2008-09-18 17:44:54 +00:00
|
|
|
return 1;
|
|
|
|
if (apdu.resplen != 0x00) {
|
|
|
|
printf("card has packages installed.\n");
|
|
|
|
printf("you would loose those, and we can't re-install them.\n");
|
|
|
|
printf("to protect you card: aborting\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* now we need to erase the card. Our command is:
|
|
|
|
* ERASE FILES 84 06 00 00
|
|
|
|
* but it needs to be send using SM 4h mode (signed and enc.)
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
unsigned const char erase_apdu[] = { 0x84, 0x06, 0x00, 0x00 };
|
|
|
|
if (! cardos_sm4h(erase_apdu, sizeof(erase_apdu),
|
|
|
|
rbuf, sizeof(rbuf), startkey, sizeof(startkey)))
|
|
|
|
return 1;
|
2008-09-22 14:21:01 +00:00
|
|
|
if (verbose) {
|
|
|
|
printf ("Erasing EEPROM!\n");
|
|
|
|
}
|
2008-09-18 17:44:54 +00:00
|
|
|
memset(&apdu, 0, sizeof(apdu));
|
|
|
|
apdu.cse = SC_APDU_CASE_3_SHORT;
|
|
|
|
apdu.cla = rbuf[0];
|
|
|
|
apdu.ins = rbuf[1];
|
|
|
|
apdu.p1 = rbuf[2];
|
|
|
|
apdu.p2 = rbuf[3];
|
|
|
|
apdu.lc = rbuf[4];
|
|
|
|
apdu.data = &rbuf[5];
|
|
|
|
apdu.datalen = rbuf[4];
|
2012-04-02 22:00:56 +00:00
|
|
|
|
2008-09-18 17:44:54 +00:00
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
if (check_apdu(&apdu))
|
2008-09-18 17:44:54 +00:00
|
|
|
return 1;
|
|
|
|
}
|
2012-04-02 22:00:56 +00:00
|
|
|
|
2008-09-22 14:21:01 +00:00
|
|
|
erase_state:
|
|
|
|
|
2008-09-18 17:44:54 +00:00
|
|
|
/* next we need to format the card. Our command is:
|
|
|
|
* FORMAT 84 40 00 01
|
|
|
|
* with P2 = 01 (go to admin mode after format)
|
|
|
|
* and with data: T L V with tag 62 and value: more TLV
|
|
|
|
* 81 02 00 80 Main Folder size 0x0080
|
|
|
|
* 85 01 01 no death bit, no deactivation bit,
|
|
|
|
* but checksums bit
|
|
|
|
* 86 0a 00 ... 10 bytes AC with all set to allow (00)
|
|
|
|
* not included: CB tag with secure mode definition
|
|
|
|
* (defaults are fine for us)
|
|
|
|
*
|
|
|
|
* this APDU needs to be send using SM 4h mode (signed and enc.)
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
unsigned const char format_apdu[] = {
|
|
|
|
0x84, 0x40, 0x00, 0x01, 0x15,
|
|
|
|
0x62, 0x13,
|
|
|
|
0x81, 0x02, 0x00, 0x80,
|
|
|
|
0x85, 0x01, 0x01,
|
|
|
|
0x86, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
2008-09-22 14:21:01 +00:00
|
|
|
if (verbose) {
|
|
|
|
printf ("Formatting!\n");
|
|
|
|
}
|
2008-09-18 17:44:54 +00:00
|
|
|
if (! cardos_sm4h(format_apdu, sizeof(format_apdu),
|
|
|
|
rbuf, sizeof(rbuf), startkey, sizeof(startkey)))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
memset(&apdu, 0, sizeof(apdu));
|
|
|
|
apdu.cse = SC_APDU_CASE_3_SHORT;
|
|
|
|
apdu.cla = rbuf[0];
|
|
|
|
apdu.ins = rbuf[1];
|
|
|
|
apdu.p1 = rbuf[2];
|
|
|
|
apdu.p2 = rbuf[3];
|
|
|
|
apdu.lc = rbuf[4];
|
|
|
|
apdu.data = &rbuf[5];
|
|
|
|
apdu.datalen = rbuf[4];
|
2012-04-02 22:00:56 +00:00
|
|
|
|
2008-09-18 17:44:54 +00:00
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
if (check_apdu(&apdu))
|
2008-09-18 17:44:54 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
2009-12-01 21:10:06 +00:00
|
|
|
}
|
|
|
|
# else /* ENABLE_OPENSSL */
|
2010-04-01 11:21:57 +00:00
|
|
|
static int cardos_format(const char *opt_startkey)
|
2009-12-01 21:10:06 +00:00
|
|
|
{
|
2010-06-08 10:45:07 +00:00
|
|
|
printf("Formatting CardOS cards requires OpenSC built with OpenSSL.\n");
|
|
|
|
printf("Aborting\n");
|
2008-09-18 17:44:54 +00:00
|
|
|
return 1;
|
|
|
|
}
|
2009-12-01 21:10:06 +00:00
|
|
|
#endif /* ENABLE_OPENSSL */
|
|
|
|
|
2010-06-08 14:41:49 +00:00
|
|
|
#ifdef ENABLE_OPENSSL
|
2009-12-01 21:10:06 +00:00
|
|
|
static int cardos_change_startkey(const char *change_startkey_apdu)
|
|
|
|
{
|
2010-05-01 12:15:36 +00:00
|
|
|
#define MAX_APDU 60
|
2009-12-01 21:10:06 +00:00
|
|
|
unsigned char cardos_version[2];
|
2010-05-01 12:15:36 +00:00
|
|
|
unsigned char apdu_bin[MAX_APDU];
|
|
|
|
size_t apdu_len=MAX_APDU;
|
2010-06-08 14:41:49 +00:00
|
|
|
unsigned char checksum[SHA_DIGEST_LENGTH];
|
2009-12-01 21:10:06 +00:00
|
|
|
|
2010-06-08 14:41:49 +00:00
|
|
|
static const unsigned char cardos_43b_checksum[SHA_DIGEST_LENGTH] =
|
|
|
|
{ 0x5C, 0xD6, 0x8C, 0x2C, 0x24, 0x77, 0x3C, 0xDC,
|
|
|
|
0x93, 0x73, 0xD8, 0x4B, 0x47, 0x29, 0x19, 0x70,
|
|
|
|
0x9F, 0xA2, 0x42, 0xB4 };
|
2009-12-01 21:10:06 +00:00
|
|
|
sc_apdu_t apdu;
|
|
|
|
u8 rbuf[256];
|
|
|
|
int r;
|
2012-04-02 22:00:56 +00:00
|
|
|
|
2019-01-28 08:56:50 +00:00
|
|
|
if (!change_startkey_apdu) {
|
|
|
|
printf("Missing change StartKey, aborting\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2009-12-01 21:10:06 +00:00
|
|
|
if (verbose) {
|
|
|
|
printf ("Change StartKey APDU:\n");
|
|
|
|
util_hex_dump_asc(stdout, (unsigned char *)change_startkey_apdu,
|
|
|
|
strlen(change_startkey_apdu), -1);
|
|
|
|
}
|
2012-04-02 22:00:56 +00:00
|
|
|
|
2009-12-01 21:10:06 +00:00
|
|
|
/* use GET DATA for version - 00 ca 01 82
|
|
|
|
* returns e.g. c8 09 for 4.2B
|
|
|
|
*/
|
|
|
|
|
|
|
|
memset(&apdu, 0, sizeof(apdu));
|
|
|
|
apdu.cla = 0x00;
|
|
|
|
apdu.ins = 0xca;
|
|
|
|
apdu.p1 = 0x01;
|
|
|
|
apdu.p2 = 0x82;
|
|
|
|
apdu.resp = rbuf;
|
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
apdu.lc = 0;
|
|
|
|
apdu.le = 256;
|
|
|
|
apdu.cse = SC_APDU_CASE_2_SHORT;
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
if (check_apdu(&apdu))
|
2009-12-01 21:10:06 +00:00
|
|
|
return 1;
|
|
|
|
if (apdu.resplen != 0x02) {
|
|
|
|
printf("did not receive version info, aborting\n");
|
|
|
|
return 1;
|
|
|
|
}
|
2008-09-18 17:44:54 +00:00
|
|
|
|
2009-12-01 21:10:06 +00:00
|
|
|
/* check all supported versions here. need a checksum check
|
|
|
|
for each of them below */
|
2009-12-02 11:07:45 +00:00
|
|
|
if ( (rbuf[0] != 0xc8 || rbuf[1] != 0x08) ) { /* M4.3B */
|
2009-12-01 21:10:06 +00:00
|
|
|
printf("currently only CardOS M4.01, M4.2B, M4.2C and M4.3B are supported, aborting\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
cardos_version[0] = rbuf[0];
|
|
|
|
cardos_version[1] = rbuf[1];
|
|
|
|
|
|
|
|
/* GET DATA for startkey index - 00 ca 01 96
|
|
|
|
* returns 6 bytes PackageLoadKey.Version, PackageLoadKey.Retry
|
|
|
|
* Startkey.Version, Startkey.Retry, 2 internal data byes */
|
|
|
|
|
|
|
|
memset(&apdu, 0, sizeof(apdu));
|
|
|
|
apdu.cla = 0x00;
|
|
|
|
apdu.ins = 0xca;
|
|
|
|
apdu.p1 = 0x01;
|
|
|
|
apdu.p2 = 0x96;
|
|
|
|
apdu.resp = rbuf;
|
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
apdu.lc = 0;
|
|
|
|
apdu.le = 256;
|
|
|
|
apdu.cse = SC_APDU_CASE_2_SHORT;
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
if (check_apdu(&apdu))
|
2009-12-01 21:10:06 +00:00
|
|
|
return 1;
|
|
|
|
if (apdu.resplen < 0x04) {
|
2016-09-28 17:35:13 +00:00
|
|
|
printf("expected 4-6 bytes form GET DATA for startkey data, but got only %"SC_FORMAT_LEN_SIZE_T"u\n",
|
|
|
|
apdu.resplen);
|
2009-12-01 21:10:06 +00:00
|
|
|
printf("aborting\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (apdu.resp[2] != 0x00) {
|
|
|
|
printf("startkey version is 0x%02x, currently we support only 0x00\n", (int) apdu.resp[3]);
|
|
|
|
printf("aborting\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (apdu.resp[3] < 5) {
|
|
|
|
printf("startkey has only %d tries left. to be safe: aborting\n", apdu.resp[3]);
|
|
|
|
return 1;
|
2012-04-02 22:00:56 +00:00
|
|
|
}
|
2009-12-01 21:10:06 +00:00
|
|
|
|
|
|
|
/* now check if the correct APDU was passed */
|
|
|
|
if (sc_hex_to_bin(change_startkey_apdu, apdu_bin, &apdu_len) != 0) {
|
|
|
|
printf("can't convert startkey apdu to binary format: aborting\n");
|
|
|
|
return 1;
|
|
|
|
}
|
2010-06-08 14:41:49 +00:00
|
|
|
SHA1(apdu_bin, apdu_len, checksum);
|
2009-12-01 21:10:06 +00:00
|
|
|
|
|
|
|
if (cardos_version[0] == 0xc8 && cardos_version[1] == 0x08) {
|
2010-06-08 14:41:49 +00:00
|
|
|
if (memcmp(checksum, cardos_43b_checksum, SHA_DIGEST_LENGTH) != 0) {
|
2009-12-01 21:10:06 +00:00
|
|
|
printf("change startkey apdu is wrong, checksum doesn't match\n");
|
2012-04-02 22:00:56 +00:00
|
|
|
util_hex_dump_asc(stdout, checksum, SHA_DIGEST_LENGTH, -1);
|
|
|
|
util_hex_dump_asc(stdout, cardos_43b_checksum, SHA_DIGEST_LENGTH, -1);
|
2009-12-01 21:10:06 +00:00
|
|
|
printf("aborting\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
goto change_startkey;
|
|
|
|
}
|
2012-04-02 22:00:56 +00:00
|
|
|
|
2009-12-01 21:10:06 +00:00
|
|
|
printf("checksum for your card not yet implemented, aborting\n");
|
|
|
|
return 1;
|
2012-04-02 22:00:56 +00:00
|
|
|
|
2009-12-01 21:10:06 +00:00
|
|
|
change_startkey:
|
|
|
|
/* run change startkey apdu */
|
|
|
|
|
|
|
|
memset(&apdu, 0, sizeof(apdu));
|
|
|
|
apdu.cla = apdu_bin[0];
|
|
|
|
apdu.ins = apdu_bin[1];
|
|
|
|
apdu.p1 = apdu_bin[2];
|
|
|
|
apdu.p2 = apdu_bin[3];
|
|
|
|
apdu.lc = apdu_bin[4];
|
|
|
|
apdu.data = &apdu_bin[5];
|
|
|
|
apdu.datalen = apdu.lc;
|
|
|
|
apdu.resp = 00;
|
|
|
|
apdu.le = 00;
|
|
|
|
apdu.cse = SC_APDU_CASE_3_SHORT;
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
if (check_apdu(&apdu))
|
2009-12-01 21:10:06 +00:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
printf("change startkey command issued with success\n");
|
|
|
|
|
|
|
|
/* GET DATA for startkey index - 00 ca 01 96
|
|
|
|
* returns 6 bytes PackageLoadKey.Version, PackageLoadKey.Retry
|
|
|
|
* Startkey.Version, Startkey.Retry, 2 internal data byes */
|
|
|
|
|
|
|
|
memset(&apdu, 0, sizeof(apdu));
|
|
|
|
apdu.cla = 0x00;
|
|
|
|
apdu.ins = 0xca;
|
|
|
|
apdu.p1 = 0x01;
|
|
|
|
apdu.p2 = 0x96;
|
|
|
|
apdu.resp = rbuf;
|
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
apdu.lc = 0;
|
|
|
|
apdu.le = 256;
|
|
|
|
apdu.cse = SC_APDU_CASE_2_SHORT;
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2014-11-10 17:24:56 +00:00
|
|
|
if (check_apdu(&apdu))
|
2009-12-01 21:10:06 +00:00
|
|
|
return 1;
|
|
|
|
if (apdu.resplen < 0x04) {
|
2016-09-28 17:35:13 +00:00
|
|
|
printf("expected 4-6 bytes form GET DATA for startkey data, but got only %"SC_FORMAT_LEN_SIZE_T"u\n",
|
|
|
|
apdu.resplen);
|
2009-12-01 21:10:06 +00:00
|
|
|
printf("aborting\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2009-12-02 09:52:12 +00:00
|
|
|
if (apdu.resp[2] != 0xff) {
|
|
|
|
printf("startkey version is 0x%02x, should have been changed to 0xff.\n", apdu.resp[2]);
|
2009-12-01 21:10:06 +00:00
|
|
|
printf("aborting\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2009-12-02 09:52:12 +00:00
|
|
|
printf("startkey is now 0xff, success!\n");
|
2009-12-01 21:10:06 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
# else /* ENABLE_OPENSSL */
|
|
|
|
static int cardos_change_startkey(const char *change_startkey_apdu) {
|
2010-06-08 14:41:49 +00:00
|
|
|
fprintf(stderr, "Changing the startkey requires OpenSC built with OpenSSL.\n");
|
2010-06-08 10:45:07 +00:00
|
|
|
fprintf(stderr, "Aborting\n");
|
2009-12-01 21:10:06 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#endif
|
2003-04-11 13:55:22 +00:00
|
|
|
|
2017-08-02 21:12:58 +00:00
|
|
|
int main(int argc, char *argv[])
|
2003-04-11 13:55:22 +00:00
|
|
|
{
|
2020-01-06 17:03:19 +00:00
|
|
|
int err = 0, r, c;
|
2008-09-18 17:44:54 +00:00
|
|
|
int do_info = 0;
|
|
|
|
int do_format = 0;
|
2009-12-01 21:10:06 +00:00
|
|
|
int do_change_startkey = 0;
|
2008-09-18 17:44:54 +00:00
|
|
|
int action_count = 0;
|
|
|
|
const char *opt_startkey = NULL;
|
2009-12-01 21:10:06 +00:00
|
|
|
const char *opt_change_startkey = NULL;
|
2006-02-07 20:14:43 +00:00
|
|
|
sc_context_param_t ctx_param;
|
2003-04-11 13:55:22 +00:00
|
|
|
|
2020-01-06 17:03:19 +00:00
|
|
|
while ((c = getopt_long(argc, argv, "hifs:r:vdwS:", options, (int *) 0)) != -1) {
|
2003-04-11 13:55:22 +00:00
|
|
|
switch (c) {
|
|
|
|
case 'h':
|
2010-07-26 11:18:00 +00:00
|
|
|
printf("NB! This tool is only for Siemens CardOS based cards!\n\n");
|
2012-05-26 16:53:09 +00:00
|
|
|
util_print_usage_and_die(app_name, options, option_help, NULL);
|
2008-09-18 17:44:54 +00:00
|
|
|
case 'i':
|
|
|
|
do_info = 1;
|
|
|
|
action_count++;
|
|
|
|
break;
|
|
|
|
case 'f':
|
|
|
|
do_format = 1;
|
|
|
|
action_count++;
|
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
opt_startkey = optarg;
|
|
|
|
break;
|
2009-12-01 21:10:06 +00:00
|
|
|
case 'S':
|
|
|
|
do_change_startkey = 1;
|
|
|
|
opt_change_startkey = optarg;
|
|
|
|
action_count++;
|
|
|
|
break;
|
2003-04-11 13:55:22 +00:00
|
|
|
case 'r':
|
2010-01-24 15:29:47 +00:00
|
|
|
opt_reader = optarg;
|
2003-04-11 13:55:22 +00:00
|
|
|
break;
|
2004-06-13 20:13:12 +00:00
|
|
|
case 'v':
|
|
|
|
verbose++;
|
2003-04-11 13:55:22 +00:00
|
|
|
break;
|
|
|
|
case 'w':
|
|
|
|
opt_wait = 1;
|
|
|
|
break;
|
2020-01-06 17:03:19 +00:00
|
|
|
default:
|
|
|
|
util_print_usage_and_die(app_name, options, option_help, NULL);
|
2003-04-11 13:55:22 +00:00
|
|
|
}
|
|
|
|
}
|
2006-02-07 20:14:43 +00:00
|
|
|
|
|
|
|
/* create sc_context_t object */
|
|
|
|
memset(&ctx_param, 0, sizeof(ctx_param));
|
|
|
|
ctx_param.ver = 0;
|
|
|
|
ctx_param.app_name = app_name;
|
|
|
|
r = sc_context_create(&ctx, &ctx_param);
|
2003-04-11 13:55:22 +00:00
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "Failed to establish context: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2010-10-20 12:33:07 +00:00
|
|
|
|
2020-01-06 17:03:19 +00:00
|
|
|
/* force CardOS card driver */
|
|
|
|
err = sc_set_card_driver(ctx, "cardos");
|
|
|
|
if (err) {
|
|
|
|
fprintf(stderr, "CardOS card driver not found!\n");
|
|
|
|
err = 1;
|
2020-01-04 16:48:04 +00:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2010-01-24 15:29:47 +00:00
|
|
|
err = util_connect_card(ctx, &card, opt_reader, opt_wait, verbose);
|
2003-04-11 13:55:22 +00:00
|
|
|
if (err)
|
|
|
|
goto end;
|
|
|
|
|
2020-01-06 17:03:19 +00:00
|
|
|
/* fail if card is not a CardOS card */
|
|
|
|
if (card->type < SC_CARD_TYPE_CARDOS_BASE || card->type >= SC_CARD_TYPE_CARDOS_BASE+1000) {
|
|
|
|
fprintf(stderr, "Card type %X: not a CardOS card\n", card->type);
|
|
|
|
err = 1;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2008-09-18 17:44:54 +00:00
|
|
|
if (do_info) {
|
|
|
|
if ((err = cardos_info())) {
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
action_count--;
|
|
|
|
}
|
2009-12-01 21:10:06 +00:00
|
|
|
if (do_change_startkey) {
|
|
|
|
if ((err = cardos_change_startkey(opt_change_startkey))) {
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
action_count--;
|
|
|
|
}
|
2008-09-18 17:44:54 +00:00
|
|
|
if (do_format) {
|
|
|
|
if ((err = cardos_format(opt_startkey))) {
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
action_count--;
|
2003-04-11 13:55:22 +00:00
|
|
|
}
|
2019-03-06 18:54:35 +00:00
|
|
|
end:
|
2003-04-11 13:55:22 +00:00
|
|
|
if (card) {
|
|
|
|
sc_unlock(card);
|
2010-01-24 15:29:47 +00:00
|
|
|
sc_disconnect_card(card);
|
2003-04-11 13:55:22 +00:00
|
|
|
}
|
2019-03-05 23:39:53 +00:00
|
|
|
sc_release_context(ctx);
|
2003-04-11 13:55:22 +00:00
|
|
|
return err;
|
|
|
|
}
|