commit changes: cardos-info is now cardos-tool.

and it knows to format, at least some cards/tokens with cardos.


git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@3566 c6295689-39f2-0310-b995-f0e70906c6a9
This commit is contained in:
aj 2008-09-18 17:44:54 +00:00
parent 021dfaccba
commit 5c29dcdb94
3 changed files with 531 additions and 18 deletions

View File

@ -1,28 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<refentry id="cardos-info">
<refentry id="cardos-tool">
<refmeta>
<refentrytitle>cardos-info</refentrytitle>
<refentrytitle>cardos-tool</refentrytitle>
<manvolnum>1</manvolnum>
<refmiscinfo>opensc</refmiscinfo>
</refmeta>
<refnamediv>
<refname>cardos-info</refname>
<refpurpose>displays information about Card OS-based security tokens
<refname>cardos-tool</refname>
<refpurpose>displays information about Card OS-based security tokens or format them
</refpurpose>
</refnamediv>
<refsect1>
<title>Synopsis</title>
<para>
<command>cardos-info</command> [OPTIONS]
<command>cardos-tool</command> [OPTIONS]
</para>
</refsect1>
<refsect1>
<title>Description</title>
<para>
The <command>cardos-info</command> utility is used to display information about
The <command>cardos-tool</command> utility is used to display information about
smart cards and similar security tokens based on Siemens Card/OS M4.
</para>
</refsect1>
@ -31,9 +31,17 @@ smart cards and similar security tokens based on Siemens Card/OS M4.
<title>Options</title>
<para>
<variablelist>
<varlistentry>
<term><option>--info</option>, <option>-i</option></term>
<listitem><para>Display information about the card or token.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--format</option>, <option>-f</option></term>
<listitem><para>Format the card or token.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--reader</option> number, <option>-r</option> number</term>
<listitem><para>Display information about the token in reader number <varname>number</varname>.
<listitem><para>Specify the reader number <varname>number</varname> to use.
The default is reader 0.</para></listitem>
</varlistentry>
<varlistentry>

View File

@ -7,7 +7,7 @@ EXTRA_DIST = Makefile.mak
noinst_HEADERS = util.h
bin_PROGRAMS = opensc-tool opensc-explorer pkcs15-tool pkcs15-crypt \
pkcs11-tool cardos-info eidenv rutoken-tool
pkcs11-tool cardos-tool eidenv rutoken-tool cardos-info
if ENABLE_OPENSSL
bin_PROGRAMS += cryptoflex-tool pkcs15-init netkey-tool piv-tool
endif
@ -34,7 +34,8 @@ cryptoflex_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS)
pkcs15_init_SOURCES = pkcs15-init.c util.c
pkcs15_init_LDADD = $(OPTIONAL_OPENSSL_LIBS) \
$(top_builddir)/src/pkcs15init/libpkcs15init.la
cardos_info_SOURCES = cardos-info.c util.c
cardos_info_SOURCES =
cardos_tool_SOURCES = cardos-tool.c util.c
eidenv_SOURCES = eidenv.c
netkey_tool_SOURCES = netkey-tool.c
netkey_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS)
@ -50,7 +51,7 @@ pkcs11_tool_SOURCES += versioninfo.rc
pkcs15_crypt_SOURCES += versioninfo.rc
cryptoflex_tool_SOURCES += versioninfo.rc
pkcs15_init_SOURCES += versioninfo.rc
cardos_info_SOURCES += versioninfo.rc
cardos_tool_SOURCES += versioninfo.rc
eidenv_SOURCES += versioninfo.rc
netkey_tool_SOURCES += versioninfo.rc
rutoken_tool_SOURCES += versioninfo.rc
@ -61,3 +62,14 @@ endif
versioninfo.rc:
sed 's/@@FILE_DESCRIPTION@@/OpenSC Utility/g' \
"$(top_builddir)/win32/versioninfo.rc.in" > versioninfo.rc
if WIN32
cardos-info.bat:
echo "@echo off" > cardos-info.bat
echo "echo running cardos-tool --info" >> cardos-info.bat
echo "cardos-tool --info >> cardos-info.bat
else
cardos-info:
printf '#!/bin/sh\necho "running cardos-tool --info $*"\nexec cardos-tool --info $*\n' > cardos-info
chmod +x cardos-info
endif

View File

@ -1,6 +1,8 @@
/*
* cardos-info.c: Info about Card OS based tokens
* cardos-tool.c: Tool and Info about Card OS based tokens
*
* Copyright (C) 2008 Andreas Jellinghaus <aj@dungeon.inka.de>
* Copyright (C) 2007 Jean-Pierre Szikora <jean-pierre.szikora@uclouvain.be>
* Copyright (C) 2003 Andreas Jellinghaus <aj@dungeon.inka.de>
* Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
*
@ -31,23 +33,35 @@
#include <errno.h>
#include <ctype.h>
#include <sys/stat.h>
#ifdef ENABLE_OPENSSL
#include <openssl/des.h>
#endif
#include <opensc/opensc.h>
#include "util.h"
static const char *app_name = "cardos-info";
static const char *app_name = "cardos-tool";
static int opt_reader = -1, opt_debug = 0, opt_wait = 0;
static int verbose = 0;
static const struct option options[] = {
{"info", 0, NULL, 'i'},
{"format", 0, NULL, 'f'},
{"startkey", 1, NULL, 's'},
{"reader", 1, NULL, 'r'},
{"card-driver", 1, NULL, 'c'},
{"wait", 0, NULL, 'w'},
{"verbose", 0, NULL, 'v'},
{"debug", 0, NULL, 'd'},
{NULL, 0, NULL, 0}
};
static const char *option_help[] = {
"Print information about this card",
"Format this card erasing all content",
"Specify startkey for format",
"Uses reader number <arg> [0]",
"Forces the use of driver <arg> [auto-detect]",
"Wait for a card to be inserted",
@ -148,11 +162,11 @@ static int cardos_info(void)
} else if (apdu.resp[0] == 0xc8 && apdu.resp[1] == 0x07) {
printf(" (that's CardOS M4.3)\n");
} else if (apdu.resp[0] == 0xc8 && apdu.resp[1] == 0x08) {
printf(" (that's CardOS M4.3b)\n");
printf(" (that's CardOS M4.3B)\n");
} else if (apdu.resp[0] == 0xc8 && apdu.resp[1] == 0x09) {
printf(" (that's CardOS M4.2b)\n");
printf(" (that's CardOS M4.2B)\n");
} else if (apdu.resp[0] == 0xc8 && apdu.resp[1] == 0x0B) {
printf(" (that's CardOS M4.2c)\n");
printf(" (that's CardOS M4.2C)\n");
} else {
printf(" (unknown Version)\n");
}
@ -185,6 +199,8 @@ static int cardos_info(void)
printf("%d (administration)\n", rbuf[0]);
} else if (rbuf[0] == 0x10) {
printf("%d (operational)\n", rbuf[0]);
} else if (rbuf[0] == 0x29) {
printf("%d (erase in progress)\n", rbuf[0]);
} else {
printf("%d (unknown)\n", rbuf[0]);
}
@ -350,15 +366,472 @@ static int cardos_info(void)
return 0;
}
#ifdef ENABLE_OPENSSL
static int cardos_sm4h(unsigned char *in, size_t inlen, unsigned char
*out, size_t outlen, unsigned char *key, size_t keylen) {
/* using a buffer with an APDU, build an SM 4h APDU for cardos */
int plain_lc; /* data size in orig APDU */
int mac_input_len, enc_input_len;
unsigned char *mac_input, *enc_input;
DES_key_schedule ks_a, ks_b;
DES_cblock des_in,des_out;
int i,j;
if (keylen != 16) {
printf("key has wrong size, need 16 bytes, got %d. aborting.\n",
keylen);
return 0;
}
if (inlen < 4)
return 0; /* failed, apdu too short */
if (inlen == 5)
plain_lc = 0;
if (inlen > 5)
plain_lc = in[4];
/* 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) {
printf("out of memory, aborting");
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 */
/* 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;
DES_ecb_encrypt(des_in, des_out, &ks_a, 1);
/* 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];
DES_ecb_encrypt(des_in, des_out, &ks_a, 1);
}
/* 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];
DES_ecb_encrypt(des_in, des_out, &ks_b, 0);
for (i=0; i < 8; i++) des_in[i] = des_out[i];
DES_ecb_encrypt(des_in, des_out, &ks_a, 1);
/* 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) {
printf("out of memory, aborting");
return 0;
}
if (plain_lc)
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 */
/* calloc already cleard the remaining bytes to 00 */
if (outlen < 5 + enc_input_len) {
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;
/* encrypt with des2 (tripple des, but using keys A-B-A) */
DES_ecb2_encrypt(des_in, des_out, &ks_a, &ks_b, 1);
/* copy encrypted bytes into output */
for (i=0; i < 8; i++) out[5+i] = des_out[i];
/* encrypt other blocks (usualy one) */
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];
/* encrypt with des2 (tripple des, but using keys A-B-A) */
DES_ecb2_encrypt(des_in, des_out, &ks_a, &ks_b, 1);
/* copy encrypted bytes into output */
for (i=0; i < 8; i++) out[5+8*j+i] = des_out[i];
}
return 1;
}
#endif
static int cardos_format()
{
unsigned const char startkey[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
sc_apdu_t apdu;
u8 rbuf[256];
int r;
/* first run GET DATA for lifecycle 00 CA 01 83
* returns 34 MANUFACTURING 20 ADMINISTRATION 10 OPERATIONAL
* 26 INITIALITATION, 23 PERSINALIZATION 3f DEATH
* 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;
}
if (apdu.sw1 != 0x90 || apdu.sw2 != 00 || opt_debug) {
fprintf(stderr, "Received (SW1=0x%02X, SW2=0x%02X)%s\n",
apdu.sw1, apdu.sw2, apdu.resplen ? ":" : "");
if (apdu.resplen)
util_hex_dump_asc(stdout, apdu.resp, apdu.resplen, -1);
return 1;
}
if (apdu.resp[0] == 0x34) {
printf("card in manufacturing state\n");
printf("cannot continue without secret change startkey command\n");
printf("aborting\n");
return 1;
/* we can leave manufacturing mode with PHASE CYCLE,
* but before we do that, we need to change the secret
* siemens start key to the default 0xff start key.
* we know the APDU for that, but it is secreat and
* 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 */
}
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");
/* PHASE CONTORL 80 10 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;
}
if (apdu.sw1 != 0x90 || apdu.sw2 != 00 || opt_debug) {
fprintf(stderr, "Received (SW1=0x%02X, SW2=0x%02X)%s\n",
apdu.sw1, apdu.sw2, apdu.resplen ? ":" : "");
if (apdu.resplen)
util_hex_dump_asc(stdout, apdu.resp, apdu.resplen, -1);
return 1;
}
/* use GET DATA for lifecacle one more */
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;
}
if (apdu.sw1 != 0x90 || apdu.sw2 != 00 || opt_debug) {
fprintf(stderr, "Received (SW1=0x%02X, SW2=0x%02X)%s\n",
apdu.sw1, apdu.sw2, apdu.resplen ? ":" : "");
if (apdu.resplen)
util_hex_dump_asc(stdout, apdu.resp, apdu.resplen, -1);
return 1;
}
if (apdu.resp[0] != 0x20) {
printf("card not in administrative state, failed\n");
printf("aborting");
return 1;
}
admin_state:
/* use GET DATA for packages - 00 ca 01 88
* returns e1 LEN MM 04 ID ID ID ID 8f 01 SS
* 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;
}
if (apdu.sw1 != 0x90 || apdu.sw2 != 00 || opt_debug) {
fprintf(stderr, "Received (SW1=0x%02X, SW2=0x%02X)%s\n",
apdu.sw1, apdu.sw2, apdu.resplen ? ":" : "");
if (apdu.resplen)
util_hex_dump_asc(stdout, apdu.resp, apdu.resplen, -1);
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;
}
/* 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;
}
if (apdu.sw1 != 0x90 || apdu.sw2 != 00 || opt_debug) {
fprintf(stderr, "Received (SW1=0x%02X, SW2=0x%02X)%s\n",
apdu.sw1, apdu.sw2, apdu.resplen ? ":" : "");
if (apdu.resplen)
util_hex_dump_asc(stdout, apdu.resp, apdu.resplen, -1);
return 1;
}
if (apdu.resplen != 0x02) {
printf("did not receive version info, aborting\n");
return 1;
}
if (rbuf[0] != 0xc8 || rbuf[1] != 0x09) {
printf("currently only CardOS M4.2B is supported, aborting\n");
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;
}
if (apdu.sw1 != 0x90 || apdu.sw2 != 00 || opt_debug) {
fprintf(stderr, "Received (SW1=0x%02X, SW2=0x%02X)%s\n",
apdu.sw1, apdu.sw2, apdu.resplen ? ":" : "");
if (apdu.resplen)
util_hex_dump_asc(stdout, apdu.resp, apdu.resplen, -1);
return 1;
}
if (apdu.resplen < 0x04) {
printf("expected 4-6 bytes form GET DATA for startkey data, but got only %ld\n", apdu.resplen);
printf("aborting\n");
return 1;
}
if (apdu.resp[3] =! 0xff) {
printf("startkey version is 0x%02x, currently we support only 0xff\n", (int) apdu.resp[3]);
printf("aborting\n");
return 1;
}
if (apdu.resp[2] < 5) {
printf("startkey has only %d tries left. to be safe: aborting\n", apdu.resp[4]);
return 1;
}
#ifdef ENABLE_OPENSSL
/* 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;
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];
r = sc_transmit_apdu(card, &apdu);
if (r) {
fprintf(stderr, "APDU transmit failed: %s\n",
sc_strerror(r));
return 1;
}
if (apdu.sw1 != 0x90 || apdu.sw2 != 00 || opt_debug) {
fprintf(stderr, "Received (SW1=0x%02X, SW2=0x%02X)%s\n",
apdu.sw1, apdu.sw2, apdu.resplen ? ":" : "");
if (apdu.resplen)
util_hex_dump_asc(stdout, apdu.resp, apdu.resplen, -1);
return 1;
}
}
/* 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};
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];
r = sc_transmit_apdu(card, &apdu);
if (r) {
fprintf(stderr, "APDU transmit failed: %s\n",
sc_strerror(r));
return 1;
}
if (apdu.sw1 != 0x90 || apdu.sw2 != 00 || opt_debug) {
fprintf(stderr, "Received (SW1=0x%02X, SW2=0x%02X)%s\n",
apdu.sw1, apdu.sw2, apdu.resplen ? ":" : "");
if (apdu.resplen)
util_hex_dump_asc(stdout, apdu.resp, apdu.resplen, -1);
return 1;
}
}
return 0;
# else
printf("this code needs to be compiled with openssl support enabled.\n");
printf("aborting\n");
return 1;
#endif /* ENABLE_OPENSSL */
}
int main(int argc, char *const argv[])
{
int err = 0, r, c, long_optind = 0;
int do_info = 0;
int do_format = 0;
int action_count = 0;
const char *opt_driver = NULL;
const char *opt_startkey = NULL;
sc_context_param_t ctx_param;
while (1) {
c = getopt_long(argc, argv, "r:vc:w", options,
c = getopt_long(argc, argv, "ifs:r:vdc:w", options,
&long_optind);
if (c == -1)
break;
@ -366,6 +839,17 @@ int main(int argc, char *const argv[])
case 'h':
case '?':
util_print_usage_and_die(app_name, options, option_help);
case 'i':
do_info = 1;
action_count++;
break;
case 'f':
do_format = 1;
action_count++;
break;
case 's':
opt_startkey = optarg;
break;
case 'r':
opt_reader = atoi(optarg);
break;
@ -410,8 +894,17 @@ int main(int argc, char *const argv[])
if (err)
goto end;
if ((err = cardos_info())) {
goto end;
if (do_info) {
if ((err = cardos_info())) {
goto end;
}
action_count--;
}
if (do_format) {
if ((err = cardos_format(opt_startkey))) {
goto end;
}
action_count--;
}
end:
if (card) {