2012-05-19 18:28:25 +00:00
|
|
|
/*
|
|
|
|
* openpgp-tool.c: OpenPGP card utility
|
|
|
|
*
|
|
|
|
* Copyright (C) 2012 Peter Marschall <peter@adpm.de>
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <stdio.h>
|
2015-08-08 06:17:12 +00:00
|
|
|
/* For dup() and dup2() functions */
|
2012-05-19 18:28:25 +00:00
|
|
|
#ifndef _WIN32
|
|
|
|
#include <unistd.h>
|
2015-08-08 06:17:12 +00:00
|
|
|
#else
|
|
|
|
/*
|
|
|
|
* Windows:
|
|
|
|
* https://msdn.microsoft.com/en-us/library/8syseb29.aspx
|
|
|
|
* https://msdn.microsoft.com/en-us/library/886kc0as.aspx
|
|
|
|
*/
|
|
|
|
#include <io.h>
|
|
|
|
#include <process.h>
|
2012-05-19 18:28:25 +00:00
|
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2015-10-12 21:26:54 +00:00
|
|
|
#include <ctype.h>
|
2012-05-19 18:28:25 +00:00
|
|
|
|
2019-03-14 22:24:23 +00:00
|
|
|
#include <getopt.h>
|
2012-05-19 18:28:25 +00:00
|
|
|
#include "libopensc/opensc.h"
|
|
|
|
#include "libopensc/asn1.h"
|
|
|
|
#include "libopensc/cards.h"
|
2019-01-30 21:00:36 +00:00
|
|
|
#include "libopensc/internal.h"
|
2012-06-06 10:04:33 +00:00
|
|
|
#include "libopensc/cardctl.h"
|
2013-02-20 04:54:30 +00:00
|
|
|
#include "libopensc/log.h"
|
2014-08-12 20:54:22 +00:00
|
|
|
#include "libopensc/errors.h"
|
2012-05-19 18:28:25 +00:00
|
|
|
#include "util.h"
|
2013-05-25 02:22:28 +00:00
|
|
|
#include "libopensc/log.h"
|
2012-05-19 18:28:25 +00:00
|
|
|
|
2013-11-02 17:45:56 +00:00
|
|
|
#define OPT_RAW 256
|
|
|
|
#define OPT_PRETTY 257
|
|
|
|
#define OPT_VERIFY 258
|
|
|
|
#define OPT_PIN 259
|
|
|
|
#define OPT_DELKEY 260
|
2012-05-26 16:52:05 +00:00
|
|
|
|
2015-10-12 21:26:54 +00:00
|
|
|
enum code_types {
|
|
|
|
TYPE_NULL,
|
|
|
|
TYPE_HEX,
|
|
|
|
TYPE_STRING
|
|
|
|
};
|
|
|
|
|
2012-05-19 18:28:25 +00:00
|
|
|
/* define structures */
|
|
|
|
struct ef_name_map {
|
|
|
|
const char *name;
|
|
|
|
const char *env_name;
|
|
|
|
const char *ef;
|
2015-10-12 21:26:54 +00:00
|
|
|
enum code_types type;
|
|
|
|
size_t offset;
|
|
|
|
size_t length; /* exact length: 0 <=> potentially infinite */
|
|
|
|
char *(*prettify_value)(u8 *, size_t);
|
2012-05-19 18:28:25 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* declare functions */
|
|
|
|
static void show_version(void);
|
2015-10-12 21:26:54 +00:00
|
|
|
static char *prettify_hex(u8 *data, size_t length, char *buffer, size_t buflen);
|
2018-06-23 18:41:20 +00:00
|
|
|
static char *prettify_algorithm(u8 *data, size_t length);
|
|
|
|
static char *prettify_date(u8 *data, size_t length);
|
2015-10-12 21:26:54 +00:00
|
|
|
static char *prettify_version(u8 *data, size_t length);
|
|
|
|
static char *prettify_manufacturer(u8 *data, size_t length);
|
|
|
|
static char *prettify_serialnumber(u8 *data, size_t length);
|
|
|
|
static char *prettify_name(u8 *data, size_t length);
|
|
|
|
static char *prettify_language(u8 *data, size_t length);
|
|
|
|
static char *prettify_gender(u8 *data, size_t length);
|
|
|
|
static void display_data(const struct ef_name_map *mapping, u8 *data, size_t length);
|
2012-05-19 18:28:25 +00:00
|
|
|
static int decode_options(int argc, char **argv);
|
2015-10-12 21:26:54 +00:00
|
|
|
static int do_info(sc_card_t *card, const struct ef_name_map *map);
|
2012-05-19 18:28:25 +00:00
|
|
|
|
|
|
|
/* define global variables */
|
2012-05-26 16:52:05 +00:00
|
|
|
static int actions = 0;
|
2012-05-19 18:28:25 +00:00
|
|
|
static char *opt_reader = NULL;
|
|
|
|
static int opt_wait = 0;
|
2012-05-26 16:52:05 +00:00
|
|
|
static int opt_raw = 0;
|
|
|
|
static int verbose = 0;
|
|
|
|
static int opt_userinfo = 0;
|
|
|
|
static int opt_cardinfo = 0;
|
2018-06-23 18:41:20 +00:00
|
|
|
static int opt_keyinfo = 0;
|
2012-05-19 18:28:25 +00:00
|
|
|
static char *exec_program = NULL;
|
2012-06-06 10:04:33 +00:00
|
|
|
static int opt_genkey = 0;
|
|
|
|
static u8 key_id = 0;
|
2018-10-03 09:04:12 +00:00
|
|
|
static char *keytype = NULL;
|
2012-06-06 10:04:33 +00:00
|
|
|
static int opt_verify = 0;
|
|
|
|
static char *verifytype = NULL;
|
|
|
|
static int opt_pin = 0;
|
2014-09-26 20:53:39 +00:00
|
|
|
static const char *pin = NULL;
|
2013-03-04 04:28:08 +00:00
|
|
|
static int opt_erase = 0;
|
2013-03-04 11:13:03 +00:00
|
|
|
static int opt_delkey = 0;
|
2018-05-20 14:01:34 +00:00
|
|
|
static size_t opt_dump_do = 0;
|
|
|
|
static unsigned int do_dump_idx[200]; /* large enough and checked on input */
|
2012-05-26 16:52:05 +00:00
|
|
|
|
|
|
|
static const char *app_name = "openpgp-tool";
|
2012-05-19 18:28:25 +00:00
|
|
|
|
|
|
|
static const struct option options[] = {
|
2012-05-26 16:52:05 +00:00
|
|
|
{ "reader", required_argument, NULL, 'r' },
|
|
|
|
{ "wait", no_argument, NULL, 'w' },
|
|
|
|
{ "exec", required_argument, NULL, 'x' },
|
|
|
|
{ "raw", no_argument, NULL, OPT_RAW },
|
|
|
|
{ "pretty", no_argument, NULL, OPT_PRETTY },
|
|
|
|
{ "card-info", no_argument, NULL, 'C' },
|
|
|
|
{ "user-info", no_argument, NULL, 'U' },
|
2018-06-23 18:41:20 +00:00
|
|
|
{ "key-info", no_argument, NULL, 'K' },
|
2012-06-06 10:04:33 +00:00
|
|
|
{ "gen-key", required_argument, NULL, 'G' },
|
2018-10-03 09:04:12 +00:00
|
|
|
{ "key-type", required_argument, NULL, 't' },
|
2012-05-26 16:52:05 +00:00
|
|
|
{ "help", no_argument, NULL, 'h' },
|
|
|
|
{ "verbose", no_argument, NULL, 'v' },
|
|
|
|
{ "version", no_argument, NULL, 'V' },
|
2013-03-04 04:28:08 +00:00
|
|
|
{ "erase", no_argument, NULL, 'E' },
|
2012-06-06 10:04:33 +00:00
|
|
|
{ "verify", required_argument, NULL, OPT_VERIFY },
|
|
|
|
{ "pin", required_argument, NULL, OPT_PIN },
|
2013-03-04 11:13:03 +00:00
|
|
|
{ "del-key", required_argument, NULL, OPT_DELKEY },
|
2014-08-12 20:54:22 +00:00
|
|
|
{ "do", required_argument, NULL, 'd' },
|
2012-05-19 18:28:25 +00:00
|
|
|
{ NULL, 0, NULL, 0 }
|
|
|
|
};
|
|
|
|
|
2012-05-26 16:52:05 +00:00
|
|
|
static const char *option_help[] = {
|
|
|
|
/* r */ "Use reader number <arg> [0]",
|
|
|
|
/* w */ "Wait for card insertion",
|
|
|
|
/* x */ "Execute program <arg> with data in env vars",
|
|
|
|
"Print values in raw format",
|
|
|
|
"Print values in pretty format",
|
2015-10-12 21:26:54 +00:00
|
|
|
/* C */ "Show card information",
|
2012-05-26 16:52:05 +00:00
|
|
|
/* U */ "Show card holder information",
|
2018-06-23 18:41:20 +00:00
|
|
|
/* K */ "Show key information",
|
2012-06-06 10:04:33 +00:00
|
|
|
/* G */ "Generate key",
|
2018-10-03 09:04:12 +00:00
|
|
|
/* t */ "Key type (default: rsa2048)",
|
2012-05-26 16:52:05 +00:00
|
|
|
/* h */ "Print this help message",
|
|
|
|
/* v */ "Verbose operation. Use several times to enable debug output.",
|
2012-06-06 10:04:33 +00:00
|
|
|
/* V */ "Show version number",
|
2013-03-04 04:28:08 +00:00
|
|
|
/* E */ "Erase (reset) the card",
|
2012-06-06 10:04:33 +00:00
|
|
|
"Verify PIN (CHV1, CHV2, CHV3...)",
|
2013-03-04 11:13:03 +00:00
|
|
|
"PIN string",
|
2015-10-04 10:55:25 +00:00
|
|
|
"Delete key (1, 2, 3 or all)",
|
2018-05-20 12:46:56 +00:00
|
|
|
/* d */ "Dump private data object number <arg> (i.e. DO <arg>)",
|
2012-05-26 16:52:05 +00:00
|
|
|
};
|
2012-05-19 18:28:25 +00:00
|
|
|
|
2015-10-12 21:26:54 +00:00
|
|
|
|
|
|
|
static const struct ef_name_map card_data[] = {
|
|
|
|
{ "AID", "OPENPGP_AID", "3F00:004F", TYPE_HEX, 0, 16, NULL },
|
|
|
|
{ "Version", "OPENPGP_VERSION", "3F00:004F", TYPE_HEX, 6, 2, prettify_version },
|
|
|
|
{ "Manufacturer", "OPENPGP_MANUFACTURER", "3F00:004F", TYPE_HEX, 8, 2, prettify_manufacturer },
|
|
|
|
{ "Serial number", "OPENPGP_SERIALNO", "3F00:004F", TYPE_HEX, 10, 4, prettify_serialnumber },
|
|
|
|
{ NULL, NULL, NULL, TYPE_NULL, 0, 0, NULL }
|
|
|
|
};
|
|
|
|
|
2018-06-23 18:41:20 +00:00
|
|
|
static const struct ef_name_map key_data[] = {
|
|
|
|
{ "Aut Algorithm", "OPENPGP_AUT_ALGORITHM", "3F00:006E:0073:00C3", TYPE_HEX, 0, 0, prettify_algorithm },
|
|
|
|
{ "Aut Create Date", "OPENPGP_AUT_DATE", "3F00:006E:0073:00CD", TYPE_HEX, 8, 4, prettify_date },
|
|
|
|
{ "Aut Fingerprint", "OPENPGP_AUT_FP", "3F00:006E:0073:00C5", TYPE_HEX, 40, 20, NULL },
|
|
|
|
{ "Dec Algorithm", "OPENPGP_DEC_ALGORITHM", "3F00:006E:0073:00C2", TYPE_HEX, 0, 0, prettify_algorithm },
|
|
|
|
{ "Dec Create Date", "OPENPGP_DEC_DATE", "3F00:006E:0073:00CD", TYPE_HEX, 4, 4, prettify_date },
|
|
|
|
{ "Dec Fingerprint", "OPENPGP_DEC_FP", "3F00:006E:0073:00C5", TYPE_HEX, 20, 20, NULL },
|
|
|
|
{ "Sig Algorithm", "OPENPGP_SIG_ALGORITHM", "3F00:006E:0073:00C1", TYPE_HEX, 0, 0, prettify_algorithm },
|
|
|
|
{ "Sig Create Date", "OPENPGP_SIG_DATE", "3F00:006E:0073:00CD", TYPE_HEX, 0, 4, prettify_date },
|
|
|
|
{ "Sig Fingerprint", "OPENPGP_SIG_FP", "3F00:006E:0073:00C5", TYPE_HEX, 0, 20, NULL },
|
|
|
|
{ NULL, NULL, NULL, TYPE_NULL, 0, 0, NULL }
|
|
|
|
};
|
|
|
|
|
2015-10-12 21:26:54 +00:00
|
|
|
static const struct ef_name_map user_data[] = {
|
|
|
|
{ "Account", "OPENGPG_ACCOUNT", "3F00:005E", TYPE_STRING, 0, 0, NULL },
|
|
|
|
{ "URL", "OPENPGP_URL", "3F00:5F50", TYPE_STRING, 0, 0, NULL },
|
|
|
|
{ "Name", "OPENPGP_NAME", "3F00:0065:005B", TYPE_STRING, 0, 0, prettify_name },
|
|
|
|
{ "Language", "OPENPGP_LANG", "3F00:0065:5F2D", TYPE_STRING, 0, 0, prettify_language },
|
|
|
|
{ "Gender", "OPENPGP_GENDER", "3F00:0065:5F35", TYPE_STRING, 0, 0, prettify_gender },
|
|
|
|
{ "DO 0101", "OPENPGP_DO0101", "3F00:0101", TYPE_STRING, 0, 0, NULL },
|
|
|
|
{ "DO 0102", "OPENPGP_DO0102", "3F00:0102", TYPE_STRING, 0, 0, NULL },
|
|
|
|
// { "DO 0103", "OPENPGP_DO0103", "3F00:0103", TYPE_STRING, 0, 0, NULL },
|
|
|
|
// { "DO 0104", "OPENPGP_DO0104", "3F00:0104", TYPE_STRING, 0, 0, NULL },
|
|
|
|
{ NULL, NULL, NULL, TYPE_NULL, 0, 0, NULL }
|
2012-05-19 18:28:25 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static void show_version(void)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
2015-07-03 16:21:38 +00:00
|
|
|
"openpgp-tool - OpenPGP card utility version " PACKAGE_VERSION "\n"
|
|
|
|
"\n"
|
2015-10-12 21:26:54 +00:00
|
|
|
"Copyright (c) 2012-18 Peter Marschall <peter@adpm.de>\n"
|
2015-07-03 16:21:38 +00:00
|
|
|
"Licensed under LGPL v2\n");
|
2012-05-19 18:28:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-12 21:26:54 +00:00
|
|
|
/* prettify hex */
|
|
|
|
static char *prettify_hex(u8 *data, size_t length, char *buffer, size_t buflen)
|
|
|
|
{
|
|
|
|
if (data != NULL) {
|
|
|
|
int r = sc_bin_to_hex(data, length, buffer, buflen, ':');
|
|
|
|
|
2018-10-21 17:39:50 +00:00
|
|
|
if (r == SC_SUCCESS)
|
2015-10-12 21:26:54 +00:00
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-06-23 18:41:20 +00:00
|
|
|
/* prettify algorithm parameters */
|
|
|
|
static char *prettify_algorithm(u8 *data, size_t length)
|
|
|
|
{
|
|
|
|
if (data != NULL && length >= 1) {
|
|
|
|
static char result[64]; /* large enough */
|
|
|
|
|
|
|
|
if (data[0] == 0x01 && length >= 5) { /* RSA */
|
|
|
|
unsigned short modulus = (data[1] << 8) + data[2];
|
|
|
|
snprintf(result, sizeof(result), "RSA%u", modulus);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
else if (data[0] == 0x12) { /* ECDH */
|
|
|
|
strcpy(result, "ECDH");
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
else if (data[0] == 0x13) { /* ECDSA */
|
|
|
|
strcpy(result, "ECDSA");
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* prettify date/time */
|
|
|
|
static char *prettify_date(u8 *data, size_t length)
|
|
|
|
{
|
|
|
|
if (data != NULL && length == 4) {
|
2018-11-23 17:51:22 +00:00
|
|
|
time_t time = (time_t) (data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]);
|
2019-01-14 09:01:14 +00:00
|
|
|
struct tm tm;
|
2018-06-23 18:41:20 +00:00
|
|
|
static char result[64]; /* large enough */
|
|
|
|
|
2019-01-14 09:01:14 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
if (0 != gmtime_s(&tm, &time))
|
|
|
|
return NULL;
|
|
|
|
#else
|
|
|
|
if (NULL == gmtime_r(&time, &tm))
|
|
|
|
return NULL;
|
|
|
|
#endif
|
|
|
|
strftime(result, sizeof(result), "%Y-%m-%d %H:%M:%S", &tm);
|
2018-06-23 18:41:20 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-12 21:26:54 +00:00
|
|
|
#define BCD2CHAR(x) (((((x) & 0xF0) >> 4) * 10) + ((x) & 0x0F))
|
|
|
|
|
|
|
|
/* prettify OpenPGP card version */
|
|
|
|
static char *prettify_version(u8 *data, size_t length)
|
|
|
|
{
|
|
|
|
if (data != NULL && length >= 2) {
|
|
|
|
static char result[10]; /* large enough for even 2*3 digits + separator */
|
|
|
|
int major = BCD2CHAR(data[0]);
|
|
|
|
int minor = BCD2CHAR(data[1]);
|
|
|
|
|
|
|
|
sprintf(result, "%d.%d", major, minor);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* prettify manufacturer */
|
|
|
|
static char *prettify_manufacturer(u8 *data, size_t length)
|
|
|
|
{
|
|
|
|
if (data != NULL && length >= 2) {
|
|
|
|
unsigned int manuf = (data[0] << 8) + data[1];
|
|
|
|
|
|
|
|
switch (manuf) {
|
|
|
|
case 0x0001: return "PPC Card Systems";
|
|
|
|
case 0x0002: return "Prism";
|
|
|
|
case 0x0003: return "OpenFortress";
|
|
|
|
case 0x0004: return "Wewid";
|
|
|
|
case 0x0005: return "ZeitControl";
|
|
|
|
case 0x0006: return "Yubico";
|
|
|
|
case 0x0007: return "OpenKMS";
|
|
|
|
case 0x0008: return "LogoEmail";
|
|
|
|
case 0x0009: return "Fidesmo";
|
|
|
|
case 0x000A: return "Dangerous Things";
|
|
|
|
|
|
|
|
case 0x002A: return "Magrathea";
|
|
|
|
case 0x0042: return "GnuPG e.V.";
|
|
|
|
|
|
|
|
case 0x1337: return "Warsaw Hackerspace";
|
|
|
|
case 0x2342: return "warpzone"; /* hackerspace Muenster. */
|
|
|
|
case 0x63AF: return "Trustica";
|
|
|
|
case 0xBD0E: return "Paranoidlabs";
|
|
|
|
case 0xF517: return "FSIJ";
|
|
|
|
|
|
|
|
/* 0x0000 and 0xFFFF are defined as test cards per spec,
|
|
|
|
0xFF00 to 0xFFFE are assigned for use with randomly created
|
|
|
|
serial numbers. */
|
|
|
|
case 0x0000:
|
|
|
|
case 0xffff: return "test card";
|
|
|
|
default: return (manuf & 0xff00) == 0xff00 ? "unmanaged S/N range" : "unknown";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* prettify pure serial number */
|
|
|
|
static char *prettify_serialnumber(u8 *data, size_t length)
|
|
|
|
{
|
|
|
|
if (data != NULL && length >= 4) {
|
|
|
|
static char result[15]; /* large enough for even 2*3 digits + separator */
|
2018-11-23 17:51:22 +00:00
|
|
|
unsigned long serial = (unsigned long) (data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]);
|
2015-10-12 21:26:54 +00:00
|
|
|
|
2018-11-23 17:51:22 +00:00
|
|
|
sprintf(result, "%08lX", serial);
|
2015-10-12 21:26:54 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-05-26 16:52:05 +00:00
|
|
|
/* prettify card holder's name */
|
2015-10-12 21:26:54 +00:00
|
|
|
static char *prettify_name(u8 *data, size_t length)
|
2012-05-19 18:28:25 +00:00
|
|
|
{
|
2015-10-12 21:26:54 +00:00
|
|
|
if (data != NULL && length > 0) {
|
|
|
|
char *src = (char *) data;
|
|
|
|
char *dst = (char *) data;
|
2012-05-26 16:52:05 +00:00
|
|
|
|
2015-10-12 21:26:54 +00:00
|
|
|
while (*src != '\0' && length > 0) {
|
2012-05-26 16:52:05 +00:00
|
|
|
*dst = *src++;
|
2015-10-12 21:26:54 +00:00
|
|
|
length--;
|
2012-05-26 16:52:05 +00:00
|
|
|
if (*dst == '<') {
|
2018-11-01 10:04:29 +00:00
|
|
|
if (length > 0 && *src == '<') {
|
2012-05-26 16:52:05 +00:00
|
|
|
src++;
|
2015-10-12 21:26:54 +00:00
|
|
|
length--;
|
|
|
|
}
|
2012-05-26 16:52:05 +00:00
|
|
|
*dst = ' ';
|
|
|
|
}
|
|
|
|
dst++;
|
|
|
|
}
|
|
|
|
*dst = '\0';
|
2015-10-12 21:26:54 +00:00
|
|
|
return (char *) data;
|
2012-05-26 16:52:05 +00:00
|
|
|
}
|
2015-10-12 21:26:54 +00:00
|
|
|
return NULL;
|
2012-05-19 18:28:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* prettify language */
|
2015-10-12 21:26:54 +00:00
|
|
|
static char *prettify_language(u8 *data, size_t length)
|
2012-05-19 18:28:25 +00:00
|
|
|
{
|
2015-10-12 21:26:54 +00:00
|
|
|
if (data != NULL && length > 0) {
|
|
|
|
char *str = (char *) data;
|
|
|
|
|
2012-05-19 18:28:25 +00:00
|
|
|
switch (strlen(str)) {
|
2013-11-02 17:45:56 +00:00
|
|
|
case 8: memmove(str+7, str+6, 1+strlen(str+6));
|
2012-05-19 18:28:25 +00:00
|
|
|
str[6] = ',';
|
|
|
|
/* fall through */
|
2013-11-02 17:45:56 +00:00
|
|
|
case 6: memmove(str+5, str+4, 1+strlen(str+4));
|
2012-05-19 18:28:25 +00:00
|
|
|
str[4] = ',';
|
|
|
|
/* fall through */
|
2013-11-02 17:45:56 +00:00
|
|
|
case 4: memmove(str+3, str+2, 1+strlen(str+2));
|
2012-05-19 18:28:25 +00:00
|
|
|
str[2] = ',';
|
|
|
|
/* fall through */
|
2013-11-02 17:45:56 +00:00
|
|
|
case 2: return str;
|
2012-05-19 18:28:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* convert the raw ISO-5218 SEX value to an english word */
|
2015-10-12 21:26:54 +00:00
|
|
|
static char *prettify_gender(u8 *data, size_t length)
|
2012-05-19 18:28:25 +00:00
|
|
|
{
|
2015-10-12 21:26:54 +00:00
|
|
|
if (data != NULL && length > 0) {
|
|
|
|
switch (*data) {
|
2013-11-02 17:45:56 +00:00
|
|
|
case '0': return "unknown";
|
|
|
|
case '1': return "male";
|
|
|
|
case '2': return "female";
|
2015-10-12 14:40:59 +00:00
|
|
|
case '9': return "not announced";
|
2012-05-19 18:28:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-12 21:26:54 +00:00
|
|
|
#define INDENT 16
|
|
|
|
|
|
|
|
static void display_data(const struct ef_name_map *map, u8 *data, size_t length)
|
2012-05-19 18:28:25 +00:00
|
|
|
{
|
2015-10-12 21:26:54 +00:00
|
|
|
if (map != NULL && data != NULL) {
|
|
|
|
char buffer[8192];
|
|
|
|
char *value = NULL;
|
|
|
|
|
|
|
|
if (opt_raw) {
|
|
|
|
/* length-wise safe, but may cut off data (safe for OpenPGP cards 2.x) */
|
|
|
|
if (length > sizeof(buffer))
|
|
|
|
length = sizeof(buffer);
|
|
|
|
|
|
|
|
if (map->type == TYPE_HEX) {
|
|
|
|
if (exec_program) {
|
|
|
|
value = prettify_hex(data, length, buffer, sizeof(buffer));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
sc_hex_dump(data, length, buffer, sizeof(buffer));
|
|
|
|
/* remove trailing newline */
|
|
|
|
if (*buffer != '\0' && buffer[strlen(buffer)-1] == '\n')
|
|
|
|
buffer[strlen(buffer)-1] = '\0';
|
|
|
|
value = buffer;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
value = (char *) data;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (map->prettify_value != NULL)
|
|
|
|
value = map->prettify_value(data, length);
|
|
|
|
else {
|
|
|
|
value = (map->type == TYPE_HEX)
|
|
|
|
? prettify_hex(data, length, buffer, sizeof(buffer))
|
|
|
|
: (char *) data;
|
|
|
|
}
|
|
|
|
}
|
2012-05-19 18:28:25 +00:00
|
|
|
|
|
|
|
if (value != NULL) {
|
|
|
|
if (exec_program) {
|
2015-10-12 21:26:54 +00:00
|
|
|
char *envvar= malloc(strlen(map->env_name) +
|
|
|
|
strlen(value) + 2);
|
2012-05-19 18:28:25 +00:00
|
|
|
|
|
|
|
if (envvar != NULL) {
|
2015-10-12 21:26:54 +00:00
|
|
|
strcpy(envvar, map->env_name);
|
2012-05-19 18:28:25 +00:00
|
|
|
strcat(envvar, "=");
|
|
|
|
strcat(envvar, value);
|
|
|
|
putenv(envvar);
|
2015-10-12 21:26:54 +00:00
|
|
|
/* envvar deliberately kept: see putenv(3) */
|
2012-05-19 18:28:25 +00:00
|
|
|
}
|
2015-10-12 21:26:54 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
const char *label = map->name;
|
|
|
|
int fill = (int) (INDENT - strlen(label));
|
2012-05-19 18:28:25 +00:00
|
|
|
|
2015-10-12 21:26:54 +00:00
|
|
|
printf("%s:%*s%s\n", label, fill, "", value);
|
2012-05-19 18:28:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int decode_options(int argc, char **argv)
|
|
|
|
{
|
|
|
|
int c;
|
2018-05-20 13:43:52 +00:00
|
|
|
char *endptr;
|
|
|
|
unsigned long val;
|
2012-05-19 18:28:25 +00:00
|
|
|
|
2018-10-03 09:04:12 +00:00
|
|
|
while ((c = getopt_long(argc, argv,"r:x:CUG:KEht:wvVd:", options, (int *) 0)) != EOF) {
|
2012-05-19 18:28:25 +00:00
|
|
|
switch (c) {
|
|
|
|
case 'r':
|
|
|
|
opt_reader = optarg;
|
|
|
|
break;
|
|
|
|
case 'x':
|
|
|
|
if (exec_program)
|
|
|
|
free(exec_program);
|
|
|
|
exec_program = strdup(optarg);
|
|
|
|
break;
|
2012-05-26 16:52:05 +00:00
|
|
|
case OPT_RAW:
|
|
|
|
opt_raw = 1;
|
|
|
|
break;
|
|
|
|
case OPT_PRETTY:
|
|
|
|
opt_raw = 0;
|
2012-05-19 18:28:25 +00:00
|
|
|
break;
|
2012-06-06 10:04:33 +00:00
|
|
|
case OPT_VERIFY:
|
|
|
|
opt_verify++;
|
|
|
|
if (verifytype)
|
|
|
|
free(verifytype);
|
|
|
|
verifytype = strdup(optarg);
|
|
|
|
actions++;
|
|
|
|
break;
|
|
|
|
case OPT_PIN:
|
|
|
|
opt_pin++;
|
2014-11-04 20:44:02 +00:00
|
|
|
util_get_pin(optarg, &pin);
|
2012-06-06 10:04:33 +00:00
|
|
|
break;
|
2012-05-26 16:52:05 +00:00
|
|
|
case 'C':
|
|
|
|
opt_cardinfo++;
|
2015-10-12 19:07:58 +00:00
|
|
|
actions++;
|
2012-05-26 16:52:05 +00:00
|
|
|
break;
|
|
|
|
case 'U':
|
|
|
|
opt_userinfo++;
|
2015-10-12 19:07:58 +00:00
|
|
|
actions++;
|
2012-05-26 16:52:05 +00:00
|
|
|
break;
|
2018-06-23 18:41:20 +00:00
|
|
|
case 'K':
|
|
|
|
opt_keyinfo++;
|
|
|
|
actions++;
|
|
|
|
break;
|
2012-06-06 10:04:33 +00:00
|
|
|
case 'G':
|
|
|
|
opt_genkey++;
|
|
|
|
key_id = optarg[0] - '0';
|
|
|
|
actions++;
|
|
|
|
break;
|
2018-10-03 09:04:12 +00:00
|
|
|
case 't':
|
|
|
|
if (keytype)
|
|
|
|
free(keytype);
|
|
|
|
keytype = strdup(optarg);
|
2012-06-06 10:04:33 +00:00
|
|
|
break;
|
2012-05-26 16:52:05 +00:00
|
|
|
case 'h':
|
2012-05-26 16:53:09 +00:00
|
|
|
util_print_usage_and_die(app_name, options, option_help, NULL);
|
2012-05-19 18:28:25 +00:00
|
|
|
break;
|
|
|
|
case 'w':
|
|
|
|
opt_wait = 1;
|
|
|
|
break;
|
2012-05-26 16:52:05 +00:00
|
|
|
case 'v':
|
|
|
|
verbose++;
|
|
|
|
break;
|
2012-05-19 18:28:25 +00:00
|
|
|
case 'V':
|
|
|
|
show_version();
|
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
break;
|
2013-03-04 04:28:08 +00:00
|
|
|
case 'E':
|
|
|
|
opt_erase++;
|
2015-10-13 11:04:19 +00:00
|
|
|
actions++;
|
2013-03-04 04:28:08 +00:00
|
|
|
break;
|
2013-03-04 11:13:03 +00:00
|
|
|
case OPT_DELKEY:
|
|
|
|
opt_delkey++;
|
|
|
|
if (strcmp(optarg, "all") != 0) /* Arg string is not 'all' */
|
|
|
|
key_id = optarg[0] - '0';
|
|
|
|
else /* Arg string is 'all' */
|
|
|
|
key_id = 'a';
|
2018-05-20 14:03:55 +00:00
|
|
|
actions++;
|
2015-10-04 10:45:25 +00:00
|
|
|
break;
|
2014-08-12 20:54:22 +00:00
|
|
|
case 'd':
|
2018-05-20 13:43:52 +00:00
|
|
|
endptr = NULL;
|
|
|
|
val = strtoul(optarg, &endptr, 16);
|
|
|
|
if (endptr == NULL || endptr == optarg || *endptr != '\0') {
|
2018-10-03 09:25:15 +00:00
|
|
|
util_error("Unable to parse DO identifier");
|
|
|
|
exit(EXIT_FAILURE);
|
2018-05-20 13:43:52 +00:00
|
|
|
}
|
2018-05-20 14:01:34 +00:00
|
|
|
if (opt_dump_do < sizeof(do_dump_idx) / sizeof(*do_dump_idx)) {
|
|
|
|
do_dump_idx[opt_dump_do] = (unsigned int) (val | 0x100);
|
|
|
|
opt_dump_do++;
|
|
|
|
}
|
2013-03-04 11:13:03 +00:00
|
|
|
actions++;
|
|
|
|
break;
|
2012-05-19 18:28:25 +00:00
|
|
|
default:
|
2012-05-26 16:53:09 +00:00
|
|
|
util_print_usage_and_die(app_name, options, option_help, NULL);
|
2012-05-19 18:28:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-26 16:52:05 +00:00
|
|
|
return optind;
|
2012-05-19 18:28:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-12 21:26:54 +00:00
|
|
|
static int do_info(sc_card_t *card, const struct ef_name_map *map)
|
2012-05-19 18:28:25 +00:00
|
|
|
{
|
|
|
|
int i;
|
2015-10-12 21:26:54 +00:00
|
|
|
u8 buf[2048];
|
2012-05-19 18:28:25 +00:00
|
|
|
|
2015-10-12 21:26:54 +00:00
|
|
|
for (i = 0; map[i].ef != NULL; i++) {
|
2012-05-19 18:28:25 +00:00
|
|
|
sc_path_t path;
|
|
|
|
sc_file_t *file;
|
2016-02-29 17:13:28 +00:00
|
|
|
size_t count;
|
2012-05-19 18:28:25 +00:00
|
|
|
int r;
|
|
|
|
|
2015-10-12 21:26:54 +00:00
|
|
|
sc_format_path(map[i].ef, &path);
|
2012-05-19 18:28:25 +00:00
|
|
|
r = sc_select_file(card, &path, &file);
|
|
|
|
if (r) {
|
2015-10-12 21:26:54 +00:00
|
|
|
util_error("failed to select EF %s: %s", map[i].ef, sc_strerror(r));
|
2012-05-26 16:52:05 +00:00
|
|
|
return EXIT_FAILURE;
|
2012-05-19 18:28:25 +00:00
|
|
|
}
|
|
|
|
|
2016-02-29 17:13:28 +00:00
|
|
|
count = file->size;
|
2014-06-01 17:42:01 +00:00
|
|
|
if (!count)
|
|
|
|
continue;
|
|
|
|
|
2016-02-29 17:13:28 +00:00
|
|
|
if (count > sizeof(buf) - 1) {
|
2015-10-12 21:26:54 +00:00
|
|
|
util_error("too small buffer to read the OpenPGP map");
|
2014-06-01 17:42:01 +00:00
|
|
|
return EXIT_FAILURE;
|
2012-05-19 18:28:25 +00:00
|
|
|
}
|
2013-11-02 17:45:56 +00:00
|
|
|
|
2016-02-29 17:13:28 +00:00
|
|
|
r = sc_read_binary(card, 0, buf, count, 0);
|
2013-11-02 17:45:56 +00:00
|
|
|
if (r < 0) {
|
2015-10-12 21:26:54 +00:00
|
|
|
util_error("failed to read %s: %s", map[i].ef, sc_strerror(r));
|
2014-06-01 17:42:01 +00:00
|
|
|
return EXIT_FAILURE;
|
2013-11-02 17:45:56 +00:00
|
|
|
}
|
2015-10-12 21:26:54 +00:00
|
|
|
if (r != (signed) count || (size_t) r < map[i].offset + map[i].length) {
|
2018-10-03 09:25:15 +00:00
|
|
|
util_error("%s: expecting %"SC_FORMAT_LEN_SIZE_T"d bytes, got only %d",
|
2015-10-12 21:26:54 +00:00
|
|
|
map[i].ef, count, r);
|
2014-06-01 17:42:01 +00:00
|
|
|
return EXIT_FAILURE;
|
2013-11-02 17:45:56 +00:00
|
|
|
}
|
2015-10-12 21:26:54 +00:00
|
|
|
if (map[i].offset > 0) {
|
|
|
|
memmove(buf, buf + map[i].offset, map[i].length);
|
|
|
|
count -= map[i].offset;
|
|
|
|
}
|
|
|
|
if (map[i].length > 0 && count > map[i].length)
|
|
|
|
count = map[i].length;
|
|
|
|
if (map[i].type == TYPE_STRING)
|
|
|
|
buf[count] = '\0';
|
2014-06-01 17:42:01 +00:00
|
|
|
|
2015-10-12 21:26:54 +00:00
|
|
|
display_data(&map[i], buf, count);
|
2012-05-19 18:28:25 +00:00
|
|
|
}
|
|
|
|
|
2012-05-26 16:52:05 +00:00
|
|
|
return EXIT_SUCCESS;
|
2012-05-19 18:28:25 +00:00
|
|
|
}
|
|
|
|
|
2014-08-12 20:54:22 +00:00
|
|
|
static int do_dump_do(sc_card_t *card, unsigned int tag)
|
|
|
|
{
|
2018-06-05 07:08:17 +00:00
|
|
|
int r;
|
|
|
|
size_t length;
|
|
|
|
unsigned char buffer[254]; // Private DO are specified up to 254 bytes
|
2014-08-12 20:54:22 +00:00
|
|
|
|
|
|
|
memset(buffer, '\0', sizeof(buffer));
|
2014-12-09 19:59:54 +00:00
|
|
|
|
2018-05-20 13:43:52 +00:00
|
|
|
if (tag < 0x101 || tag > 0x104) {
|
2018-10-03 09:25:15 +00:00
|
|
|
util_error("illegal DO identifier %04X", tag);
|
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
2018-05-20 13:43:52 +00:00
|
|
|
}
|
|
|
|
|
2014-08-12 20:54:22 +00:00
|
|
|
r = sc_get_data(card, tag, buffer, sizeof(buffer));
|
|
|
|
if (r < 0) {
|
2018-10-03 09:25:15 +00:00
|
|
|
util_error("failed to get data object DO %04X: %s", tag, sc_strerror(r));
|
2018-06-05 07:08:17 +00:00
|
|
|
if (SC_ERROR_SECURITY_STATUS_NOT_SATISFIED == r) {
|
2018-10-03 09:25:15 +00:00
|
|
|
util_error("make sure the 'verify' and 'pin' parameters are correct");
|
2014-08-12 20:54:22 +00:00
|
|
|
}
|
|
|
|
return r;
|
|
|
|
}
|
2018-06-05 07:08:17 +00:00
|
|
|
length = (size_t) r; /* r is guaranteed to be non-negative */
|
|
|
|
|
|
|
|
if (opt_raw) {
|
|
|
|
int tmp;
|
|
|
|
FILE *fp;
|
2014-08-12 20:54:22 +00:00
|
|
|
|
2018-06-05 07:08:17 +00:00
|
|
|
#ifndef _WIN32
|
2014-08-12 20:54:22 +00:00
|
|
|
tmp = dup(fileno(stdout));
|
2018-06-05 07:08:17 +00:00
|
|
|
#else
|
2015-08-08 06:17:12 +00:00
|
|
|
tmp = _dup(_fileno(stdout));
|
2018-06-05 07:08:17 +00:00
|
|
|
#endif
|
2015-01-28 03:45:08 +00:00
|
|
|
if (tmp < 0)
|
|
|
|
return EXIT_FAILURE;
|
2014-08-12 20:54:22 +00:00
|
|
|
fp = freopen(NULL, "wb", stdout);
|
2015-08-08 06:17:12 +00:00
|
|
|
if (fp) {
|
2018-06-05 07:08:17 +00:00
|
|
|
r = (int) fwrite(buffer, sizeof(char), length, fp);
|
2014-08-12 20:54:22 +00:00
|
|
|
}
|
2018-06-05 07:08:17 +00:00
|
|
|
#ifndef _WIN32
|
2014-08-12 20:54:22 +00:00
|
|
|
dup2(tmp, fileno(stdout));
|
2018-06-05 07:08:17 +00:00
|
|
|
#else
|
2015-08-08 06:17:12 +00:00
|
|
|
_dup2(tmp, _fileno(stdout));
|
2018-06-05 07:08:17 +00:00
|
|
|
#endif
|
2014-08-12 20:54:22 +00:00
|
|
|
clearerr(stdout);
|
2015-02-05 00:36:40 +00:00
|
|
|
close(tmp);
|
2018-06-05 07:08:17 +00:00
|
|
|
|
2018-06-05 07:43:31 +00:00
|
|
|
if (length != (size_t) r) /* fail on write errors */
|
2014-08-12 20:54:22 +00:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
} else {
|
2018-06-05 07:08:17 +00:00
|
|
|
util_hex_dump_asc(stdout, buffer, length, -1);
|
2014-08-12 20:54:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
2012-05-19 18:28:25 +00:00
|
|
|
|
2019-01-14 09:40:08 +00:00
|
|
|
int do_genkey(sc_card_t *card, u8 in_key_id, const char *keytype)
|
2012-06-06 10:04:33 +00:00
|
|
|
{
|
|
|
|
int r;
|
|
|
|
sc_cardctl_openpgp_keygen_info_t key_info;
|
|
|
|
u8 fingerprints[60];
|
|
|
|
sc_path_t path;
|
|
|
|
sc_file_t *file;
|
|
|
|
|
2019-01-14 09:40:08 +00:00
|
|
|
/* validate in_key_id */
|
|
|
|
if (in_key_id < 1 || in_key_id > 3) {
|
|
|
|
util_error("unknown key ID %d", in_key_id);
|
2018-10-03 09:25:15 +00:00
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
2012-06-06 10:04:33 +00:00
|
|
|
}
|
2018-10-03 09:04:12 +00:00
|
|
|
|
2018-10-21 16:06:37 +00:00
|
|
|
/* fall back to RSA 2048 if keytype is not given */
|
2018-10-03 09:04:12 +00:00
|
|
|
if (!keytype)
|
|
|
|
keytype = "RSA2048";
|
|
|
|
|
2012-06-06 10:04:33 +00:00
|
|
|
memset(&key_info, 0, sizeof(sc_cardctl_openpgp_keygen_info_t));
|
2018-10-03 09:04:12 +00:00
|
|
|
|
|
|
|
/* generate key depending on keytype passed */
|
|
|
|
if (strncasecmp("RSA", keytype, strlen("RSA")) == 0) {
|
|
|
|
size_t keylen = 2048; /* default length for RSA keys */
|
|
|
|
const char *keylen_ptr = keytype + strlen("RSA");
|
|
|
|
|
|
|
|
/* try to get key length from keytype, e.g. "rsa3072" -> 3072 */
|
|
|
|
if (strlen(keylen_ptr) > 0) {
|
|
|
|
if (strspn(keylen_ptr, "0123456789") == strlen(keylen_ptr) &&
|
|
|
|
atol(keylen_ptr) >= 1024 && atol(keylen_ptr) <= 4096) {
|
|
|
|
keylen = atol(keylen_ptr);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
util_error("illegal key type: %s", keytype);
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set key_info */
|
2019-01-14 09:40:08 +00:00
|
|
|
key_info.key_id = in_key_id;
|
2018-10-03 09:04:12 +00:00
|
|
|
key_info.algorithm = SC_OPENPGP_KEYALGO_RSA;
|
2019-01-30 21:00:36 +00:00
|
|
|
key_info.u.rsa.modulus_len = keylen;
|
|
|
|
key_info.u.rsa.modulus = malloc(BYTES4BITS(keylen));
|
2018-10-03 09:04:12 +00:00
|
|
|
|
|
|
|
r = sc_card_ctl(card, SC_CARDCTL_OPENPGP_GENERATE_KEY, &key_info);
|
2019-01-30 21:00:36 +00:00
|
|
|
free(key_info.u.rsa.modulus);
|
2018-10-03 09:04:12 +00:00
|
|
|
if (r < 0) {
|
|
|
|
util_error("failed to generate key: %s", sc_strerror(r));
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
//TODO: deal with EC keys
|
|
|
|
util_error("Generating non-RSA keys is not yet implemented");
|
2018-10-03 09:25:15 +00:00
|
|
|
return EXIT_FAILURE;
|
2012-06-06 10:04:33 +00:00
|
|
|
}
|
2018-10-03 09:04:12 +00:00
|
|
|
|
2012-06-06 10:04:33 +00:00
|
|
|
sc_format_path("006E007300C5", &path);
|
|
|
|
r = sc_select_file(card, &path, &file);
|
2017-10-31 09:12:12 +00:00
|
|
|
if (r < 0) {
|
2018-10-03 09:25:15 +00:00
|
|
|
util_error("failed to retrieve fingerprints: %s", sc_strerror(r));
|
|
|
|
return EXIT_FAILURE;
|
2017-10-31 09:12:12 +00:00
|
|
|
}
|
2012-06-06 10:04:33 +00:00
|
|
|
r = sc_read_binary(card, 0, fingerprints, 60, 0);
|
|
|
|
if (r < 0) {
|
2018-10-03 09:25:15 +00:00
|
|
|
util_error("failed to retrieve fingerprints: %s", sc_strerror(r));
|
|
|
|
return EXIT_FAILURE;
|
2012-06-06 10:04:33 +00:00
|
|
|
}
|
2019-01-14 09:40:08 +00:00
|
|
|
printf("Fingerprint:\n%s\n", (char *)sc_dump_hex(fingerprints + 20*(in_key_id - 1), 20));
|
2018-10-03 09:04:12 +00:00
|
|
|
|
2018-10-03 09:25:15 +00:00
|
|
|
return EXIT_SUCCESS;
|
2012-06-06 10:04:33 +00:00
|
|
|
}
|
|
|
|
|
2019-01-14 09:40:08 +00:00
|
|
|
int do_verify(sc_card_t *card, char *type, const char *in_pin)
|
2012-06-06 10:04:33 +00:00
|
|
|
{
|
|
|
|
struct sc_pin_cmd_data data;
|
|
|
|
int tries_left;
|
|
|
|
int r;
|
2019-01-14 09:40:08 +00:00
|
|
|
if (!type || !in_pin)
|
2012-06-06 10:04:33 +00:00
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
|
|
|
|
if (strncasecmp("CHV", type, 3) != 0) {
|
2018-10-03 09:25:15 +00:00
|
|
|
util_error("invalid PIN type. Please use CHV1, CHV2 or CHV3");
|
2012-06-06 10:04:33 +00:00
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type[3] < '1' || type[3] > '3' || type[4] != '\0') {
|
2018-10-03 09:25:15 +00:00
|
|
|
util_error("invalid PIN reference. Please use CHV1, CHV2 or CHV3");
|
2012-06-06 10:04:33 +00:00
|
|
|
return SC_ERROR_INVALID_PIN_REFERENCE;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&data, 0, sizeof(struct sc_pin_cmd_data));
|
|
|
|
data.cmd = SC_PIN_CMD_VERIFY;
|
|
|
|
data.pin_type = SC_AC_CHV;
|
|
|
|
data.pin_reference = type[3] - '0';
|
2019-01-14 09:40:08 +00:00
|
|
|
data.pin1.data = (unsigned char *) in_pin;
|
|
|
|
data.pin1.len = (int)strlen(in_pin);
|
2012-06-06 10:04:33 +00:00
|
|
|
r = sc_pin_cmd(card, &data, &tries_left);
|
|
|
|
return r;
|
|
|
|
}
|
2012-05-19 18:28:25 +00:00
|
|
|
|
2013-03-04 11:13:03 +00:00
|
|
|
/**
|
|
|
|
* Delete key, for OpenPGP card.
|
|
|
|
* This function is not complete and is reserved for future version (> 2) of OpenPGP card.
|
|
|
|
**/
|
2019-01-14 09:40:08 +00:00
|
|
|
int delete_key_openpgp(sc_card_t *card, u8 in_key_id)
|
2013-03-04 11:13:03 +00:00
|
|
|
{
|
|
|
|
char *del_fingerprint = "00:DA:00:C6:14:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00";
|
|
|
|
char *del_creationtime = "00:DA:00:CD:04:00:00:00:00";
|
|
|
|
/* We need to replace the 4th byte later */
|
|
|
|
char *apdustring = NULL;
|
|
|
|
u8 buf[SC_MAX_APDU_BUFFER_SIZE];
|
|
|
|
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
|
|
|
|
sc_apdu_t apdu;
|
|
|
|
size_t len0;
|
|
|
|
int i;
|
|
|
|
int r = SC_SUCCESS;
|
|
|
|
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
|
if (i == 0) /* Reset fingerprint */
|
|
|
|
apdustring = del_fingerprint;
|
|
|
|
else /* Reset creation time */
|
|
|
|
apdustring = del_creationtime;
|
|
|
|
/* Convert the string to binary array */
|
|
|
|
len0 = sizeof(buf);
|
|
|
|
sc_hex_to_bin(apdustring, buf, &len0);
|
|
|
|
|
|
|
|
/* Replace DO tag, subject to key ID */
|
2019-01-14 09:40:08 +00:00
|
|
|
buf[3] = buf[3] + in_key_id;
|
2013-03-04 11:13:03 +00:00
|
|
|
|
|
|
|
/* Build APDU from binary array */
|
|
|
|
r = sc_bytes2apdu(card->ctx, buf, len0, &apdu);
|
|
|
|
if (r) {
|
2018-10-03 09:25:15 +00:00
|
|
|
util_error("failed to build APDU: %s", sc_strerror(r));
|
2013-11-02 18:53:35 +00:00
|
|
|
return r;
|
2013-03-04 11:13:03 +00:00
|
|
|
}
|
|
|
|
apdu.resp = rbuf;
|
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
|
|
|
|
/* Send APDU to card */
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2013-11-02 18:53:35 +00:00
|
|
|
if (r) {
|
2018-10-03 09:25:15 +00:00
|
|
|
util_error("transmitting APDU failed: %s", sc_strerror(r));
|
2013-11-02 18:53:35 +00:00
|
|
|
return r;
|
|
|
|
}
|
2013-03-04 11:13:03 +00:00
|
|
|
}
|
|
|
|
/* TODO: Rewrite Extended Header List.
|
|
|
|
* Not support by OpenGPG v2 yet */
|
2013-11-03 03:26:25 +00:00
|
|
|
return r;
|
2013-03-04 11:13:03 +00:00
|
|
|
}
|
|
|
|
|
2019-01-14 09:40:08 +00:00
|
|
|
int do_delete_key(sc_card_t *card, u8 in_key_id)
|
2013-03-04 11:13:03 +00:00
|
|
|
{
|
2013-11-03 03:26:25 +00:00
|
|
|
sc_path_t path;
|
2013-03-04 11:13:03 +00:00
|
|
|
int r = SC_SUCCESS;
|
|
|
|
|
|
|
|
/* Currently, only Gnuk supports deleting keys */
|
|
|
|
if (card->type != SC_CARD_TYPE_OPENPGP_GNUK) {
|
2018-10-03 09:25:15 +00:00
|
|
|
util_error("only Gnuk supports deleting keys. General OpenPGP doesn't");
|
2013-03-04 11:13:03 +00:00
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
|
2019-01-14 09:40:08 +00:00
|
|
|
if (in_key_id < 1 || (in_key_id > 3 && in_key_id != 'a')) {
|
|
|
|
util_error("invalid key id %d", in_key_id);
|
2013-03-04 11:13:03 +00:00
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
}
|
2018-10-03 09:25:15 +00:00
|
|
|
|
2019-01-14 09:40:08 +00:00
|
|
|
if (in_key_id == 1 || in_key_id == 'a') {
|
2013-11-03 03:26:25 +00:00
|
|
|
sc_format_path("B601", &path);
|
|
|
|
r |= sc_delete_file(card, &path);
|
2013-03-04 11:13:03 +00:00
|
|
|
}
|
2019-01-14 09:40:08 +00:00
|
|
|
if (in_key_id == 2 || in_key_id == 'a') {
|
2013-11-03 03:26:25 +00:00
|
|
|
sc_format_path("B801", &path);
|
|
|
|
r |= sc_delete_file(card, &path);
|
2013-03-04 11:13:03 +00:00
|
|
|
}
|
2019-01-14 09:40:08 +00:00
|
|
|
if (in_key_id == 3 || in_key_id == 'a') {
|
2013-11-03 03:26:25 +00:00
|
|
|
sc_format_path("A401", &path);
|
|
|
|
r |= sc_delete_file(card, &path);
|
2013-03-04 11:13:03 +00:00
|
|
|
}
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2013-03-04 04:28:08 +00:00
|
|
|
int do_erase(sc_card_t *card)
|
|
|
|
{
|
|
|
|
printf("Erase card\n");
|
2018-01-05 10:08:14 +00:00
|
|
|
return sc_card_ctl(card, SC_CARDCTL_ERASE_CARD, NULL);
|
2013-03-04 04:28:08 +00:00
|
|
|
}
|
|
|
|
|
2012-05-19 18:28:25 +00:00
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
sc_context_t *ctx = NULL;
|
|
|
|
sc_context_param_t ctx_param;
|
|
|
|
sc_card_t *card = NULL;
|
|
|
|
int r;
|
2012-05-26 16:52:05 +00:00
|
|
|
int argind = 0;
|
2014-08-20 19:45:42 +00:00
|
|
|
int exit_status = EXIT_SUCCESS;
|
2012-05-19 18:28:25 +00:00
|
|
|
|
2012-05-26 16:52:05 +00:00
|
|
|
/* decode options */
|
2012-05-19 18:28:25 +00:00
|
|
|
argind = decode_options(argc, argv);
|
|
|
|
|
|
|
|
/* connect to the card */
|
|
|
|
memset(&ctx_param, 0, sizeof(ctx_param));
|
|
|
|
ctx_param.ver = 0;
|
|
|
|
ctx_param.app_name = app_name;
|
|
|
|
|
|
|
|
r = sc_context_create(&ctx, &ctx_param);
|
|
|
|
if (r) {
|
2018-10-03 09:25:15 +00:00
|
|
|
util_fatal("failed to establish context: %s", sc_strerror(r));
|
2012-05-26 16:52:05 +00:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = util_connect_card(ctx, &card, opt_reader, opt_wait, verbose);
|
2012-05-19 18:28:25 +00:00
|
|
|
if (r) {
|
2019-03-06 18:54:35 +00:00
|
|
|
sc_release_context(ctx);
|
2018-10-03 09:25:15 +00:00
|
|
|
util_fatal("failed to connect to card: %s", sc_strerror(r));
|
2012-05-26 16:52:05 +00:00
|
|
|
return EXIT_FAILURE;
|
2012-05-19 18:28:25 +00:00
|
|
|
}
|
|
|
|
|
2012-05-26 16:52:05 +00:00
|
|
|
/* check card type */
|
2018-01-05 10:08:14 +00:00
|
|
|
if ((card->type != SC_CARD_TYPE_OPENPGP_BASE) &&
|
|
|
|
(card->type != SC_CARD_TYPE_OPENPGP_V1) &&
|
|
|
|
(card->type != SC_CARD_TYPE_OPENPGP_V2) &&
|
|
|
|
(card->type != SC_CARD_TYPE_OPENPGP_V3) &&
|
|
|
|
(card->type != SC_CARD_TYPE_OPENPGP_GNUK)) {
|
2018-10-03 09:25:15 +00:00
|
|
|
util_error("card type %X: not an OpenPGP card", card->type);
|
2012-05-26 16:52:05 +00:00
|
|
|
exit_status = EXIT_FAILURE;
|
2012-05-19 18:28:25 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2012-05-26 16:52:05 +00:00
|
|
|
/* fail on too many arguments */
|
2012-05-19 18:28:25 +00:00
|
|
|
if (argind > argc)
|
2012-05-26 16:53:09 +00:00
|
|
|
util_print_usage_and_die(app_name, options, option_help, NULL);
|
2012-05-19 18:28:25 +00:00
|
|
|
|
2012-05-26 16:52:05 +00:00
|
|
|
/* set default action */
|
|
|
|
if (!actions)
|
|
|
|
opt_userinfo = 1;
|
|
|
|
|
2015-10-12 21:26:54 +00:00
|
|
|
if (opt_cardinfo)
|
|
|
|
exit_status |= do_info(card, card_data);
|
|
|
|
|
2012-05-26 16:52:05 +00:00
|
|
|
if (opt_userinfo)
|
2015-10-12 21:26:54 +00:00
|
|
|
exit_status |= do_info(card, user_data);
|
2012-05-19 18:28:25 +00:00
|
|
|
|
2018-06-23 18:41:20 +00:00
|
|
|
if (opt_keyinfo)
|
|
|
|
exit_status |= do_info(card, key_data);
|
|
|
|
|
2012-06-06 10:04:33 +00:00
|
|
|
if (opt_verify && opt_pin) {
|
|
|
|
exit_status |= do_verify(card, verifytype, pin);
|
|
|
|
}
|
|
|
|
|
2014-08-12 20:54:22 +00:00
|
|
|
if (opt_dump_do) {
|
2018-05-20 14:01:34 +00:00
|
|
|
size_t n;
|
|
|
|
|
|
|
|
for (n = 0; n < opt_dump_do; n++) {
|
|
|
|
exit_status |= do_dump_do(card, do_dump_idx[n]);
|
|
|
|
}
|
2014-08-12 20:54:22 +00:00
|
|
|
}
|
|
|
|
|
2012-06-06 10:04:33 +00:00
|
|
|
if (opt_genkey)
|
2018-10-03 09:04:12 +00:00
|
|
|
exit_status |= do_genkey(card, key_id, keytype);
|
2012-06-06 10:04:33 +00:00
|
|
|
|
2012-05-19 18:28:25 +00:00
|
|
|
if (exec_program) {
|
|
|
|
char *const largv[] = {exec_program, NULL};
|
|
|
|
sc_unlock(card);
|
|
|
|
sc_disconnect_card(card);
|
|
|
|
sc_release_context(ctx);
|
2015-08-08 06:17:12 +00:00
|
|
|
#ifndef _WIN32
|
2012-05-19 18:28:25 +00:00
|
|
|
execv(exec_program, largv);
|
2015-08-08 06:17:12 +00:00
|
|
|
#else
|
2017-01-25 22:27:27 +00:00
|
|
|
_execv(exec_program, (const char * const*)largv);
|
2015-08-08 06:17:12 +00:00
|
|
|
#endif
|
2012-05-19 18:28:25 +00:00
|
|
|
/* we should not get here */
|
|
|
|
perror("execv()");
|
2012-05-26 16:52:05 +00:00
|
|
|
exit(EXIT_FAILURE);
|
2012-05-19 18:28:25 +00:00
|
|
|
}
|
|
|
|
|
2013-03-04 11:13:03 +00:00
|
|
|
if (opt_delkey)
|
2014-12-11 04:51:15 +00:00
|
|
|
exit_status |= do_delete_key(card, key_id);
|
2013-03-04 11:13:03 +00:00
|
|
|
|
2013-03-04 04:28:08 +00:00
|
|
|
if (opt_erase)
|
2014-12-11 04:51:15 +00:00
|
|
|
exit_status |= do_erase(card);
|
2013-03-04 04:28:08 +00:00
|
|
|
|
2012-05-19 18:28:25 +00:00
|
|
|
out:
|
|
|
|
sc_unlock(card);
|
|
|
|
sc_disconnect_card(card);
|
|
|
|
sc_release_context(ctx);
|
2012-05-26 16:52:05 +00:00
|
|
|
|
2012-05-19 18:28:25 +00:00
|
|
|
exit(exit_status);
|
|
|
|
}
|