<commit_before>/*
 * pkcs15-tool.c: Tool for poking with PKCS #15 smart cards
 *
 * Copyright (C) 2001  <NAME> <<EMAIL>>
 * Copyright (C) 2008  <NAME> <<EMAIL>>
 *
 * 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"

#ifdef __APPLE__
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 500
#endif
#include <assert.h>
#include <ctype.h>
#ifdef _WIN32
#ifdef __MINGW32__
// work around for https://sourceforge.net/p/mingw-w64/bugs/476/
#include <windows.h>
#endif
#include <shellapi.h>
#include <tchar.h>
#else
#include <ftw.h>
#endif
#include <stdio.h>

#ifdef ENABLE_OPENSSL
#if defined(HAVE_INTTYPES_H)
#include <inttypes.h>
#elif defined(HAVE_STDINT_H)
#include <stdint.h>
#elif defined(_MSC_VER)
typedef unsigned __int32 uint32_t;
#else
#warning no uint32_t type available, please contact <EMAIL>
#endif
#include <openssl/bn.h>
#include <openssl/crypto.h>
#endif
#include <limits.h>

#include "libopensc/pkcs15.h"
#include "libopensc/asn1.h"
#include "util.h"
#include "pkcs11/pkcs11-display.h"

static const char *app_name = "pkcs15-tool";

static int opt_wait = 0;
static int opt_no_cache = 0;
static int opt_clear_cache = 0;
static char * opt_auth_id = NULL;
static char * opt_reader = NULL;
static char * opt_cert = NULL;
static char * opt_data = NULL;
static int opt_raw = 0;
static char * opt_pubkey = NULL;
static char * opt_outfile = NULL;
static char * opt_bind_to_aid = NULL;
static const char * opt_newpin = NULL;
static const char * opt_pin = NULL;
static const char * opt_puk = NULL;
static int	compact = 0;
static int	verbose = 0;
static int opt_use_pinpad = 0;
#if defined(ENABLE_OPENSSL) && (defined(_WIN32) || defined(HAVE_INTTYPES_H))
static int opt_rfc4716 = 0;
#endif

enum {
	OPT_CHANGE_PIN = 0x100,
	OPT_LIST_PINS,
	OPT_READER,
	OPT_TEST_SESSION_PIN,
	OPT_PIN_ID,
	OPT_NO_CACHE,
	OPT_CLEAR_CACHE,
	OPT_LIST_PUB,
	OPT_READ_PUB,
#if defined(ENABLE_OPENSSL) && (defined(_WIN32) || defined(HAVE_INTTYPES_H))
	OPT_READ_SSH,
	OPT_RFC4716,
#endif
	OPT_PIN,
	OPT_NEWPIN,
	OPT_PUK,
	OPT_VERIFY_PIN,
	OPT_BIND_TO_AID,
	OPT_LIST_APPLICATIONS,
	OPT_LIST_SKEYS,
	OPT_USE_PINPAD,
	OPT_USE_PINPAD_DEPRECATED,
	OPT_RAW,
	OPT_PRINT_VERSION,
	OPT_LIST_INFO,
	OPT_READ_CERT,
};

#define NELEMENTS(x)	(sizeof(x)/sizeof((x)[0]))

static int	authenticate(sc_pkcs15_object_t *obj);

static const struct option options[] = {
	{ "version",		0, NULL,			OPT_PRINT_VERSION },
	{ "list-info",	no_argument, NULL,		OPT_LIST_INFO },
	{ "list-applications",	no_argument, NULL,		OPT_LIST_APPLICATIONS },
	{ "read-certificate",	required_argument, NULL,	OPT_READ_CERT },
	{ "list-certificates",	no_argument, NULL,		'c' },
	{ "read-data-object",	required_argument, NULL,	'R' },
	{ "raw",		no_argument, NULL,		OPT_RAW },
	{ "list-data-objects",	no_argument, NULL,		'C' },
	{ "list-pins",		no_argument, NULL,		OPT_LIST_PINS },
	{ "list-secret-keys",	no_argument, NULL,		OPT_LIST_SKEYS },
	{ "short",			no_argument, NULL,		's' },
	{ "dump",		no_argument, NULL,		'D' },
	{ "unblock-pin",	no_argument, NULL,		'u' },
	{ "change-pin",		no_argument, NULL,		OPT_CHANGE_PIN },
	{ "list-keys",		no_argument, NULL,		'k' },
	{ "list-public-keys",	no_argument, NULL,		OPT_LIST_PUB },
	{ "read-public-key",	required_argument, NULL,	OPT_READ_PUB },
#if defined(ENABLE_OPENSSL) && (defined(_WIN32) || defined(HAVE_INTTYPES_H))
	{ "read-ssh-key",	required_argument, NULL,	OPT_READ_SSH },
	{ "rfc4716",		no_argument, NULL,		OPT_RFC4716 },
#endif
	{ "test-update",	no_argument, NULL,		'T' },
	{ "update",		no_argument, NULL,		'U' },
	{ "reader",		required_argument, NULL,	OPT_READER },
	{ "pin",		required_argument, NULL,	OPT_PIN },
	{ "new-pin",		required_argument, NULL,	OPT_NEWPIN },
	{ "puk",		required_argument, NULL,	OPT_PUK },
	{ "verify-pin",		no_argument, NULL,		OPT_VERIFY_PIN },
	{ "test-session-pin",	no_argument, NULL,		OPT_TEST_SESSION_PIN },
	{ "output",		required_argument, NULL,	'o' },
	{ "no-cache",		no_argument, NULL,		OPT_NO_CACHE },
	{ "clear-cache",	no_argument, NULL,		OPT_CLEAR_CACHE },
	{ "auth-id",		required_argument, NULL,	'a' },
	{ "aid",		required_argument, NULL,	OPT_BIND_TO_AID },
	{ "wait",		no_argument, NULL,		'w' },
	{ "verbose",		no_argument, NULL,		'v' },
	{ "use-pinpad",		no_argument, NULL,		OPT_USE_PINPAD },
	{ "no-prompt",		no_argument, NULL,		OPT_USE_PINPAD_DEPRECATED },
	{ NULL, 0, NULL, 0 }
};

static const char *option_help[] = {
	"Print OpenSC package version",
	"List card information",
	"List the on-card PKCS#15 applications",
	"Read certificate with ID <arg>",
	"List certificates",
	"Reads data object with OID, applicationName or label <arg>",
	"Outputs raw 8 bit data to stdout. File output will not be affected by this, it always uses raw mode.",
	"List data objects",
	"List PIN codes",
	"List secret keys",
	"Output lists in compact format",
	"List all card objects",
	"Unblock PIN code",
	"Change PIN or PUK code",
	"List private keys",
	"List public keys",
	"Reads public key with ID <arg>",
#if defined(ENABLE_OPENSSL) && (defined(_WIN32) || defined(HAVE_INTTYPES_H))
	"Reads public key with ID <arg>, outputs ssh format",
	"Outputs the public key in RFC 4716 format (requires --read-ssh-key)",
#endif
	"Test if the card needs a security update",
	"Update the card with a security update",
	"Uses reader number <arg>",
	"Specify PIN",
	"Specify New PIN (when changing or unblocking)",
	"Specify Unblock PIN",
	"Verify PIN after card binding (without 'auth-id' the first non-SO, non-Unblock PIN will be verified)",
	"Equivalent to --verify-pin with additional session PIN generation",
	"Outputs to file <arg>",
	"Disable card caching",
	"Clear card caching",
	"The auth ID of the PIN to use",
	"Specify AID of the on-card PKCS#15 application to bind to (in hexadecimal form)",
	"Wait for card insertion",
	"Verbose operation. Use several times to enable debug output.",
	"Do not prompt the user; if no PINs supplied, pinpad will be used.",
	NULL,
	NULL
};

static sc_context_t *ctx = NULL;
static sc_card_t *card = NULL;
static struct sc_pkcs15_card *p15card = NULL;

struct _access_rule_text {
	unsigned flag;
	const char *label;
} _access_rules_text[] = {
	{SC_PKCS15_ACCESS_RULE_MODE_READ, "read"},
	{SC_PKCS15_ACCESS_RULE_MODE_UPDATE, "update"},
	{SC_PKCS15_ACCESS_RULE_MODE_EXECUTE, "execute"},
	{SC_PKCS15_ACCESS_RULE_MODE_DELETE, "delete"},
	{SC_PKCS15_ACCESS_RULE_MODE_ATTRIBUTE, "attribute"},
	{SC_PKCS15_ACCESS_RULE_MODE_PSO_CDS, "pso_cds"},
	{SC_PKCS15_ACCESS_RULE_MODE_PSO_VERIFY, "pso_verify"},
	{SC_PKCS15_ACCESS_RULE_MODE_PSO_DECRYPT, "pso_decrypt"},
	{SC_PKCS15_ACCESS_RULE_MODE_PSO_ENCRYPT, "pso_encrypt"},
	{SC_PKCS15_ACCESS_RULE_MODE_INT_AUTH, "int_auth"},
	{SC_PKCS15_ACCESS_RULE_MODE_EXT_AUTH, "ext_auth"},
	{0, NULL},
};

static const char *key_types[] = { "", "RSA", "DSA", "GOSTR3410", "EC", "EDDSA", "XEDDSA", "" };

static void
print_access_rules(const struct sc_pkcs15_accessrule *rules, int num)
{
	int i, j;

	if (!rules->access_mode)
		return;

	printf("\tAccess Rules   :");
	for (i = 0; i < num; i++)   {
		int next_coma = 0;

		if (!(rules + i)->access_mode)
			break;
		printf(" ");

		for (j = 0; _access_rules_text[j].label;j++)   {
			if ((rules + i)->access_mode & (_access_rules_text[j].flag))   {
				printf("%s%s", next_coma ? "," : "", _access_rules_text[j].label);
				next_coma = 1;
			}
		}

		printf(":%s;", (rules + i)->auth_id.len ? sc_pkcs15_print_id(&(rules + i)->auth_id) : "<always>");
	}
	printf("\n");
}

static void print_common_flags(const struct sc_pkcs15_object *obj)
{
	const char *common_flags[] = {"private", "modifiable"};
	unsigned int i;
	printf("\tObject Flags   : [0x%02X]", obj->flags);
	for (i = 0; i < NELEMENTS(common_flags); i++) {
		if (obj->flags & (1 << i)) {
			printf(", %s", common_flags[i]);
		}
	}
	printf("\n");
}

static void print_cert_info(const struct sc_pkcs15_object *obj)
{
	struct sc_pkcs15_cert_info *cert_info = (struct sc_pkcs15_cert_info *) obj->data;
	struct sc_pkcs15_cert *cert_parsed = NULL;
	int rv;

	if (compact) {
		printf("\tPath:%s  ID:%s", sc_print_path(&cert_info->path),
			sc_pkcs15_print_id(&cert_info->id));
		if (cert_info->authority)
			printf("  Authority");
		return;
	}

	printf("X.509 Certificate [%.*s]\n", (int) sizeof obj->label, obj->label);
	print_common_flags(obj);
	printf("\tAuthority      : %s\n", cert_info->authority ? "yes" : "no");
	printf("\tPath           : %s\n", sc_print_path(&cert_info->path));
	printf("\tID             : %s\n", sc_pkcs15_print_id(&cert_info->id));

	print_access_rules(obj->access_rules, SC_PKCS15_MAX_ACCESS_RULES);

	rv = sc_pkcs15_read_certificate(p15card, cert_info, &cert_parsed);
	if (rv >= 0 && cert_parsed)   {
		printf("\tEncoded serial : %02X %02X ", *(cert_parsed->serial), *(cert_parsed->serial + 1));
		util_hex_dump(stdout, cert_parsed->serial + 2, cert_parsed->serial_len - 2, "");
		printf("\n");
		sc_pkcs15_free_certificate(cert_parsed);
	}
}

static int list_certificates(void)
{
	int r, i;
	struct sc_pkcs15_object *objs[32];

	r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_CERT_X509, objs, 32);
	if (r < 0) {
		fprintf(stderr, "Certificate enumeration failed: %s\n", sc_strerror(r));
		return 1;
	}
	if (compact)
		printf("Card has %d Certificate(s).\n", r);
	else if (verbose)
		printf("Card has %d Certificate(s).\n\n", r);
	for (i = 0; i < r; i++) {
		print_cert_info(objs[i]);
		printf("\n");
	}

	return 0;
}

static int
print_pem_object(const char *kind, const u8*data, size_t data_len)
{
	FILE		*outf;
	unsigned char	*buf = NULL;
	size_t		buf_len = 1024;
	int		r;

	/* With base64, every 3 bytes yield 4 characters, and with
	 * 64 chars per line we know almost exactly how large a buffer we
	 * will need. */
	buf_len = (data_len + 2) / 3 * 4;
	buf_len += 2 * (buf_len / 64 + 2); /* certain platforms use CRLF */
	buf_len += 64;			   /* slack for checksum etc */

	if (!(buf = malloc(buf_len))) {
		perror("print_pem_object");
		return 1;
	}

	r = sc_base64_encode(data, data_len, buf, buf_len, 64);
	if (r < 0) {
		fprintf(stderr, "Base64 encoding failed: %s\n", sc_strerror(r));
		free(buf);
		return 1;
	}

	if (opt_outfile != NULL) {
		outf = fopen(opt_outfile, "w");
		if (outf == NULL) {
			fprintf(stderr, "Error opening file '%s': %s\n",
				opt_outfile, strerror(errno));
			free(buf);
			return 2;
		}
	} else
		outf = stdout;
	fprintf(outf,
		"-----BEGIN %s-----\n"
		"%s"
		"-----END %s-----\n",
		kind, buf, kind);
	if (outf != stdout)
		fclose(outf);
	free(buf);
	return 0;
}

static void
list_data_object(const char *kind, const unsigned char *data, size_t data_len)
{
	char title[0x100];
	size_t i;

	snprintf(title, sizeof(title), "%s (%lu bytes): ", kind, (unsigned long) data_len);
	printf("%s", title);
	memset(title, ' ', strlen(title));
	for (i = 0; i < data_len; i++)   {
		if (i && !(i%48))
			printf("\n%s", title);
		printf("%02X", data[i]);
	}
	printf("\n");
}

static int
print_data_object(const char *kind, const u8*data, size_t data_len)
{
	size_t i;

	if (opt_outfile != NULL) {
		FILE *outf;
		outf = fopen(opt_outfile, "wb");
		if (outf == NULL) {
			fprintf(stderr, "Error opening file '%s': %s\n",
				opt_outfile, strerror(errno));
			return 2;
			}
		for (i=0; i < data_len; i++)
			fprintf(outf, "%c", data[i]);
		fclose(outf);
	} else {
		if (opt_raw) {
			for (i=0; i < data_len; i++)
				printf("%c", data[i]);
		} else {
			printf("%s (%lu bytes): <",
				kind, (unsigned long) data_len);
			for (i=0; i < data_len; i++)
				printf(" %02X", data[i]);
			printf(" >\n");
		}
	}
	return 0;
}

static int read_certificate(void)
{
	int r, i, count;
	struct sc_pkcs15_id id;
	struct sc_pkcs15_object *objs[32];

	id.len = SC_PKCS15_MAX_ID_SIZE;
	sc_pkcs15_hex_string_to_id(opt_cert, &id);

	r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_CERT_X509, objs, 32);
	if (r < 0) {
		fprintf(stderr, "Certificate enumeration failed: %s\n", sc_strerror(r));
		return 1;
	}
	count = r;
	for (i = 0; i < count; i++) {
		struct sc_pkcs15_cert_info *cinfo = (struct sc_pkcs15_cert_info *) objs[i]->data;
		struct sc_pkcs15_cert *cert;

		if (sc_pkcs15_compare_id(&id, &cinfo->id) != 1)
			continue;

		if (verbose)
			printf("Reading certificate with ID '%s'\n", opt_cert);
		r = sc_pkcs15_read_certificate(p15card, cinfo, &cert);
		if (r) {
			fprintf(stderr, "Certificate read failed: %s\n", sc_strerror(r));
			return 1;
		}
		r = print_pem_object("CERTIFICATE", cert->data.value, cert->data.len);
		sc_pkcs15_free_certificate(cert);
		return r;
	}
	fprintf(stderr, "Certificate with ID '%s' not found.\n", opt_cert);
	return 2;
}

static int read_data_object(void)
{
	int r, i, count;
	struct sc_pkcs15_object *objs[32];
	struct sc_object_id oid;

	r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_DATA_OBJECT, objs, 32);
	if (r < 0) {
		fprintf(stderr, "Data object enumeration failed: %s\n", sc_strerror(r));
		return 1;
	}
	count = r;

	for (i = 0; i < count; i++) {
		struct sc_pkcs15_data_info *cinfo = (struct sc_pkcs15_data_info *) objs[i]->data;
		struct sc_pkcs15_data *data_object = NULL;

		if (!sc_format_oid(&oid, opt_data))   {
			if (!sc_compare_oid(&oid, &cinfo->app_oid))
				continue;
		}
		else   {
			if (strcmp(opt_data, cinfo->app_label) && strncmp(opt_data, objs[i]->label, sizeof objs[i]->label))
				continue;
		}

		if (verbose)
			printf("Reading data object with label '%s'\n", opt_data);
		r = authenticate(objs[i]);
		if (r >= 0) {
			r = sc_pkcs15_read_data_object(p15card, cinfo, &data_object);
			if (r) {
				fprintf(stderr, "Data object read failed: %s\n", sc_strerror(r));
				if (r == SC_ERROR_FILE_NOT_FOUND)
					continue; /* DEE emulation may say there is a file */
				return 1;
			}
			r = print_data_object("Data Object", data_object->data, data_object->data_len);
			sc_pkcs15_free_data_object(data_object);
			return r;
		} else {
			fprintf(stderr, "Authentication error: %s\n", sc_strerror(r));
			return 1;
		}
	}
	fprintf(stderr, "Data object with label '%s' not found.\n", opt_data);
	return 2;
}

static int list_data_objects(void)
{
	int r, i, count;
	struct sc_pkcs15_object *objs[32];

	r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_DATA_OBJECT, objs, 32);
	if (r < 0) {
		fprintf(stderr, "Data object enumeration failed: %s\n", sc_strerror(r));
		return 1;
	}
	count = r;
	if (compact)
		printf("Card has %d Data object(s).\n", count);
	else if (verbose)
		printf("Card has %d Data object(s).\n\n", count);
	for (i = 0; i < count; i++) {
		int idx;
		struct sc_pkcs15_data_info *cinfo = (struct sc_pkcs15_data_info *) objs[i]->data;

		if (compact) {
			printf("\tPath:%-12s", sc_print_path(&cinfo->path));
			if (sc_valid_oid(&cinfo->app_oid)) {
				printf("  %i", cinfo->app_oid.value[0]);
				for (idx = 1; idx < SC_MAX_OBJECT_ID_OCTETS && cinfo->app_oid.value[idx] != -1 ; idx++)
					printf(".%i", cinfo->app_oid.value[idx]);
			}
			if (objs[i]->auth_id.len == 0) {
				struct sc_pkcs15_data *data_object;
				r = sc_pkcs15_read_data_object(p15card, cinfo, &data_object);
				if (r) {
					fprintf(stderr, "Data object read failed: %s\n", sc_strerror(r));
					if (r == SC_ERROR_FILE_NOT_FOUND)
						continue; /* DEE emulation may say there is a file */
					return 1;
				}
				sc_pkcs15_free_data_object(data_object);
				printf("  Size:%5"SC_FORMAT_LEN_SIZE_T"u",
				       cinfo->data.len);
			} else {
				printf("  AuthID:%-3s", sc_pkcs15_print_id(&objs[i]->auth_id));
			}
			printf("  %-20s", cinfo->app_label);
			printf("\n");
			continue;
		}

		if (objs[i]->label[0] != '\0')
			printf("Data object '%.*s'\n",(int) sizeof objs[i]->label, objs[i]->label);
		else
			printf("Data object <%i>\n", i);
		printf("\tapplicationName: %s\n", cinfo->app_label);
		if (sc_valid_oid(&cinfo->app_oid)) {
			printf("\tapplicationOID:  %i", cinfo->app_oid.value[0]);
			for (idx = 1; idx < SC_MAX_OBJECT_ID_OCTETS && cinfo->app_oid.value[idx] != -1 ; idx++)
				printf(".%i", cinfo->app_oid.value[idx]);
			printf("\n");
		}
		printf("\tPath:            %s\n", sc_print_path(&cinfo->path));
		if (objs[i]->auth_id.len == 0) {
			struct sc_pkcs15_data *data_object;
			r = sc_pkcs15_read_data_object(p15card, cinfo, &data_object);
			if (r) {
				fprintf(stderr, "Data object read failed: %s\n", sc_strerror(r));
				if (r == SC_ERROR_FILE_NOT_FOUND)
					 continue; /* DEE emulation may say there is a file */
				return 1;
			}
			list_data_object("\tData", data_object->data, data_object->data_len);
			sc_pkcs15_free_data_object(data_object);
		}
		else {
			printf("\tAuth ID:         %s\n", sc_pkcs15_print_id(&objs[i]->auth_id));
		}

		printf("\n");
	}
	return 0;
}

static void print_key_usages(int usage)
{
	size_t i;
	const char *usages[] = {
		"encrypt", "decrypt", "sign", "signRecover", "wrap", "unwrap",
		"verify", "verifyRecover", "derive", "nonRepudiation"
	};
	const size_t usage_count = NELEMENTS(usages);
	for (i = 0; i < usage_count; i++)
		if (usage & (1 << i))
			printf(", %s", usages[i]);
}

static void print_key_access_flags(int flags)
{
	size_t i;
	const char *key_access_flags[] = {
		"sensitive", "extract", "alwaysSensitive","neverExtract", "local"
	};
	const size_t af_count = NELEMENTS(key_access_flags);
	for (i = 0; i < af_count; i++)
		if (flags & (1 << i))
			printf(", %s", key_access_flags[i]);
}

static void print_prkey_info(const struct sc_pkcs15_object *obj)
{
	struct sc_pkcs15_prkey_info *prkey = (struct sc_pkcs15_prkey_info *) obj->data;
	unsigned char guid[40];
	size_t guid_len;
	int i;
	int last_algo_refs = 0;

	if (compact) {
		printf("\t%-3s", key_types[7 & obj->type]);

		if (prkey->modulus_length)
			printf("[%lu]", (unsigned long)prkey->modulus_length);
		else {
			if (prkey->field_length)
				printf("[%lu]", (unsigned long)prkey->field_length);
		}
		printf("  ID:%s", sc_pkcs15_print_id(&prkey->id));
		printf("  Ref:0x%02X", prkey->key_reference);
		if (obj->auth_id.len != 0)
			printf("  AuthID:%s", sc_pkcs15_print_id(&obj->auth_id));
		printf("\n\t     %-18.*s [0x%02X", (int) sizeof obj->label, obj->label, prkey->usage);
		print_key_usages(prkey->usage);
		printf("]");
		return;
	}

	printf("Private %s Key [%.*s]\n", key_types[7 & obj->type], (int) sizeof obj->label, obj->label);
	print_common_flags(obj);
	printf("\tUsage          : [0x%02X]", prkey->usage);
	print_key_usages(prkey->usage);
	printf("\n");
	printf("\tAccess Flags   : [0x%02X]", prkey->access_flags);
	print_key_access_flags(prkey->access_flags);
	printf("\n");
	printf("\tAlgo_refs      : ");
	/* zero may be valid and don't know how many were read  print at least 1*/
	for (i = 0; i< SC_MAX_SUPPORTED_ALGORITHMS; i++) {
		if (prkey->algo_refs[i] != 0)
			last_algo_refs = i;
	}
	for (i = 0; i< last_algo_refs + 1; i++) {
		printf("%s%u", (i == 0) ? "" : ", ", prkey->algo_refs[i]);
	}
	printf("\n");

	print_access_rules(obj->access_rules, SC_PKCS15_MAX_ACCESS_RULES);

	if (prkey->modulus_length)
		printf("\tModLength      : %lu\n", (unsigned long)prkey->modulus_length);
	else
		printf("\tFieldLength    : %lu\n", (unsigned long)prkey->field_length);
	printf("\tKey ref        : %d (0x%02X)\n", prkey->key_reference, prkey->key_reference);
	printf("\tNative         : %s\n", prkey->native ? "yes" : "no");
	if (prkey->path.len || prkey->path.aid.len)
		printf("\tPath           : %s\n", sc_print_path(&prkey->path));
	if (obj->auth_id.len != 0)
		printf("\tAuth ID        : %s\n", sc_pkcs15_print_id(&obj->auth_id));
	printf("\tID             : %s\n", sc_pkcs15_print_id(&prkey->id));

	guid_len = sizeof(guid);
	if (!sc_pkcs15_get_object_guid(p15card, obj, 1, guid, &guid_len))   {
		printf("\tMD:guid        : ");
		if (strlen((char *)guid) == guid_len)   {
			printf("%s\n", (char *)guid);
		}
		else  {
			printf("0x'");
			util_hex_dump(stdout, guid, guid_len, "");
			printf("'\n");
		}
	}
}


static int list_private_keys(void)
{
	int r, i;
	struct sc_pkcs15_object *objs[32];

	r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_PRKEY, objs, 32);
	if (r < 0) {
		fprintf(stderr, "Private key enumeration failed: %s\n", sc_strerror(r));
		return 1;
	}
	if (compact)
		printf("Card has %d Private key(s).\n", r);
	else if (verbose)
		printf("Card has %d Private key(s).\n\n", r);
	for (i = 0; i < r; i++) {
		print_prkey_info(objs[i]);
		printf("\n");
	}
	return 0;
}

static void print_pubkey_info(const struct sc_pkcs15_object *obj)
{
	const struct sc_pkcs15_pubkey_info *pubkey = (const struct sc_pkcs15_pubkey_info *) obj->data;
	int have_path = (pubkey->path.len != 0) || (pubkey->path.aid.len != 0);

	if (compact) {
		printf("\t%-3s", key_types[7 & obj->type]);

		if (pubkey->modulus_length)
			printf("[%lu]", (unsigned long)pubkey->modulus_length);
		else
			printf("[FieldLength:%lu]", (unsigned long)pubkey->field_length);
		printf("  %s", sc_pkcs15_print_id(&pubkey->id));
		printf("  Ref:0x%02X", pubkey->key_reference);
		if (obj->auth_id.len != 0)
			printf("  AuthID:%s", sc_pkcs15_print_id(&obj->auth_id));
		printf("  %-18.*s [0x%02X", (int) sizeof obj->label, obj->label, pubkey->usage);
		print_key_usages(pubkey->usage);
		printf("]");
		return;
	}

	printf("Public %s Key [%.*s]\n", key_types[7 & obj->type], (int) sizeof obj->label, obj->label);
	print_common_flags(obj);
	printf("\tUsage          : [0x%02X]", pubkey->usage);
	print_key_usages(pubkey->usage);
	printf("\n");

	printf("\tAccess Flags   : [0x%02X]", pubkey->access_flags);
	print_key_access_flags(pubkey->access_flags);
	printf("\n");

	print_access_rules(obj->access_rules, SC_PKCS15_MAX_ACCESS_RULES);

	if (pubkey->modulus_length)   {
		printf("\tModLength      : %lu\n", (unsigned long)pubkey->modulus_length);
	}
	else if (pubkey->field_length)   {
		printf("\tFieldLength    : %lu\n", (unsigned long)pubkey->field_length);
	}
	else if ((obj->type == SC_PKCS15_TYPE_PUBKEY_EC || obj->type == SC_PKCS15_TYPE_PUBKEY_EDDSA)
			&& have_path)   {
		sc_pkcs15_pubkey_t *pkey = NULL;
		if (!sc_pkcs15_read_pubkey(p15card, obj, &pkey))   {
			printf("\tFieldLength    : %lu\n", (unsigned long)pkey->u.ec.params.field_length);
			sc_pkcs15_free_pubkey(pkey);
		}
	}

	printf("\tKey ref        : %d (0x%02X)\n", pubkey->key_reference,  pubkey->key_reference);
	printf("\tNative         : %s\n", pubkey->native ? "yes" : "no");
	if (have_path)
		printf("\tPath           : %s\n", sc_print_path(&pubkey->path));
	if (obj->auth_id.len != 0)
		printf("\tAuth ID        : %s\n", sc_pkcs15_print_id(&obj->auth_id));
	printf("\tID             : %s\n", sc_pkcs15_print_id(&pubkey->id));
	if (!have_path || obj->content.len)
		printf("\tDirectValue    : <%s>\n", obj->content.len ? "present" : "absent");
}

static int list_public_keys(void)
{
	int r, i;
	struct sc_pkcs15_object *objs[32];

	r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_PUBKEY, objs, 32);
	if (r < 0) {
		fprintf(stderr, "Public key enumeration failed: %s\n", sc_strerror(r));
		return 1;
	}
	if (compact)
		printf("Card has %d Public key(s).\n", r);
	else if (verbose)
		printf("Card has %d Public key(s).\n\n", r);
	for (i = 0; i < r; i++) {
		print_pubkey_info(objs[i]);
		printf("\n");
	}
	return 0;
}

static int read_public_key(void)
{
	int r;
	struct sc_pkcs15_id id;
	struct sc_pkcs15_object *obj;
	sc_pkcs15_pubkey_t *pubkey = NULL;
	sc_pkcs15_cert_t *cert = NULL;
	sc_pkcs15_der_t pem_key;

	pem_key.value = NULL;
	pem_key.len = 0;

	id.len = SC_PKCS15_MAX_ID_SIZE;
	sc_pkcs15_hex_string_to_id(opt_pubkey, &id);

	r = sc_pkcs15_find_pubkey_by_id(p15card, &id, &obj);
	if (r >= 0) {
		if (verbose)
			printf("Reading public key with ID '%s'\n", opt_pubkey);
		r = sc_pkcs15_read_pubkey(p15card, obj, &pubkey);
	} else if (r == SC_ERROR_OBJECT_NOT_FOUND) {
		/* No pubkey - try if there's a certificate */
		r = sc_pkcs15_find_cert_by_id(p15card, &id, &obj);
		if (r >= 0) {
			if (verbose)
				printf("Reading certificate with ID '%s'\n", opt_pubkey);
			r = sc_pkcs15_read_certificate(p15card, (sc_pkcs15_cert_info_t *) obj->data, &cert);
		}
		if (r >= 0)
			pubkey = cert->key;
	}

	if (r == SC_ERROR_OBJECT_NOT_FOUND) {
		fprintf(stderr, "Public key with ID '%s' not found.\n", opt_pubkey);
		r = 2;
		goto out;
	}
	if (r < 0) {
		fprintf(stderr, "Public key enumeration failed: %s\n", sc_strerror(r));
		r = 1;
		goto out;
	}
	if (!pubkey) {
		fprintf(stderr, "Public key not available\n");
		r = 1;
		goto out;
	}

	r = sc_pkcs15_encode_pubkey_as_spki(ctx, pubkey, &pem_key.value, &pem_key.len);
	if (r < 0) {
		fprintf(stderr, "Error encoding PEM key: %s\n", sc_strerror(r));
		r = 1;
	} else {
		r = print_pem_object("PUBLIC KEY", pem_key.value, pem_key.len);
		free(pem_key.value);
	}

out:
	if (cert)
		sc_pkcs15_free_certificate(cert);
	else
		sc_pkcs15_free_pubkey(pubkey);

	return r;
}

static void print_skey_info(const struct sc_pkcs15_object *obj)
{
	static const char *skey_types[] = { "", "Generic", "DES", "2DES", "3DES", "", "", "" };
	struct sc_pkcs15_skey_info *skey = (struct sc_pkcs15_skey_info *) obj->data;
	unsigned char guid[40];
	size_t guid_len;

	printf("Secret %s Key [%.*s]\n", skey_types[7 & obj->type], (int) sizeof obj->label, obj->label);
	print_common_flags(obj);
	printf("\tUsage          : [0x%02X]", skey->usage);
	print_key_usages(skey->usage);
	printf("\n");

	printf("\tAccess Flags   : [0x%02X]", skey->access_flags);
	print_key_access_flags(skey->access_flags);
	printf("\n");

	print_access_rules(obj->access_rules, SC_PKCS15_MAX_ACCESS_RULES);

	printf("\tSize           : %lu bits\n", (unsigned long)skey->value_len);
	printf("\tID             : %s\n", sc_pkcs15_print_id(&skey->id));
	printf("\tNative         : %s\n", skey->native ? "yes" : "no");
	printf("\tKey ref        : %d (0x%02X)\n", skey->key_reference, skey->key_reference);

	if (skey->path.len || skey->path.aid.len)
		printf("\tPath           : %s\n", sc_print_path(&skey->path));

	guid_len = sizeof(guid);
	if (!sc_pkcs15_get_object_guid(p15card, obj, 1, guid, &guid_len))   {
		printf("\tGUID           : %s\n", (char *)guid);
	}

}

static int list_skeys(void)
{
	int r, i;
	struct sc_pkcs15_object *objs[32];

	r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_SKEY, objs, 32);
	if (r < 0) {
		fprintf(stderr, "Secret key enumeration failed: %s\n", sc_strerror(r));
		return 1;
	}
	if (verbose)
		printf("Card has %d Secret key(s).\n\n", r);
	for (i = 0; i < r; i++) {
		print_skey_info(objs[i]);
		printf("\n");
	}

	return 0;
}


#if defined(ENABLE_OPENSSL) && (defined(_WIN32) || defined(HAVE_INTTYPES_H))

static void print_ssh_key(FILE *outf, const char * alg, struct sc_pkcs15_object *obj, unsigned char * buf, uint32_t len) {
	unsigned char *uu;
	int r;

	uu = malloc(len*2); // Way over - even if we have extra LFs; as each 6 bits take one byte.
	if (!uu)
		return;

	if (opt_rfc4716) {
		r = sc_base64_encode(buf, len, uu, 2*len, 64);
		if (r < 0) {
			free(uu);
			return;
		}

		fprintf(outf,"---- BEGIN SSH2 PUBLIC KEY ----\n");

		if (obj->label[0] != '\0')
			fprintf(outf,"Comment: \"%.*s\"\n", (int) sizeof obj->label, obj->label);

		fprintf(outf,"%s", uu);
		fprintf(outf,"---- END SSH2 PUBLIC KEY ----\n");
	} else {
		// Old style openssh - [<quote protected options> <whitespace> <keytype> <whitespace> <key> [<whitespace> anything else]
		//
		r = sc_base64_encode(buf, len, uu, 2*len, 0);
		if (r < 0) {
			free(uu);
			return;
		}

		if (obj->label[0] != '\0')
			fprintf(outf,"%s %s %.*s\n", alg, uu, (int) sizeof obj->label, obj->label);
		else
			fprintf(outf,"%s %s\n", alg, uu);
	}
	free(uu);
	return;
}

static int read_ssh_key(void)
{
	int r;
	struct sc_pkcs15_id id;
	struct sc_pkcs15_object *obj = NULL;
	sc_pkcs15_pubkey_t *pubkey = NULL;
	sc_pkcs15_cert_t *cert = NULL;
	FILE *outf = NULL;

	if (opt_outfile != NULL) {
		outf = fopen(opt_outfile, "w");
		if (outf == NULL) {
			fprintf(stderr, "Error opening file '%s': %s\n", opt_outfile, strerror(errno));
			goto fail2;
		}
	}
	else   {
		outf = stdout;
	}

	id.len = SC_PKCS15_MAX_ID_SIZE;
	sc_pkcs15_hex_string_to_id(opt_pubkey, &id);

	r = sc_pkcs15_find_pubkey_by_id(p15card, &id, &obj);
	if (r >= 0) {
		if (verbose)
			fprintf(stderr,"Reading ssh key with ID '%s'\n", opt_pubkey);
		r = authenticate(obj);
		if (r >= 0)
			r = sc_pkcs15_read_pubkey(p15card, obj, &pubkey);
	}
	else if (r == SC_ERROR_OBJECT_NOT_FOUND) {
		/* No pubkey - try if there's a certificate */
		r = sc_pkcs15_find_cert_by_id(p15card, &id, &obj);
		if (r >= 0) {
			if (verbose)
				fprintf(stderr,"Reading certificate with ID '%s'\n", opt_pubkey);
			r = sc_pkcs15_read_certificate(p15card, (sc_pkcs15_cert_info_t *) obj->data, &cert);
		}
		if (r >= 0)
			pubkey = cert->key;
	}

	if (r == SC_ERROR_OBJECT_NOT_FOUND) {
		if (outf != stdout)
			fclose(outf);
		fprintf(stderr, "Public key with ID '%s' not found.\n", opt_pubkey);
		return 2;
	}
	if (r < 0) {
		if (outf != stdout)
			fclose(outf);
		fprintf(stderr, "Public key enumeration failed: %s\n", sc_strerror(r));
		return 1;
	}

	if (pubkey->algorithm == SC_ALGORITHM_EDDSA) {
		// SSH supports only ed25519 key now

		char alg[20];
		/* Large enough to fit the following:
		 * 2 x 4B item length headers
		 * max 11B algorithm name, 32B key data */
		unsigned char buf[64];
		unsigned int len, n;

		n = pubkey->u.eddsa.pubkey.len;
		if (n != 32) {
			fprintf(stderr, "Wrong public key length\n");
			goto fail2;
		}

		buf[0] = 0;
		buf[1] = 0;
		buf[2] = 0;
		len = snprintf((char *) buf+4, 20, "ssh-ed25519");
		memcpy(alg, buf+4, 20);
		buf[3] = len;
		len += 4;

		buf[len++] = 0;
		buf[len++] = 0;
		buf[len++] = 0;
		buf[len++] = n & 0xff;
		memcpy(buf + len, pubkey->u.eddsa.pubkey.value, n);
		len += n;

		print_ssh_key(outf, alg, obj, buf, len);
	}
	if (pubkey->algorithm == SC_ALGORITHM_EC) {
		// support only for NIST
		// 'ssh-keygen -t ecdsa' allow only field lengths 256/384/521

		static struct supported_ec_curves {
			char *curve_name;
			struct sc_object_id curve_oid;
			size_t size;
		} ec_curves[] = {
			{"secp256r1", {{1, 2, 840, 10045, 3, 1, 7, -1}},256},
			{"secp384r1", {{1, 3, 132, 0, 34, -1}},         384},
			{"secp521r1", {{1, 3, 132, 0, 35, -1}},         521},
		        {NULL, {{-1}}, 0},
		};
		char alg[20];
		/* Large enough to fit the following:
		 * 3 x 4B item length headers
		 * max 20B algorithm name, 9B curve name, max 256B key data */
		unsigned char buf[300];
		unsigned int i, len, tmp, n;

		for (n = 0,i = 0; ec_curves[i].curve_name != NULL; i++) {
			if(sc_compare_oid (&ec_curves[i].curve_oid,&pubkey->u.ec.params.id))
				n = ec_curves[i].size;
		}
		if (!n) {
			fprintf(stderr, "Unsupported curve\n");
			goto fail2;
		}
		if (n != pubkey->u.ec.params.field_length) {
			fprintf(stderr, "Wrong field length\n");
			goto fail2;
		}

		buf[0] = 0;
		buf[1] = 0;
		buf[2] = 0;
		len = snprintf((char *) buf+4, 20, "ecdsa-sha2-nistp%d", n);
		memcpy(alg, buf+4, 20);
		buf[3] = len;

		len += 4;
		buf[len++] = 0;
		buf[len++] = 0;
		buf[len++] = 0;
		tmp = snprintf((char *) buf+len+1, 9, "nistp%d", n);
		buf[len++] = tmp;
		len += tmp;

		n = pubkey->u.ec.ecpointQ.len;
		if(n > 255) {
			fprintf(stderr, "Wrong public key length\n");
			goto fail2;
		}
		buf[len++] = 0;
		buf[len++] = 0;
		buf[len++] = 0;
		buf[len++] = n & 0xff;
		memcpy(buf+len,pubkey->u.ec.ecpointQ.value,n);
		len += n;

		print_ssh_key(outf, alg, obj, buf, len);
	}

	if (pubkey->algorithm == SC_ALGORITHM_RSA) {
		unsigned char buf[2048];
		uint32_t len, n;

		if (!pubkey->u.rsa.modulus.data || !pubkey->u.rsa.modulus.len ||
				!pubkey->u.rsa.exponent.data || !pubkey->u.rsa.exponent.len)  {
			fprintf(stderr, "Failed to decode public RSA key.\n");
			goto fail2;
		}

		buf[0]=0;
		buf[1]=0;
		buf[2]=0;
		buf[3]=7;

		len = sprintf((char *) buf+4,"ssh-rsa");
		len+=4;

		if (sizeof(buf)-len < 4+pubkey->u.rsa.exponent.len)
			goto fail;

		n = pubkey->u.rsa.exponent.len;
		if (pubkey->u.rsa.exponent.data[0] & 0x80)
			n++;

		buf[len++]=(n >>24) & 0xff;
		buf[len++]=(n >>16) & 0xff;
		buf[len++]=(n >>8) & 0xff;
		buf[len++]=(n) & 0xff;
		if (pubkey->u.rsa.exponent.data[0] & 0x80)
			buf[len++]= 0;

		memcpy(buf+len,pubkey->u.rsa.exponent.data, pubkey->u.rsa.exponent.len);
		len += pubkey->u.rsa.exponent.len;

		if (sizeof(buf)-len < 5+pubkey->u.rsa.modulus.len)
			goto fail;

		n = pubkey->u.rsa.modulus.len;
		if (pubkey->u.rsa.modulus.data[0] & 0x80)
			n++;
		buf[len++]=(n >>24) & 0xff;
		buf[len++]=(n >>16) & 0xff;
		buf[len++]=(n >>8) & 0xff;
		buf[len++]=(n) & 0xff;

		if (pubkey->u.rsa.modulus.data[0] & 0x80)
			buf[len++]= 0;

		memcpy(buf+len,pubkey->u.rsa.modulus.data, pubkey->u.rsa.modulus.len);
		len += pubkey->u.rsa.modulus.len;

		print_ssh_key(outf, "ssh-rsa", obj, buf, len);
	}

	if (pubkey->algorithm == SC_ALGORITHM_DSA) {
		unsigned char buf[2048];
		uint32_t len;
		uint32_t n;

		if (!pubkey->u.dsa.p.data || !pubkey->u.dsa.p.len ||
				!pubkey->u.dsa.q.data || !pubkey->u.dsa.q.len ||
				!pubkey->u.dsa.g.data || !pubkey->u.dsa.g.len ||
				!pubkey->u.dsa.pub.data || !pubkey->u.dsa.pub.len)   {
			fprintf(stderr, "Failed to decode DSA key.\n");
			goto fail2;
		}

		buf[0]=0;
		buf[1]=0;
		buf[2]=0;
		buf[3]=7;

		len = sprintf((char *) buf+4,"ssh-dss");
		len+=4;

		if (sizeof(buf)-len < 5+pubkey->u.dsa.p.len)
			goto fail;

		n = pubkey->u.dsa.p.len;
		if (pubkey->u.dsa.p.data[0] & 0x80)
			n++;

		buf[len++]=(n >>24) & 0xff;
		buf[len++]=(n >>16) & 0xff;
		buf[len++]=(n >>8) & 0xff;
		buf[len++]=(n) & 0xff;
		if (pubkey->u.dsa.p.data[0] & 0x80)
			buf[len++]= 0;

		memcpy(buf+len,pubkey->u.dsa.p.data, pubkey->u.dsa.p.len);
		len += pubkey->u.dsa.p.len;

		if (sizeof(buf)-len < 5+pubkey->u.dsa.q.len)
			goto fail;

		n = pubkey->u.dsa.q.len;
		if (pubkey->u.dsa.q.data[0] & 0x80)
			n++;

		buf[len++]=(n >>24) & 0xff;
		buf[len++]=(n >>16) & 0xff;
		buf[len++]=(n >>8) & 0xff;
		buf[len++]=(n) & 0xff;
		if (pubkey->u.dsa.q.data[0] & 0x80)
			buf[len++]= 0;

		memcpy(buf+len,pubkey->u.dsa.q.data, pubkey->u.dsa.q.len);
		len += pubkey->u.dsa.q.len;

		if (sizeof(buf)-len < 5+pubkey->u.dsa.g.len)
			goto fail;
		n = pubkey->u.dsa.g.len;
		if (pubkey->u.dsa.g.data[0] & 0x80)
			n++;

		buf[len++]=(n >>24) & 0xff;
		buf[len++]=(n >>16) & 0xff;
		buf[len++]=(n >>8) & 0xff;
		buf[len++]=(n) & 0xff;
		if (pubkey->u.dsa.g.data[0] & 0x80)
			buf[len++]= 0;

		memcpy(buf+len,pubkey->u.dsa.g.data, pubkey->u.dsa.g.len);
		len += pubkey->u.dsa.g.len;

		if (sizeof(buf)-len < 5+pubkey->u.dsa.pub.len)
			goto fail;

		n = pubkey->u.dsa.pub.len;
		if (pubkey->u.dsa.pub.data[0] & 0x80)
			n++;

		buf[len++]=(n >>24) & 0xff;
		buf[len++]=(n >>16) & 0xff;
		buf[len++]=(n >>8) & 0xff;
		buf[len++]=(n) & 0xff;
		if (pubkey->u.dsa.pub.data[0] & 0x80)
			buf[len++]= 0;

		memcpy(buf+len,pubkey->u.dsa.pub.data, pubkey->u.dsa.pub.len);
		len += pubkey->u.dsa.pub.len;

		print_ssh_key(outf, "ssh-dss", obj, buf, len);
	}

	if (outf != stdout)
		fclose(outf);
	if (cert)
		sc_pkcs15_free_certificate(cert);
	else
		sc_pkcs15_free_pubkey(pubkey);
	return 0;
fail:
	printf("can't convert key: buffer too small\n");
fail2:
	if (outf && outf != stdout)
		fclose(outf);
	if (cert)
		sc_pkcs15_free_certificate(cert);
	else
		sc_pkcs15_free_pubkey(pubkey);
	return SC_ERROR_OUT_OF_MEMORY;
}

#endif

static sc_pkcs15_object_t *
get_pin_info(void)
{<commit_msg>pkcs15-tool: Update the logic to make it more clear for some dumb static analyzers<commit_after>/*
 * pkcs15-tool.c: Tool for poking with PKCS #15 smart cards
 *
 * Copyright (C) 2001  <NAME> <<EMAIL>>
 * Copyright (C) 2008  <NAME> <<EMAIL>>
 *
 * 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"

#ifdef __APPLE__
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 500
#endif
#include <assert.h>
#include <ctype.h>
#ifdef _WIN32
#ifdef __MINGW32__
// work around for https://sourceforge.net/p/mingw-w64/bugs/476/
#include <windows.h>
#endif
#include <shellapi.h>
#include <tchar.h>
#else
#include <ftw.h>
#endif
#include <stdio.h>

#ifdef ENABLE_OPENSSL
#if defined(HAVE_INTTYPES_H)
#include <inttypes.h>
#elif defined(HAVE_STDINT_H)
#include <stdint.h>
#elif defined(_MSC_VER)
typedef unsigned __int32 uint32_t;
#else
#warning no uint32_t type available, please contact <EMAIL>
#endif
#include <openssl/bn.h>
#include <openssl/crypto.h>
#endif
#include <limits.h>

#include "libopensc/pkcs15.h"
#include "libopensc/asn1.h"
#include "util.h"
#include "pkcs11/pkcs11-display.h"

static const char *app_name = "pkcs15-tool";

static int opt_wait = 0;
static int opt_no_cache = 0;
static int opt_clear_cache = 0;
static char * opt_auth_id = NULL;
static char * opt_reader = NULL;
static char * opt_cert = NULL;
static char * opt_data = NULL;
static int opt_raw = 0;
static char * opt_pubkey = NULL;
static char * opt_outfile = NULL;
static char * opt_bind_to_aid = NULL;
static const char * opt_newpin = NULL;
static const char * opt_pin = NULL;
static const char * opt_puk = NULL;
static int	compact = 0;
static int	verbose = 0;
static int opt_use_pinpad = 0;
#if defined(ENABLE_OPENSSL) && (defined(_WIN32) || defined(HAVE_INTTYPES_H))
static int opt_rfc4716 = 0;
#endif

enum {
	OPT_CHANGE_PIN = 0x100,
	OPT_LIST_PINS,
	OPT_READER,
	OPT_TEST_SESSION_PIN,
	OPT_PIN_ID,
	OPT_NO_CACHE,
	OPT_CLEAR_CACHE,
	OPT_LIST_PUB,
	OPT_READ_PUB,
#if defined(ENABLE_OPENSSL) && (defined(_WIN32) || defined(HAVE_INTTYPES_H))
	OPT_READ_SSH,
	OPT_RFC4716,
#endif
	OPT_PIN,
	OPT_NEWPIN,
	OPT_PUK,
	OPT_VERIFY_PIN,
	OPT_BIND_TO_AID,
	OPT_LIST_APPLICATIONS,
	OPT_LIST_SKEYS,
	OPT_USE_PINPAD,
	OPT_USE_PINPAD_DEPRECATED,
	OPT_RAW,
	OPT_PRINT_VERSION,
	OPT_LIST_INFO,
	OPT_READ_CERT,
};

#define NELEMENTS(x)	(sizeof(x)/sizeof((x)[0]))

static int	authenticate(sc_pkcs15_object_t *obj);

static const struct option options[] = {
	{ "version",		0, NULL,			OPT_PRINT_VERSION },
	{ "list-info",	no_argument, NULL,		OPT_LIST_INFO },
	{ "list-applications",	no_argument, NULL,		OPT_LIST_APPLICATIONS },
	{ "read-certificate",	required_argument, NULL,	OPT_READ_CERT },
	{ "list-certificates",	no_argument, NULL,		'c' },
	{ "read-data-object",	required_argument, NULL,	'R' },
	{ "raw",		no_argument, NULL,		OPT_RAW },
	{ "list-data-objects",	no_argument, NULL,		'C' },
	{ "list-pins",		no_argument, NULL,		OPT_LIST_PINS },
	{ "list-secret-keys",	no_argument, NULL,		OPT_LIST_SKEYS },
	{ "short",			no_argument, NULL,		's' },
	{ "dump",		no_argument, NULL,		'D' },
	{ "unblock-pin",	no_argument, NULL,		'u' },
	{ "change-pin",		no_argument, NULL,		OPT_CHANGE_PIN },
	{ "list-keys",		no_argument, NULL,		'k' },
	{ "list-public-keys",	no_argument, NULL,		OPT_LIST_PUB },
	{ "read-public-key",	required_argument, NULL,	OPT_READ_PUB },
#if defined(ENABLE_OPENSSL) && (defined(_WIN32) || defined(HAVE_INTTYPES_H))
	{ "read-ssh-key",	required_argument, NULL,	OPT_READ_SSH },
	{ "rfc4716",		no_argument, NULL,		OPT_RFC4716 },
#endif
	{ "test-update",	no_argument, NULL,		'T' },
	{ "update",		no_argument, NULL,		'U' },
	{ "reader",		required_argument, NULL,	OPT_READER },
	{ "pin",		required_argument, NULL,	OPT_PIN },
	{ "new-pin",		required_argument, NULL,	OPT_NEWPIN },
	{ "puk",		required_argument, NULL,	OPT_PUK },
	{ "verify-pin",		no_argument, NULL,		OPT_VERIFY_PIN },
	{ "test-session-pin",	no_argument, NULL,		OPT_TEST_SESSION_PIN },
	{ "output",		required_argument, NULL,	'o' },
	{ "no-cache",		no_argument, NULL,		OPT_NO_CACHE },
	{ "clear-cache",	no_argument, NULL,		OPT_CLEAR_CACHE },
	{ "auth-id",		required_argument, NULL,	'a' },
	{ "aid",		required_argument, NULL,	OPT_BIND_TO_AID },
	{ "wait",		no_argument, NULL,		'w' },
	{ "verbose",		no_argument, NULL,		'v' },
	{ "use-pinpad",		no_argument, NULL,		OPT_USE_PINPAD },
	{ "no-prompt",		no_argument, NULL,		OPT_USE_PINPAD_DEPRECATED },
	{ NULL, 0, NULL, 0 }
};

static const char *option_help[] = {
	"Print OpenSC package version",
	"List card information",
	"List the on-card PKCS#15 applications",
	"Read certificate with ID <arg>",
	"List certificates",
	"Reads data object with OID, applicationName or label <arg>",
	"Outputs raw 8 bit data to stdout. File output will not be affected by this, it always uses raw mode.",
	"List data objects",
	"List PIN codes",
	"List secret keys",
	"Output lists in compact format",
	"List all card objects",
	"Unblock PIN code",
	"Change PIN or PUK code",
	"List private keys",
	"List public keys",
	"Reads public key with ID <arg>",
#if defined(ENABLE_OPENSSL) && (defined(_WIN32) || defined(HAVE_INTTYPES_H))
	"Reads public key with ID <arg>, outputs ssh format",
	"Outputs the public key in RFC 4716 format (requires --read-ssh-key)",
#endif
	"Test if the card needs a security update",
	"Update the card with a security update",
	"Uses reader number <arg>",
	"Specify PIN",
	"Specify New PIN (when changing or unblocking)",
	"Specify Unblock PIN",
	"Verify PIN after card binding (without 'auth-id' the first non-SO, non-Unblock PIN will be verified)",
	"Equivalent to --verify-pin with additional session PIN generation",
	"Outputs to file <arg>",
	"Disable card caching",
	"Clear card caching",
	"The auth ID of the PIN to use",
	"Specify AID of the on-card PKCS#15 application to bind to (in hexadecimal form)",
	"Wait for card insertion",
	"Verbose operation. Use several times to enable debug output.",
	"Do not prompt the user; if no PINs supplied, pinpad will be used.",
	NULL,
	NULL
};

static sc_context_t *ctx = NULL;
static sc_card_t *card = NULL;
static struct sc_pkcs15_card *p15card = NULL;

struct _access_rule_text {
	unsigned flag;
	const char *label;
} _access_rules_text[] = {
	{SC_PKCS15_ACCESS_RULE_MODE_READ, "read"},
	{SC_PKCS15_ACCESS_RULE_MODE_UPDATE, "update"},
	{SC_PKCS15_ACCESS_RULE_MODE_EXECUTE, "execute"},
	{SC_PKCS15_ACCESS_RULE_MODE_DELETE, "delete"},
	{SC_PKCS15_ACCESS_RULE_MODE_ATTRIBUTE, "attribute"},
	{SC_PKCS15_ACCESS_RULE_MODE_PSO_CDS, "pso_cds"},
	{SC_PKCS15_ACCESS_RULE_MODE_PSO_VERIFY, "pso_verify"},
	{SC_PKCS15_ACCESS_RULE_MODE_PSO_DECRYPT, "pso_decrypt"},
	{SC_PKCS15_ACCESS_RULE_MODE_PSO_ENCRYPT, "pso_encrypt"},
	{SC_PKCS15_ACCESS_RULE_MODE_INT_AUTH, "int_auth"},
	{SC_PKCS15_ACCESS_RULE_MODE_EXT_AUTH, "ext_auth"},
	{0, NULL},
};

static const char *key_types[] = { "", "RSA", "DSA", "GOSTR3410", "EC", "EDDSA", "XEDDSA", "" };

static void
print_access_rules(const struct sc_pkcs15_accessrule *rules, int num)
{
	int i, j;

	if (!rules->access_mode)
		return;

	printf("\tAccess Rules   :");
	for (i = 0; i < num; i++)   {
		int next_coma = 0;

		if (!(rules + i)->access_mode)
			break;
		printf(" ");

		for (j = 0; _access_rules_text[j].label;j++)   {
			if ((rules + i)->access_mode & (_access_rules_text[j].flag))   {
				printf("%s%s", next_coma ? "," : "", _access_rules_text[j].label);
				next_coma = 1;
			}
		}

		printf(":%s;", (rules + i)->auth_id.len ? sc_pkcs15_print_id(&(rules + i)->auth_id) : "<always>");
	}
	printf("\n");
}

static void print_common_flags(const struct sc_pkcs15_object *obj)
{
	const char *common_flags[] = {"private", "modifiable"};
	unsigned int i;
	printf("\tObject Flags   : [0x%02X]", obj->flags);
	for (i = 0; i < NELEMENTS(common_flags); i++) {
		if (obj->flags & (1 << i)) {
			printf(", %s", common_flags[i]);
		}
	}
	printf("\n");
}

static void print_cert_info(const struct sc_pkcs15_object *obj)
{
	struct sc_pkcs15_cert_info *cert_info = (struct sc_pkcs15_cert_info *) obj->data;
	struct sc_pkcs15_cert *cert_parsed = NULL;
	int rv;

	if (compact) {
		printf("\tPath:%s  ID:%s", sc_print_path(&cert_info->path),
			sc_pkcs15_print_id(&cert_info->id));
		if (cert_info->authority)
			printf("  Authority");
		return;
	}

	printf("X.509 Certificate [%.*s]\n", (int) sizeof obj->label, obj->label);
	print_common_flags(obj);
	printf("\tAuthority      : %s\n", cert_info->authority ? "yes" : "no");
	printf("\tPath           : %s\n", sc_print_path(&cert_info->path));
	printf("\tID             : %s\n", sc_pkcs15_print_id(&cert_info->id));

	print_access_rules(obj->access_rules, SC_PKCS15_MAX_ACCESS_RULES);

	rv = sc_pkcs15_read_certificate(p15card, cert_info, &cert_parsed);
	if (rv >= 0 && cert_parsed)   {
		printf("\tEncoded serial : %02X %02X ", *(cert_parsed->serial), *(cert_parsed->serial + 1));
		util_hex_dump(stdout, cert_parsed->serial + 2, cert_parsed->serial_len - 2, "");
		printf("\n");
		sc_pkcs15_free_certificate(cert_parsed);
	}
}

static int list_certificates(void)
{
	int r, i;
	struct sc_pkcs15_object *objs[32];

	r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_CERT_X509, objs, 32);
	if (r < 0) {
		fprintf(stderr, "Certificate enumeration failed: %s\n", sc_strerror(r));
		return 1;
	}
	if (compact)
		printf("Card has %d Certificate(s).\n", r);
	else if (verbose)
		printf("Card has %d Certificate(s).\n\n", r);
	for (i = 0; i < r; i++) {
		print_cert_info(objs[i]);
		printf("\n");
	}

	return 0;
}

static int
print_pem_object(const char *kind, const u8*data, size_t data_len)
{
	FILE		*outf;
	unsigned char	*buf = NULL;
	size_t		buf_len = 1024;
	int		r;

	/* With base64, every 3 bytes yield 4 characters, and with
	 * 64 chars per line we know almost exactly how large a buffer we
	 * will need. */
	buf_len = (data_len + 2) / 3 * 4;
	buf_len += 2 * (buf_len / 64 + 2); /* certain platforms use CRLF */
	buf_len += 64;			   /* slack for checksum etc */

	if (!(buf = malloc(buf_len))) {
		perror("print_pem_object");
		return 1;
	}

	r = sc_base64_encode(data, data_len, buf, buf_len, 64);
	if (r < 0) {
		fprintf(stderr, "Base64 encoding failed: %s\n", sc_strerror(r));
		free(buf);
		return 1;
	}

	if (opt_outfile != NULL) {
		outf = fopen(opt_outfile, "w");
		if (outf == NULL) {
			fprintf(stderr, "Error opening file '%s': %s\n",
				opt_outfile, strerror(errno));
			free(buf);
			return 2;
		}
	} else
		outf = stdout;
	fprintf(outf,
		"-----BEGIN %s-----\n"
		"%s"
		"-----END %s-----\n",
		kind, buf, kind);
	if (outf != stdout)
		fclose(outf);
	free(buf);
	return 0;
}

static void
list_data_object(const char *kind, const unsigned char *data, size_t data_len)
{
	char title[0x100];
	size_t i;

	snprintf(title, sizeof(title), "%s (%lu bytes): ", kind, (unsigned long) data_len);
	printf("%s", title);
	memset(title, ' ', strlen(title));
	for (i = 0; i < data_len; i++)   {
		if (i && !(i%48))
			printf("\n%s", title);
		printf("%02X", data[i]);
	}
	printf("\n");
}

static int
print_data_object(const char *kind, const u8*data, size_t data_len)
{
	size_t i;

	if (opt_outfile != NULL) {
		FILE *outf;
		outf = fopen(opt_outfile, "wb");
		if (outf == NULL) {
			fprintf(stderr, "Error opening file '%s': %s\n",
				opt_outfile, strerror(errno));
			return 2;
			}
		for (i=0; i < data_len; i++)
			fprintf(outf, "%c", data[i]);
		fclose(outf);
	} else {
		if (opt_raw) {
			for (i=0; i < data_len; i++)
				printf("%c", data[i]);
		} else {
			printf("%s (%lu bytes): <",
				kind, (unsigned long) data_len);
			for (i=0; i < data_len; i++)
				printf(" %02X", data[i]);
			printf(" >\n");
		}
	}
	return 0;
}

static int read_certificate(void)
{
	int r, i, count;
	struct sc_pkcs15_id id;
	struct sc_pkcs15_object *objs[32];

	id.len = SC_PKCS15_MAX_ID_SIZE;
	sc_pkcs15_hex_string_to_id(opt_cert, &id);

	r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_CERT_X509, objs, 32);
	if (r < 0) {
		fprintf(stderr, "Certificate enumeration failed: %s\n", sc_strerror(r));
		return 1;
	}
	count = r;
	for (i = 0; i < count; i++) {
		struct sc_pkcs15_cert_info *cinfo = (struct sc_pkcs15_cert_info *) objs[i]->data;
		struct sc_pkcs15_cert *cert;

		if (sc_pkcs15_compare_id(&id, &cinfo->id) != 1)
			continue;

		if (verbose)
			printf("Reading certificate with ID '%s'\n", opt_cert);
		r = sc_pkcs15_read_certificate(p15card, cinfo, &cert);
		if (r) {
			fprintf(stderr, "Certificate read failed: %s\n", sc_strerror(r));
			return 1;
		}
		r = print_pem_object("CERTIFICATE", cert->data.value, cert->data.len);
		sc_pkcs15_free_certificate(cert);
		return r;
	}
	fprintf(stderr, "Certificate with ID '%s' not found.\n", opt_cert);
	return 2;
}

static int read_data_object(void)
{
	int r, i, count;
	struct sc_pkcs15_object *objs[32];
	struct sc_object_id oid;

	r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_DATA_OBJECT, objs, 32);
	if (r < 0) {
		fprintf(stderr, "Data object enumeration failed: %s\n", sc_strerror(r));
		return 1;
	}
	count = r;

	for (i = 0; i < count; i++) {
		struct sc_pkcs15_data_info *cinfo = (struct sc_pkcs15_data_info *) objs[i]->data;
		struct sc_pkcs15_data *data_object = NULL;

		if (!sc_format_oid(&oid, opt_data))   {
			if (!sc_compare_oid(&oid, &cinfo->app_oid))
				continue;
		}
		else   {
			if (strcmp(opt_data, cinfo->app_label) && strncmp(opt_data, objs[i]->label, sizeof objs[i]->label))
				continue;
		}

		if (verbose)
			printf("Reading data object with label '%s'\n", opt_data);
		r = authenticate(objs[i]);
		if (r >= 0) {
			r = sc_pkcs15_read_data_object(p15card, cinfo, &data_object);
			if (r) {
				fprintf(stderr, "Data object read failed: %s\n", sc_strerror(r));
				if (r == SC_ERROR_FILE_NOT_FOUND)
					continue; /* DEE emulation may say there is a file */
				return 1;
			}
			r = print_data_object("Data Object", data_object->data, data_object->data_len);
			sc_pkcs15_free_data_object(data_object);
			return r;
		} else {
			fprintf(stderr, "Authentication error: %s\n", sc_strerror(r));
			return 1;
		}
	}
	fprintf(stderr, "Data object with label '%s' not found.\n", opt_data);
	return 2;
}

static int list_data_objects(void)
{
	int r, i, count;
	struct sc_pkcs15_object *objs[32];

	r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_DATA_OBJECT, objs, 32);
	if (r < 0) {
		fprintf(stderr, "Data object enumeration failed: %s\n", sc_strerror(r));
		return 1;
	}
	count = r;
	if (compact)
		printf("Card has %d Data object(s).\n", count);
	else if (verbose)
		printf("Card has %d Data object(s).\n\n", count);
	for (i = 0; i < count; i++) {
		int idx;
		struct sc_pkcs15_data_info *cinfo = (struct sc_pkcs15_data_info *) objs[i]->data;

		if (compact) {
			printf("\tPath:%-12s", sc_print_path(&cinfo->path));
			if (sc_valid_oid(&cinfo->app_oid)) {
				printf("  %i", cinfo->app_oid.value[0]);
				for (idx = 1; idx < SC_MAX_OBJECT_ID_OCTETS && cinfo->app_oid.value[idx] != -1 ; idx++)
					printf(".%i", cinfo->app_oid.value[idx]);
			}
			if (objs[i]->auth_id.len == 0) {
				struct sc_pkcs15_data *data_object;
				r = sc_pkcs15_read_data_object(p15card, cinfo, &data_object);
				if (r) {
					fprintf(stderr, "Data object read failed: %s\n", sc_strerror(r));
					if (r == SC_ERROR_FILE_NOT_FOUND)
						continue; /* DEE emulation may say there is a file */
					return 1;
				}
				sc_pkcs15_free_data_object(data_object);
				printf("  Size:%5"SC_FORMAT_LEN_SIZE_T"u",
				       cinfo->data.len);
			} else {
				printf("  AuthID:%-3s", sc_pkcs15_print_id(&objs[i]->auth_id));
			}
			printf("  %-20s", cinfo->app_label);
			printf("\n");
			continue;
		}

		if (objs[i]->label[0] != '\0')
			printf("Data object '%.*s'\n",(int) sizeof objs[i]->label, objs[i]->label);
		else
			printf("Data object <%i>\n", i);
		printf("\tapplicationName: %s\n", cinfo->app_label);
		if (sc_valid_oid(&cinfo->app_oid)) {
			printf("\tapplicationOID:  %i", cinfo->app_oid.value[0]);
			for (idx = 1; idx < SC_MAX_OBJECT_ID_OCTETS && cinfo->app_oid.value[idx] != -1 ; idx++)
				printf(".%i", cinfo->app_oid.value[idx]);
			printf("\n");
		}
		printf("\tPath:            %s\n", sc_print_path(&cinfo->path));
		if (objs[i]->auth_id.len == 0) {
			struct sc_pkcs15_data *data_object;
			r = sc_pkcs15_read_data_object(p15card, cinfo, &data_object);
			if (r) {
				fprintf(stderr, "Data object read failed: %s\n", sc_strerror(r));
				if (r == SC_ERROR_FILE_NOT_FOUND)
					 continue; /* DEE emulation may say there is a file */
				return 1;
			}
			list_data_object("\tData", data_object->data, data_object->data_len);
			sc_pkcs15_free_data_object(data_object);
		}
		else {
			printf("\tAuth ID:         %s\n", sc_pkcs15_print_id(&objs[i]->auth_id));
		}

		printf("\n");
	}
	return 0;
}

static void print_key_usages(int usage)
{
	size_t i;
	const char *usages[] = {
		"encrypt", "decrypt", "sign", "signRecover", "wrap", "unwrap",
		"verify", "verifyRecover", "derive", "nonRepudiation"
	};
	const size_t usage_count = NELEMENTS(usages);
	for (i = 0; i < usage_count; i++)
		if (usage & (1 << i))
			printf(", %s", usages[i]);
}

static void print_key_access_flags(int flags)
{
	size_t i;
	const char *key_access_flags[] = {
		"sensitive", "extract", "alwaysSensitive","neverExtract", "local"
	};
	const size_t af_count = NELEMENTS(key_access_flags);
	for (i = 0; i < af_count; i++)
		if (flags & (1 << i))
			printf(", %s", key_access_flags[i]);
}

static void print_prkey_info(const struct sc_pkcs15_object *obj)
{
	struct sc_pkcs15_prkey_info *prkey = (struct sc_pkcs15_prkey_info *) obj->data;
	unsigned char guid[40];
	size_t guid_len;
	int i;
	int last_algo_refs = 0;

	if (compact) {
		printf("\t%-3s", key_types[7 & obj->type]);

		if (prkey->modulus_length)
			printf("[%lu]", (unsigned long)prkey->modulus_length);
		else {
			if (prkey->field_length)
				printf("[%lu]", (unsigned long)prkey->field_length);
		}
		printf("  ID:%s", sc_pkcs15_print_id(&prkey->id));
		printf("  Ref:0x%02X", prkey->key_reference);
		if (obj->auth_id.len != 0)
			printf("  AuthID:%s", sc_pkcs15_print_id(&obj->auth_id));
		printf("\n\t     %-18.*s [0x%02X", (int) sizeof obj->label, obj->label, prkey->usage);
		print_key_usages(prkey->usage);
		printf("]");
		return;
	}

	printf("Private %s Key [%.*s]\n", key_types[7 & obj->type], (int) sizeof obj->label, obj->label);
	print_common_flags(obj);
	printf("\tUsage          : [0x%02X]", prkey->usage);
	print_key_usages(prkey->usage);
	printf("\n");
	printf("\tAccess Flags   : [0x%02X]", prkey->access_flags);
	print_key_access_flags(prkey->access_flags);
	printf("\n");
	printf("\tAlgo_refs      : ");
	/* zero may be valid and don't know how many were read  print at least 1*/
	for (i = 0; i< SC_MAX_SUPPORTED_ALGORITHMS; i++) {
		if (prkey->algo_refs[i] != 0)
			last_algo_refs = i;
	}
	for (i = 0; i< last_algo_refs + 1; i++) {
		printf("%s%u", (i == 0) ? "" : ", ", prkey->algo_refs[i]);
	}
	printf("\n");

	print_access_rules(obj->access_rules, SC_PKCS15_MAX_ACCESS_RULES);

	if (prkey->modulus_length)
		printf("\tModLength      : %lu\n", (unsigned long)prkey->modulus_length);
	else
		printf("\tFieldLength    : %lu\n", (unsigned long)prkey->field_length);
	printf("\tKey ref        : %d (0x%02X)\n", prkey->key_reference, prkey->key_reference);
	printf("\tNative         : %s\n", prkey->native ? "yes" : "no");
	if (prkey->path.len || prkey->path.aid.len)
		printf("\tPath           : %s\n", sc_print_path(&prkey->path));
	if (obj->auth_id.len != 0)
		printf("\tAuth ID        : %s\n", sc_pkcs15_print_id(&obj->auth_id));
	printf("\tID             : %s\n", sc_pkcs15_print_id(&prkey->id));

	guid_len = sizeof(guid);
	if (!sc_pkcs15_get_object_guid(p15card, obj, 1, guid, &guid_len))   {
		printf("\tMD:guid        : ");
		if (strlen((char *)guid) == guid_len)   {
			printf("%s\n", (char *)guid);
		}
		else  {
			printf("0x'");
			util_hex_dump(stdout, guid, guid_len, "");
			printf("'\n");
		}
	}
}


static int list_private_keys(void)
{
	int r, i;
	struct sc_pkcs15_object *objs[32];

	r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_PRKEY, objs, 32);
	if (r < 0) {
		fprintf(stderr, "Private key enumeration failed: %s\n", sc_strerror(r));
		return 1;
	}
	if (compact)
		printf("Card has %d Private key(s).\n", r);
	else if (verbose)
		printf("Card has %d Private key(s).\n\n", r);
	for (i = 0; i < r; i++) {
		print_prkey_info(objs[i]);
		printf("\n");
	}
	return 0;
}

static void print_pubkey_info(const struct sc_pkcs15_object *obj)
{
	const struct sc_pkcs15_pubkey_info *pubkey = (const struct sc_pkcs15_pubkey_info *) obj->data;
	int have_path = (pubkey->path.len != 0) || (pubkey->path.aid.len != 0);

	if (compact) {
		printf("\t%-3s", key_types[7 & obj->type]);

		if (pubkey->modulus_length)
			printf("[%lu]", (unsigned long)pubkey->modulus_length);
		else
			printf("[FieldLength:%lu]", (unsigned long)pubkey->field_length);
		printf("  %s", sc_pkcs15_print_id(&pubkey->id));
		printf("  Ref:0x%02X", pubkey->key_reference);
		if (obj->auth_id.len != 0)
			printf("  AuthID:%s", sc_pkcs15_print_id(&obj->auth_id));
		printf("  %-18.*s [0x%02X", (int) sizeof obj->label, obj->label, pubkey->usage);
		print_key_usages(pubkey->usage);
		printf("]");
		return;
	}

	printf("Public %s Key [%.*s]\n", key_types[7 & obj->type], (int) sizeof obj->label, obj->label);
	print_common_flags(obj);
	printf("\tUsage          : [0x%02X]", pubkey->usage);
	print_key_usages(pubkey->usage);
	printf("\n");

	printf("\tAccess Flags   : [0x%02X]", pubkey->access_flags);
	print_key_access_flags(pubkey->access_flags);
	printf("\n");

	print_access_rules(obj->access_rules, SC_PKCS15_MAX_ACCESS_RULES);

	if (pubkey->modulus_length)   {
		printf("\tModLength      : %lu\n", (unsigned long)pubkey->modulus_length);
	}
	else if (pubkey->field_length)   {
		printf("\tFieldLength    : %lu\n", (unsigned long)pubkey->field_length);
	}
	else if ((obj->type == SC_PKCS15_TYPE_PUBKEY_EC || obj->type == SC_PKCS15_TYPE_PUBKEY_EDDSA)
			&& have_path)   {
		sc_pkcs15_pubkey_t *pkey = NULL;
		if (!sc_pkcs15_read_pubkey(p15card, obj, &pkey))   {
			printf("\tFieldLength    : %lu\n", (unsigned long)pkey->u.ec.params.field_length);
			sc_pkcs15_free_pubkey(pkey);
		}
	}

	printf("\tKey ref        : %d (0x%02X)\n", pubkey->key_reference,  pubkey->key_reference);
	printf("\tNative         : %s\n", pubkey->native ? "yes" : "no");
	if (have_path)
		printf("\tPath           : %s\n", sc_print_path(&pubkey->path));
	if (obj->auth_id.len != 0)
		printf("\tAuth ID        : %s\n", sc_pkcs15_print_id(&obj->auth_id));
	printf("\tID             : %s\n", sc_pkcs15_print_id(&pubkey->id));
	if (!have_path || obj->content.len)
		printf("\tDirectValue    : <%s>\n", obj->content.len ? "present" : "absent");
}

static int list_public_keys(void)
{
	int r, i;
	struct sc_pkcs15_object *objs[32];

	r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_PUBKEY, objs, 32);
	if (r < 0) {
		fprintf(stderr, "Public key enumeration failed: %s\n", sc_strerror(r));
		return 1;
	}
	if (compact)
		printf("Card has %d Public key(s).\n", r);
	else if (verbose)
		printf("Card has %d Public key(s).\n\n", r);
	for (i = 0; i < r; i++) {
		print_pubkey_info(objs[i]);
		printf("\n");
	}
	return 0;
}

static int read_public_key(void)
{
	int r;
	struct sc_pkcs15_id id;
	struct sc_pkcs15_object *obj;
	sc_pkcs15_pubkey_t *pubkey = NULL;
	sc_pkcs15_cert_t *cert = NULL;
	sc_pkcs15_der_t pem_key;

	pem_key.value = NULL;
	pem_key.len = 0;

	id.len = SC_PKCS15_MAX_ID_SIZE;
	sc_pkcs15_hex_string_to_id(opt_pubkey, &id);

	r = sc_pkcs15_find_pubkey_by_id(p15card, &id, &obj);
	if (r >= 0) {
		if (verbose)
			printf("Reading public key with ID '%s'\n", opt_pubkey);
		r = sc_pkcs15_read_pubkey(p15card, obj, &pubkey);
	} else if (r == SC_ERROR_OBJECT_NOT_FOUND) {
		/* No pubkey - try if there's a certificate */
		r = sc_pkcs15_find_cert_by_id(p15card, &id, &obj);
		if (r >= 0) {
			if (verbose)
				printf("Reading certificate with ID '%s'\n", opt_pubkey);
			r = sc_pkcs15_read_certificate(p15card, (sc_pkcs15_cert_info_t *) obj->data, &cert);
		}
		if (r >= 0)
			pubkey = cert->key;
	}

	if (r == SC_ERROR_OBJECT_NOT_FOUND) {
		fprintf(stderr, "Public key with ID '%s' not found.\n", opt_pubkey);
		r = 2;
		goto out;
	}
	if (r < 0) {
		fprintf(stderr, "Public key enumeration failed: %s\n", sc_strerror(r));
		r = 1;
		goto out;
	}
	if (!pubkey) {
		fprintf(stderr, "Public key not available\n");
		r = 1;
		goto out;
	}

	r = sc_pkcs15_encode_pubkey_as_spki(ctx, pubkey, &pem_key.value, &pem_key.len);
	if (r < 0) {
		fprintf(stderr, "Error encoding PEM key: %s\n", sc_strerror(r));
		r = 1;
	} else {
		r = print_pem_object("PUBLIC KEY", pem_key.value, pem_key.len);
		free(pem_key.value);
	}

out:
	if (cert)
		sc_pkcs15_free_certificate(cert);
	else
		sc_pkcs15_free_pubkey(pubkey);

	return r;
}

static void print_skey_info(const struct sc_pkcs15_object *obj)
{
	static const char *skey_types[] = { "", "Generic", "DES", "2DES", "3DES", "", "", "" };
	struct sc_pkcs15_skey_info *skey = (struct sc_pkcs15_skey_info *) obj->data;
	unsigned char guid[40];
	size_t guid_len;

	printf("Secret %s Key [%.*s]\n", skey_types[7 & obj->type], (int) sizeof obj->label, obj->label);
	print_common_flags(obj);
	printf("\tUsage          : [0x%02X]", skey->usage);
	print_key_usages(skey->usage);
	printf("\n");

	printf("\tAccess Flags   : [0x%02X]", skey->access_flags);
	print_key_access_flags(skey->access_flags);
	printf("\n");

	print_access_rules(obj->access_rules, SC_PKCS15_MAX_ACCESS_RULES);

	printf("\tSize           : %lu bits\n", (unsigned long)skey->value_len);
	printf("\tID             : %s\n", sc_pkcs15_print_id(&skey->id));
	printf("\tNative         : %s\n", skey->native ? "yes" : "no");
	printf("\tKey ref        : %d (0x%02X)\n", skey->key_reference, skey->key_reference);

	if (skey->path.len || skey->path.aid.len)
		printf("\tPath           : %s\n", sc_print_path(&skey->path));

	guid_len = sizeof(guid);
	if (!sc_pkcs15_get_object_guid(p15card, obj, 1, guid, &guid_len))   {
		printf("\tGUID           : %s\n", (char *)guid);
	}

}

static int list_skeys(void)
{
	int r, i;
	struct sc_pkcs15_object *objs[32];

	r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_SKEY, objs, 32);
	if (r < 0) {
		fprintf(stderr, "Secret key enumeration failed: %s\n", sc_strerror(r));
		return 1;
	}
	if (verbose)
		printf("Card has %d Secret key(s).\n\n", r);
	for (i = 0; i < r; i++) {
		print_skey_info(objs[i]);
		printf("\n");
	}

	return 0;
}


#if defined(ENABLE_OPENSSL) && (defined(_WIN32) || defined(HAVE_INTTYPES_H))

static void print_ssh_key(FILE *outf, const char * alg, struct sc_pkcs15_object *obj, unsigned char * buf, uint32_t len) {
	unsigned char *uu;
	int r;

	uu = malloc(len*2); // Way over - even if we have extra LFs; as each 6 bits take one byte.
	if (!uu)
		return;

	if (opt_rfc4716) {
		r = sc_base64_encode(buf, len, uu, 2*len, 64);
		if (r < 0) {
			free(uu);
			return;
		}

		fprintf(outf,"---- BEGIN SSH2 PUBLIC KEY ----\n");

		if (obj->label[0] != '\0')
			fprintf(outf,"Comment: \"%.*s\"\n", (int) sizeof obj->label, obj->label);

		fprintf(outf,"%s", uu);
		fprintf(outf,"---- END SSH2 PUBLIC KEY ----\n");
	} else {
		// Old style openssh - [<quote protected options> <whitespace> <keytype> <whitespace> <key> [<whitespace> anything else]
		//
		r = sc_base64_encode(buf, len, uu, 2*len, 0);
		if (r < 0) {
			free(uu);
			return;
		}

		if (obj->label[0] != '\0')
			fprintf(outf,"%s %s %.*s\n", alg, uu, (int) sizeof obj->label, obj->label);
		else
			fprintf(outf,"%s %s\n", alg, uu);
	}
	free(uu);
	return;
}

static int read_ssh_key(void)
{
	int r;
	struct sc_pkcs15_id id;
	struct sc_pkcs15_object *obj = NULL;
	sc_pkcs15_pubkey_t *pubkey = NULL;
	sc_pkcs15_cert_t *cert = NULL;
	FILE *outf = NULL;

	if (opt_outfile != NULL) {
		outf = fopen(opt_outfile, "w");
		if (outf == NULL) {
			fprintf(stderr, "Error opening file '%s': %s\n", opt_outfile, strerror(errno));
			goto fail2;
		}
	}
	else   {
		outf = stdout;
	}

	id.len = SC_PKCS15_MAX_ID_SIZE;
	sc_pkcs15_hex_string_to_id(opt_pubkey, &id);

	r = sc_pkcs15_find_pubkey_by_id(p15card, &id, &obj);
	if (r >= 0) {
		if (verbose)
			fprintf(stderr,"Reading ssh key with ID '%s'\n", opt_pubkey);
		r = authenticate(obj);
		if (r >= 0)
			r = sc_pkcs15_read_pubkey(p15card, obj, &pubkey);
	}
	else if (r == SC_ERROR_OBJECT_NOT_FOUND) {
		/* No pubkey - try if there's a certificate */
		r = sc_pkcs15_find_cert_by_id(p15card, &id, &obj);
		if (r >= 0) {
			if (verbose)
				fprintf(stderr,"Reading certificate with ID '%s'\n", opt_pubkey);
			r = sc_pkcs15_read_certificate(p15card, (sc_pkcs15_cert_info_t *) obj->data, &cert);
		}
		if (r >= 0)
			pubkey = cert->key;
	}

	if (r == SC_ERROR_OBJECT_NOT_FOUND) {
		if (opt_outfile != NULL)
			fclose(outf);
		fprintf(stderr, "Public key with ID '%s' not found.\n", opt_pubkey);
		return 2;
	}
	if (r < 0) {
		if (opt_outfile != NULL)
			fclose(outf);
		fprintf(stderr, "Public key enumeration failed: %s\n", sc_strerror(r));
		return 1;
	}

	if (pubkey->algorithm == SC_ALGORITHM_EDDSA) {
		// SSH supports only ed25519 key now

		char alg[20];
		/* Large enough to fit the following:
		 * 2 x 4B item length headers
		 * max 11B algorithm name, 32B key data */
		unsigned char buf[64];
		unsigned int len, n;

		n = pubkey->u.eddsa.pubkey.len;
		if (n != 32) {
			fprintf(stderr, "Wrong public key length\n");
			goto fail2;
		}

		buf[0] = 0;
		buf[1] = 0;
		buf[2] = 0;
		len = snprintf((char *) buf+4, 20, "ssh-ed25519");
		memcpy(alg, buf+4, 20);
		buf[3] = len;
		len += 4;

		buf[len++] = 0;
		buf[len++] = 0;
		buf[len++] = 0;
		buf[len++] = n & 0xff;
		memcpy(buf + len, pubkey->u.eddsa.pubkey.value, n);
		len += n;

		print_ssh_key(outf, alg, obj, buf, len);
	}
	if (pubkey->algorithm == SC_ALGORITHM_EC) {
		// support only for NIST
		// 'ssh-keygen -t ecdsa' allow only field lengths 256/384/521

		static struct supported_ec_curves {
			char *curve_name;
			struct sc_object_id curve_oid;
			size_t size;
		} ec_curves[] = {
			{"secp256r1", {{1, 2, 840, 10045, 3, 1, 7, -1}},256},
			{"secp384r1", {{1, 3, 132, 0, 34, -1}},         384},
			{"secp521r1", {{1, 3, 132, 0, 35, -1}},         521},
		        {NULL, {{-1}}, 0},
		};
		char alg[20];
		/* Large enough to fit the following:
		 * 3 x 4B item length headers
		 * max 20B algorithm name, 9B curve name, max 256B key data */
		unsigned char buf[300];
		unsigned int i, len, tmp, n;

		for (n = 0,i = 0; ec_curves[i].curve_name != NULL; i++) {
			if(sc_compare_oid (&ec_curves[i].curve_oid,&pubkey->u.ec.params.id))
				n = ec_curves[i].size;
		}
		if (!n) {
			fprintf(stderr, "Unsupported curve\n");
			goto fail2;
		}
		if (n != pubkey->u.ec.params.field_length) {
			fprintf(stderr, "Wrong field length\n");
			goto fail2;
		}

		buf[0] = 0;
		buf[1] = 0;
		buf[2] = 0;
		len = snprintf((char *) buf+4, 20, "ecdsa-sha2-nistp%d", n);
		memcpy(alg, buf+4, 20);
		buf[3] = len;

		len += 4;
		buf[len++] = 0;
		buf[len++] = 0;
		buf[len++] = 0;
		tmp = snprintf((char *) buf+len+1, 9, "nistp%d", n);
		buf[len++] = tmp;
		len += tmp;

		n = pubkey->u.ec.ecpointQ.len;
		if(n > 255) {
			fprintf(stderr, "Wrong public key length\n");
			goto fail2;
		}
		buf[len++] = 0;
		buf[len++] = 0;
		buf[len++] = 0;
		buf[len++] = n & 0xff;
		memcpy(buf+len,pubkey->u.ec.ecpointQ.value,n);
		len += n;

		print_ssh_key(outf, alg, obj, buf, len);
	}

	if (pubkey->algorithm == SC_ALGORITHM_RSA) {
		unsigned char buf[2048];
		uint32_t len, n;

		if (!pubkey->u.rsa.modulus.data || !pubkey->u.rsa.modulus.len ||
				!pubkey->u.rsa.exponent.data || !pubkey->u.rsa.exponent.len)  {
			fprintf(stderr, "Failed to decode public RSA key.\n");
			goto fail2;
		}

		buf[0]=0;
		buf[1]=0;
		buf[2]=0;
		buf[3]=7;

		len = sprintf((char *) buf+4,"ssh-rsa");
		len+=4;

		if (sizeof(buf)-len < 4+pubkey->u.rsa.exponent.len)
			goto fail;

		n = pubkey->u.rsa.exponent.len;
		if (pubkey->u.rsa.exponent.data[0] & 0x80)
			n++;

		buf[len++]=(n >>24) & 0xff;
		buf[len++]=(n >>16) & 0xff;
		buf[len++]=(n >>8) & 0xff;
		buf[len++]=(n) & 0xff;
		if (pubkey->u.rsa.exponent.data[0] & 0x80)
			buf[len++]= 0;

		memcpy(buf+len,pubkey->u.rsa.exponent.data, pubkey->u.rsa.exponent.len);
		len += pubkey->u.rsa.exponent.len;

		if (sizeof(buf)-len < 5+pubkey->u.rsa.modulus.len)
			goto fail;

		n = pubkey->u.rsa.modulus.len;
		if (pubkey->u.rsa.modulus.data[0] & 0x80)
			n++;
		buf[len++]=(n >>24) & 0xff;
		buf[len++]=(n >>16) & 0xff;
		buf[len++]=(n >>8) & 0xff;
		buf[len++]=(n) & 0xff;

		if (pubkey->u.rsa.modulus.data[0] & 0x80)
			buf[len++]= 0;

		memcpy(buf+len,pubkey->u.rsa.modulus.data, pubkey->u.rsa.modulus.len);
		len += pubkey->u.rsa.modulus.len;

		print_ssh_key(outf, "ssh-rsa", obj, buf, len);
	}

	if (pubkey->algorithm == SC_ALGORITHM_DSA) {
		unsigned char buf[2048];
		uint32_t len;
		uint32_t n;

		if (!pubkey->u.dsa.p.data || !pubkey->u.dsa.p.len ||
				!pubkey->u.dsa.q.data || !pubkey->u.dsa.q.len ||
				!pubkey->u.dsa.g.data || !pubkey->u.dsa.g.len ||
				!pubkey->u.dsa.pub.data || !pubkey->u.dsa.pub.len)   {
			fprintf(stderr, "Failed to decode DSA key.\n");
			goto fail2;
		}

		buf[0]=0;
		buf[1]=0;
		buf[2]=0;
		buf[3]=7;

		len = sprintf((char *) buf+4,"ssh-dss");
		len+=4;

		if (sizeof(buf)-len < 5+pubkey->u.dsa.p.len)
			goto fail;

		n = pubkey->u.dsa.p.len;
		if (pubkey->u.dsa.p.data[0] & 0x80)
			n++;

		buf[len++]=(n >>24) & 0xff;
		buf[len++]=(n >>16) & 0xff;
		buf[len++]=(n >>8) & 0xff;
		buf[len++]=(n) & 0xff;
		if (pubkey->u.dsa.p.data[0] & 0x80)
			buf[len++]= 0;

		memcpy(buf+len,pubkey->u.dsa.p.data, pubkey->u.dsa.p.len);
		len += pubkey->u.dsa.p.len;

		if (sizeof(buf)-len < 5+pubkey->u.dsa.q.len)
			goto fail;

		n = pubkey->u.dsa.q.len;
		if (pubkey->u.dsa.q.data[0] & 0x80)
			n++;

		buf[len++]=(n >>24) & 0xff;
		buf[len++]=(n >>16) & 0xff;
		buf[len++]=(n >>8) & 0xff;
		buf[len++]=(n) & 0xff;
		if (pubkey->u.dsa.q.data[0] & 0x80)
			buf[len++]= 0;

		memcpy(buf+len,pubkey->u.dsa.q.data, pubkey->u.dsa.q.len);
		len += pubkey->u.dsa.q.len;

		if (sizeof(buf)-len < 5+pubkey->u.dsa.g.len)
			goto fail;
		n = pubkey->u.dsa.g.len;
		if (pubkey->u.dsa.g.data[0] & 0x80)
			n++;

		buf[len++]=(n >>24) & 0xff;
		buf[len++]=(n >>16) & 0xff;
		buf[len++]=(n >>8) & 0xff;
		buf[len++]=(n) & 0xff;
		if (pubkey->u.dsa.g.data[0] & 0x80)
			buf[len++]= 0;

		memcpy(buf+len,pubkey->u.dsa.g.data, pubkey->u.dsa.g.len);
		len += pubkey->u.dsa.g.len;

		if (sizeof(buf)-len < 5+pubkey->u.dsa.pub.len)
			goto fail;

		n = pubkey->u.dsa.pub.len;
		if (pubkey->u.dsa.pub.data[0] & 0x80)
			n++;

		buf[len++]=(n >>24) & 0xff;
		buf[len++]=(n >>16) & 0xff;
		buf[len++]=(n >>8) & 0xff;
		buf[len++]=(n) & 0xff;
		if (pubkey->u.dsa.pub.data[0] & 0x80)
			buf[len++]= 0;

		memcpy(buf+len,pubkey->u.dsa.pub.data, pubkey->u.dsa.pub.len);
		len += pubkey->u.dsa.pub.len;

		print_ssh_key(outf, "ssh-dss", obj, buf, len);
	}

	if (opt_outfile != NULL)
		fclose(outf);
	if (cert)
		sc_pkcs15_free_certificate(cert);
	else
		sc_pkcs15_free_pubkey(pubkey);
	return 0;
fail:
	printf("can't convert key: buffer too small\n");
fail2:
	if (opt_outfile != NULL && outf != NULL)
		fclose(outf);
	if (cert)
		sc_pkcs15_free_certificate(cert);
	else
		sc_pkcs15_free_pubkey(pubkey);
	return SC_ERROR_OUT_OF_MEMORY;
}

#endif

static sc_pkcs15_object_t *
get_pin_info(void)
{
{'fraction_non_alphanumeric': 0.17757649459750352, 'fraction_numerical': 0.02787212926853133, 'mean_word_length': 3.9404046681858986, 'pattern_counts': {'":': 2, '<': 161, '<?xml version=': 0, '>': 607, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7522688', 'n_tokens_mistral': 32892, 'n_tokens_neox': 29804, 'n_words': 6556}