opensc/src/tools/openpgp-tool.c

1000 lines
27 KiB
C

/*
* openpgp-tool.c: OpenPGP card utility
*
* Copyright (C) 2012-2020 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>
/* For dup() and dup2() functions */
#ifndef _WIN32
#include <unistd.h>
#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>
#endif
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <getopt.h>
#include "libopensc/opensc.h"
#include "libopensc/asn1.h"
#include "libopensc/cards.h"
#include "libopensc/internal.h"
#include "libopensc/cardctl.h"
#include "libopensc/log.h"
#include "libopensc/errors.h"
#include "util.h"
#include "libopensc/log.h"
#include "libopensc/card-openpgp.h"
#define OPT_RAW 256
#define OPT_PRETTY 257
#define OPT_VERIFY 258
#define OPT_PIN 259
#define OPT_DELKEY 260
enum code_types {
TYPE_NULL,
TYPE_HEX,
TYPE_STRING
};
/* define structures */
struct ef_name_map {
const char *name;
const char *env_name;
const char *ef;
enum code_types type;
size_t offset;
size_t length; /* exact length: 0 <=> potentially infinite */
char *(*prettify_value)(u8 *, size_t);
};
/* declare functions */
static void show_version(void);
static char *prettify_hex(u8 *data, size_t length, char *buffer, size_t buflen);
static char *prettify_algorithm(u8 *data, size_t length);
static char *prettify_date(u8 *data, size_t length);
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);
static int decode_options(int argc, char **argv);
static int do_info(sc_card_t *card, const struct ef_name_map *map);
/* define global variables */
static int actions = 0;
static char *opt_reader = NULL;
static int opt_wait = 0;
static int opt_raw = 0;
static int verbose = 0;
static int opt_userinfo = 0;
static int opt_cardinfo = 0;
static int opt_keyinfo = 0;
static char *exec_program = NULL;
static int opt_genkey = 0;
static u8 key_id = 0;
static char *opt_keytype = NULL;
static int opt_verify = 0;
static char *verifytype = NULL;
static int opt_pin = 0;
static const char *pin = NULL;
static int opt_erase = 0;
static int opt_delkey = 0;
static size_t opt_dump_do = 0;
static unsigned int do_dump_idx[200]; /* large enough and checked on input */
static const char *app_name = "openpgp-tool";
static const struct option options[] = {
{ "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' },
{ "key-info", no_argument, NULL, 'K' },
{ "gen-key", required_argument, NULL, 'G' },
{ "key-type", required_argument, NULL, 't' },
{ "help", no_argument, NULL, 'h' },
{ "verbose", no_argument, NULL, 'v' },
{ "version", no_argument, NULL, 'V' },
{ "erase", no_argument, NULL, 'E' },
{ "verify", required_argument, NULL, OPT_VERIFY },
{ "pin", required_argument, NULL, OPT_PIN },
{ "del-key", required_argument, NULL, OPT_DELKEY },
{ "do", required_argument, NULL, 'd' },
{ NULL, 0, NULL, 0 }
};
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",
/* C */ "Show card information",
/* U */ "Show card holder information",
/* K */ "Show key information",
/* G */ "Generate key",
/* t */ "Key type (default: rsa2048)",
/* h */ "Print this help message",
/* v */ "Verbose operation. Use several times to enable debug output.",
/* V */ "Show version number",
/* E */ "Erase (reset) the card",
"Verify PIN (CHV1, CHV2, CHV3...)",
"PIN string",
"Delete key (1, 2, 3 or all)",
/* d */ "Dump private data object number <arg> (i.e. DO <arg>)",
};
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 }
};
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 }
};
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 }
};
static void show_version(void)
{
fprintf(stderr,
"openpgp-tool - OpenPGP card utility version " PACKAGE_VERSION "\n"
"\n"
"Copyright (c) 2012-2020 Peter Marschall <peter@adpm.de>\n"
"Licensed under LGPL v2\n");
}
/* 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, ':');
if (r == SC_SUCCESS)
return buffer;
}
return NULL;
}
/* 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) {
time_t time = (time_t) (data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]);
struct tm tm;
static char result[64]; /* large enough */
#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);
return result;
}
return NULL;
}
#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 0x000B: return "Feitian Technologies";
case 0x002A: return "Magrathea";
case 0x0042: return "GnuPG e.V.";
case 0x1337: return "Warsaw Hackerspace";
case 0x2342: return "warpzone"; /* hackerspace Muenster. */
case 0x4354: return "Confidential Technologies"; /* cotech.de */
case 0x5443: return "TIF-IT e.V.";
case 0x63AF: return "Trustica";
case 0xBA53: return "c-base e.V.";
case 0xBD0E: return "Paranoidlabs";
case 0xF517: return "FSIJ";
case 0xF5EC: return "F-Secure";
/* 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 */
unsigned long serial = (unsigned long) (data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]);
sprintf(result, "%08lX", serial);
return result;
}
return NULL;
}
/* prettify card holder's name */
static char *prettify_name(u8 *data, size_t length)
{
if (data != NULL && length > 0) {
char *src = (char *) data;
char *dst = (char *) data;
while (*src != '\0' && length > 0) {
*dst = *src++;
length--;
if (*dst == '<') {
if (length > 0 && *src == '<') {
src++;
length--;
}
*dst = ' ';
}
dst++;
}
*dst = '\0';
return (char *) data;
}
return NULL;
}
/* prettify language */
static char *prettify_language(u8 *data, size_t length)
{
if (data != NULL && length > 0) {
char *str = (char *) data;
switch (strlen(str)) {
case 8: memmove(str+7, str+6, 1+strlen(str+6));
str[6] = ',';
/* fall through */
case 6: memmove(str+5, str+4, 1+strlen(str+4));
str[4] = ',';
/* fall through */
case 4: memmove(str+3, str+2, 1+strlen(str+2));
str[2] = ',';
/* fall through */
case 2: return str;
}
}
return NULL;
}
/* convert the raw ISO-5218 SEX value to an english word */
static char *prettify_gender(u8 *data, size_t length)
{
if (data != NULL && length > 0) {
switch (*data) {
case '0': return "unknown";
case '1': return "male";
case '2': return "female";
case '9': return "not announced";
}
}
return NULL;
}
#define INDENT 16
static void display_data(const struct ef_name_map *map, u8 *data, size_t length)
{
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;
}
}
if (value != NULL) {
if (exec_program) {
char *envvar= malloc(strlen(map->env_name) +
strlen(value) + 2);
if (envvar != NULL) {
strcpy(envvar, map->env_name);
strcat(envvar, "=");
strcat(envvar, value);
putenv(envvar);
/* envvar deliberately kept: see putenv(3) */
}
}
else {
const char *label = map->name;
int fill = (int) (INDENT - strlen(label));
printf("%s:%*s%s\n", label, fill, "", value);
}
}
}
}
static int decode_options(int argc, char **argv)
{
int c;
char *endptr;
unsigned long val;
while ((c = getopt_long(argc, argv,"r:x:CUG:KEht:wvVd:", options, (int *) 0)) != -1) {
switch (c) {
case 'r':
opt_reader = optarg;
break;
case 'x':
if (exec_program)
free(exec_program);
exec_program = strdup(optarg);
break;
case OPT_RAW:
opt_raw = 1;
break;
case OPT_PRETTY:
opt_raw = 0;
break;
case OPT_VERIFY:
opt_verify++;
if (verifytype)
free(verifytype);
verifytype = strdup(optarg);
actions++;
break;
case OPT_PIN:
opt_pin++;
util_get_pin(optarg, &pin);
break;
case 'C':
opt_cardinfo++;
actions++;
break;
case 'U':
opt_userinfo++;
actions++;
break;
case 'K':
opt_keyinfo++;
actions++;
break;
case 'G':
opt_genkey++;
key_id = optarg[0] - '0';
actions++;
break;
case 't':
if (opt_keytype)
free(opt_keytype);
opt_keytype = strdup(optarg);
break;
case 'h':
util_print_usage_and_die(app_name, options, option_help, NULL);
break;
case 'w':
opt_wait = 1;
break;
case 'v':
verbose++;
break;
case 'V':
show_version();
exit(EXIT_SUCCESS);
break;
case 'E':
opt_erase++;
actions++;
break;
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';
actions++;
break;
case 'd':
endptr = NULL;
val = strtoul(optarg, &endptr, 16);
if (endptr == NULL || endptr == optarg || *endptr != '\0') {
util_error("Unable to parse DO identifier");
exit(EXIT_FAILURE);
}
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++;
}
actions++;
break;
default:
util_print_usage_and_die(app_name, options, option_help, NULL);
}
}
return optind;
}
static int do_info(sc_card_t *card, const struct ef_name_map *map)
{
int i;
u8 buf[2048];
for (i = 0; map[i].ef != NULL; i++) {
sc_path_t path;
sc_file_t *file;
size_t count;
int r;
sc_format_path(map[i].ef, &path);
r = sc_select_file(card, &path, &file);
if (r) {
util_error("failed to select EF %s: %s", map[i].ef, sc_strerror(r));
return EXIT_FAILURE;
}
count = file->size;
if (!count)
continue;
if (count > sizeof(buf) - 1) {
util_error("too small buffer to read the OpenPGP map");
return EXIT_FAILURE;
}
r = sc_read_binary(card, 0, buf, count, 0);
if (r < 0) {
util_error("failed to read %s: %s", map[i].ef, sc_strerror(r));
return EXIT_FAILURE;
}
if (r != (signed) count || (size_t) r < map[i].offset + map[i].length) {
util_error("%s: expecting %"SC_FORMAT_LEN_SIZE_T"d bytes, got only %d",
map[i].ef, count, r);
return EXIT_FAILURE;
}
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';
display_data(&map[i], buf, count);
}
return EXIT_SUCCESS;
}
static int do_dump_do(sc_card_t *card, unsigned int tag)
{
struct pgp_priv_data *priv = DRVDATA(card);
int r;
size_t length;
unsigned char *buffer;
if (tag < 0x101 || tag > 0x104) {
util_error("illegal DO identifier %04X", tag);
return SC_ERROR_INVALID_ARGUMENTS;
}
buffer = calloc(priv->max_specialDO_size, sizeof(unsigned char));
if (buffer == NULL) {
util_error("error allocating memory for DO %04X", tag);
return SC_ERROR_OUT_OF_MEMORY;
}
r = sc_get_data(card, tag, buffer, priv->max_specialDO_size);
if (r < 0) {
free(buffer);
util_error("failed to get data object DO %04X: %s", tag, sc_strerror(r));
if (SC_ERROR_SECURITY_STATUS_NOT_SATISFIED == r) {
util_error("make sure the 'verify' and 'pin' parameters are correct");
}
return r;
}
length = (size_t) r; /* r is guaranteed to be non-negative */
if (opt_raw) {
int tmp;
FILE *fp;
#ifndef _WIN32
tmp = dup(fileno(stdout));
#else
tmp = _dup(_fileno(stdout));
#endif
if (tmp < 0)
return EXIT_FAILURE;
fp = freopen(NULL, "wb", stdout);
if (fp) {
r = (int) fwrite(buffer, sizeof(char), length, fp);
}
#ifndef _WIN32
dup2(tmp, fileno(stdout));
#else
_dup2(tmp, _fileno(stdout));
#endif
clearerr(stdout);
close(tmp);
if (length != (size_t) r) { /* fail on write errors */
free(buffer);
return EXIT_FAILURE;
}
} else {
util_hex_dump_asc(stdout, buffer, length, -1);
}
free(buffer);
return EXIT_SUCCESS;
}
int do_genkey(sc_card_t *card, u8 in_key_id, const char *keytype)
{
int r;
sc_cardctl_openpgp_keygen_info_t key_info;
u8 fingerprints[60];
sc_path_t path;
sc_file_t *file;
/* validate in_key_id */
if (in_key_id < 1 || in_key_id > 3) {
util_error("unknown key ID %d", in_key_id);
return SC_ERROR_INVALID_ARGUMENTS;
}
/* fall back to RSA 2048 if keytype is not given */
if (!keytype)
keytype = "RSA2048";
memset(&key_info, 0, sizeof(sc_cardctl_openpgp_keygen_info_t));
/* generate key depending on keytype passed */
if (strncasecmp("RSA", keytype, strlen("RSA")) == 0) {
const char *keylen_ptr = keytype + strlen("RSA");
size_t keylen = 2048; /* default key length for RSA keys */
size_t expolen = 32; /* default exponent length for RSA keys */
u8 keyformat = SC_OPENPGP_KEYFORMAT_RSA_STD; /* default keyformat */
char pathstr[SC_MAX_PATH_STRING_SIZE];
/* 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;
}
}
/* get some algorithm attributes from respective DO - ignore errors */
snprintf(pathstr, sizeof(pathstr), "006E007300C%d", in_key_id);
sc_format_path(pathstr, &path);
if (sc_select_file(card, &path, &file) >= 0) {
u8 attrs[6]; /* algorithm attrs DO for RSA is <= 6 bytes */
r = sc_read_binary(card, 0, attrs, sizeof(attrs), 0);
if (r >= 5 && attrs[0] == SC_OPENPGP_KEYALGO_RSA) {
expolen = (unsigned short) attrs[3] << 8
| (unsigned short) attrs[4];
if (r > 5)
keyformat = attrs[5];
}
}
/* set key_info */
key_info.key_id = in_key_id;
key_info.algorithm = SC_OPENPGP_KEYALGO_RSA;
key_info.u.rsa.modulus_len = keylen;
key_info.u.rsa.modulus = calloc(BYTES4BITS(keylen), 1);
key_info.u.rsa.exponent_len = expolen;
key_info.u.rsa.exponent = calloc(BYTES4BITS(expolen), 1);
key_info.u.rsa.keyformat = keyformat;
r = sc_card_ctl(card, SC_CARDCTL_OPENPGP_GENERATE_KEY, &key_info);
free(key_info.u.rsa.modulus);
free(key_info.u.rsa.exponent);
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");
return EXIT_FAILURE;
}
sc_format_path("006E007300C5", &path);
r = sc_select_file(card, &path, &file);
if (r < 0) {
util_error("failed to retrieve fingerprints: %s", sc_strerror(r));
return EXIT_FAILURE;
}
r = sc_read_binary(card, 0, fingerprints, 60, 0);
if (r < 0) {
util_error("failed to retrieve fingerprints: %s", sc_strerror(r));
return EXIT_FAILURE;
}
printf("Fingerprint:\n%s\n", (char *)sc_dump_hex(fingerprints + 20*(in_key_id - 1), 20));
return EXIT_SUCCESS;
}
int do_verify(sc_card_t *card, char *type, const char *in_pin)
{
struct sc_pin_cmd_data data;
int tries_left;
int r;
if (!type || !in_pin)
return SC_ERROR_INVALID_ARGUMENTS;
if (strncasecmp("CHV", type, 3) != 0) {
util_error("invalid PIN type. Please use CHV1, CHV2 or CHV3");
return SC_ERROR_INVALID_ARGUMENTS;
}
if (type[3] < '1' || type[3] > '3' || type[4] != '\0') {
util_error("invalid PIN reference. Please use CHV1, CHV2 or CHV3");
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';
data.pin1.data = (unsigned char *) in_pin;
data.pin1.len = (int)strlen(in_pin);
r = sc_pin_cmd(card, &data, &tries_left);
return r;
}
/**
* Delete key, for OpenPGP card.
* This function is not complete and is reserved for future version (> 2) of OpenPGP card.
**/
int delete_key_openpgp(sc_card_t *card, u8 in_key_id)
{
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 */
buf[3] = buf[3] + in_key_id;
/* Build APDU from binary array */
r = sc_bytes2apdu(card->ctx, buf, len0, &apdu);
if (r) {
util_error("failed to build APDU: %s", sc_strerror(r));
return r;
}
apdu.resp = rbuf;
apdu.resplen = sizeof(rbuf);
/* Send APDU to card */
r = sc_transmit_apdu(card, &apdu);
if (r) {
util_error("transmitting APDU failed: %s", sc_strerror(r));
return r;
}
}
/* TODO: Rewrite Extended Header List.
* Not support by OpenGPG v2 yet */
return r;
}
int do_delete_key(sc_card_t *card, u8 in_key_id)
{
sc_path_t path;
int r = SC_SUCCESS;
/* Currently, only Gnuk supports deleting keys */
if (card->type != SC_CARD_TYPE_OPENPGP_GNUK) {
util_error("only Gnuk supports deleting keys. General OpenPGP doesn't");
return SC_ERROR_NOT_SUPPORTED;
}
if (in_key_id < 1 || (in_key_id > 3 && in_key_id != 'a')) {
util_error("invalid key id %d", in_key_id);
return SC_ERROR_INVALID_ARGUMENTS;
}
if (in_key_id == 1 || in_key_id == 'a') {
sc_format_path("B601", &path);
r |= sc_delete_file(card, &path);
}
if (in_key_id == 2 || in_key_id == 'a') {
sc_format_path("B801", &path);
r |= sc_delete_file(card, &path);
}
if (in_key_id == 3 || in_key_id == 'a') {
sc_format_path("A401", &path);
r |= sc_delete_file(card, &path);
}
return r;
}
int do_erase(sc_card_t *card)
{
printf("Erase card\n");
return sc_card_ctl(card, SC_CARDCTL_ERASE_CARD, NULL);
}
int main(int argc, char **argv)
{
sc_context_t *ctx = NULL;
sc_context_param_t ctx_param;
sc_card_t *card = NULL;
int r;
int argind = 0;
int exit_status = EXIT_SUCCESS;
/* decode options */
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) {
util_fatal("failed to establish context: %s", sc_strerror(r));
return EXIT_FAILURE;
}
/* force OpenPGP card driver */
r = sc_set_card_driver(ctx, "openpgp");
if (r) {
sc_release_context(ctx);
util_fatal("OpenPGP card driver not found!\n");
return EXIT_FAILURE;
}
r = util_connect_card(ctx, &card, opt_reader, opt_wait, verbose);
if (r) {
sc_release_context(ctx);
util_fatal("failed to connect to card: %s", sc_strerror(r));
return EXIT_FAILURE;
}
/* check card type */
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)) {
util_error("card type %X: not an OpenPGP card", card->type);
exit_status = EXIT_FAILURE;
goto out;
}
/* fail on too many arguments */
if (argind > argc)
util_print_usage_and_die(app_name, options, option_help, NULL);
/* set default action */
if (!actions)
opt_userinfo = 1;
if (opt_cardinfo)
exit_status |= do_info(card, card_data);
if (opt_userinfo)
exit_status |= do_info(card, user_data);
if (opt_keyinfo)
exit_status |= do_info(card, key_data);
if (opt_verify && opt_pin) {
exit_status |= do_verify(card, verifytype, pin);
}
if (opt_dump_do) {
size_t n;
for (n = 0; n < opt_dump_do; n++) {
exit_status |= do_dump_do(card, do_dump_idx[n]);
}
}
if (opt_genkey)
exit_status |= do_genkey(card, key_id, opt_keytype);
if (exec_program) {
char *const largv[] = {exec_program, NULL};
sc_unlock(card);
sc_disconnect_card(card);
sc_release_context(ctx);
#ifndef _WIN32
execv(exec_program, largv);
#else
_execv(exec_program, (const char * const*)largv);
#endif
/* we should not get here */
perror("execv()");
exit(EXIT_FAILURE);
}
if (opt_delkey)
exit_status |= do_delete_key(card, key_id);
if (opt_erase)
exit_status |= do_erase(card);
out:
sc_unlock(card);
sc_disconnect_card(card);
sc_release_context(ctx);
exit(exit_status);
}