2006-02-14 22:09:10 +00:00
|
|
|
/*
|
|
|
|
* piv-tool.c: Tool for accessing smart cards with libopensc
|
|
|
|
*
|
2006-12-19 21:35:42 +00:00
|
|
|
* Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
|
2010-02-05 06:16:37 +00:00
|
|
|
* Copyright (C) 2005,2010 Douglas E. Engert <deengert@anl.gov>
|
2006-02-14 22:09:10 +00:00
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
2010-03-04 08:14:36 +00:00
|
|
|
#include "config.h"
|
|
|
|
|
2006-02-14 22:09:10 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <sys/stat.h>
|
2010-12-03 15:15:29 +00:00
|
|
|
|
|
|
|
/* Module only built if OPENSSL is enabled */
|
2017-05-07 19:42:32 +00:00
|
|
|
#include <openssl/opensslv.h>
|
|
|
|
#include "libopensc/sc-ossl-compat.h"
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
|
2010-12-03 15:15:29 +00:00
|
|
|
#include <openssl/opensslconf.h>
|
2017-05-07 19:42:32 +00:00
|
|
|
#include <openssl/crypto.h>
|
|
|
|
#endif
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x00907000L
|
|
|
|
#include <openssl/conf.h>
|
|
|
|
#endif
|
|
|
|
|
2006-02-14 22:09:10 +00:00
|
|
|
#include <openssl/rsa.h>
|
2017-05-07 19:42:32 +00:00
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined(OPENSSL_NO_EC) && !defined(OPENSSL_NO_ECDSA)
|
2010-12-01 20:08:42 +00:00
|
|
|
#include <openssl/ec.h>
|
2017-05-07 19:42:32 +00:00
|
|
|
#include <openssl/ecdsa.h>
|
2010-12-03 15:44:29 +00:00
|
|
|
#endif
|
2010-12-01 20:08:42 +00:00
|
|
|
#include <openssl/evp.h>
|
2006-02-14 22:09:10 +00:00
|
|
|
#include <openssl/x509.h>
|
|
|
|
#include <openssl/bn.h>
|
|
|
|
#include <openssl/bio.h>
|
|
|
|
#include <openssl/pem.h>
|
|
|
|
#include <openssl/err.h>
|
2010-12-01 20:08:42 +00:00
|
|
|
#include <openssl/obj_mac.h>
|
2006-02-14 22:09:10 +00:00
|
|
|
|
2010-03-04 08:14:36 +00:00
|
|
|
#include "libopensc/opensc.h"
|
|
|
|
#include "libopensc/cardctl.h"
|
2010-03-29 08:13:55 +00:00
|
|
|
#include "libopensc/asn1.h"
|
2010-03-04 08:14:36 +00:00
|
|
|
#include "util.h"
|
|
|
|
|
2007-06-29 13:19:19 +00:00
|
|
|
static const char *app_name = "piv-tool";
|
2006-02-14 22:09:10 +00:00
|
|
|
|
2010-01-24 15:29:47 +00:00
|
|
|
static int opt_wait = 0;
|
2006-02-14 22:09:10 +00:00
|
|
|
static char ** opt_apdus;
|
2010-01-24 15:29:47 +00:00
|
|
|
static char * opt_reader;
|
2006-02-14 22:09:10 +00:00
|
|
|
static int opt_apdu_count = 0;
|
|
|
|
static int verbose = 0;
|
|
|
|
|
|
|
|
enum {
|
|
|
|
OPT_SERIAL = 0x100,
|
|
|
|
};
|
|
|
|
|
2007-06-29 13:19:19 +00:00
|
|
|
static const struct option options[] = {
|
2007-06-21 12:58:57 +00:00
|
|
|
{ "serial", 0, NULL, OPT_SERIAL },
|
|
|
|
{ "name", 0, NULL, 'n' },
|
2011-04-08 12:28:59 +00:00
|
|
|
{ "admin", 1, NULL, 'A' },
|
|
|
|
{ "genkey", 1, NULL, 'G' },
|
2010-02-05 06:16:37 +00:00
|
|
|
{ "object", 1, NULL, 'O' },
|
|
|
|
{ "cert", 1, NULL, 'C' },
|
2011-04-11 12:02:47 +00:00
|
|
|
{ "compresscert", 1, NULL, 'Z' },
|
|
|
|
{ "out", 1, NULL, 'o' },
|
|
|
|
{ "in", 1, NULL, 'i' },
|
2007-06-21 12:58:57 +00:00
|
|
|
{ "send-apdu", 1, NULL, 's' },
|
|
|
|
{ "reader", 1, NULL, 'r' },
|
|
|
|
{ "card-driver", 1, NULL, 'c' },
|
|
|
|
{ "wait", 0, NULL, 'w' },
|
|
|
|
{ "verbose", 0, NULL, 'v' },
|
|
|
|
{ NULL, 0, NULL, 0 }
|
2006-02-14 22:09:10 +00:00
|
|
|
};
|
|
|
|
|
2007-06-29 13:19:19 +00:00
|
|
|
static const char *option_help[] = {
|
2015-10-12 09:55:13 +00:00
|
|
|
"Print the card serial number",
|
2006-02-14 22:09:10 +00:00
|
|
|
"Identify the card and print its name",
|
2015-10-12 09:55:13 +00:00
|
|
|
"Authenticate using default 3DES key",
|
2006-02-14 22:09:10 +00:00
|
|
|
"Generate key <ref>:<alg> 9A:06 on card, and output pubkey",
|
2010-02-05 06:16:37 +00:00
|
|
|
"Load an object <containerID> containerID as defined in 800-73 without leading 0x",
|
2013-05-10 13:14:20 +00:00
|
|
|
"Load a cert <ref> where <ref> is 9A,9C,9D or 9E",
|
2015-10-12 09:55:13 +00:00
|
|
|
"Load a cert that has been gzipped <ref>",
|
2010-04-13 11:29:45 +00:00
|
|
|
"Output file for cert or key",
|
2015-10-12 09:55:13 +00:00
|
|
|
"Input file for cert",
|
2006-02-14 22:09:10 +00:00
|
|
|
"Sends an APDU in format AA:BB:CC:DD:EE:FF...",
|
|
|
|
"Uses reader number <arg> [0]",
|
|
|
|
"Forces the use of driver <arg> [auto-detect]",
|
|
|
|
"Wait for a card to be inserted",
|
|
|
|
"Verbose operation. Use several times to enable debug output.",
|
|
|
|
};
|
|
|
|
|
2007-06-21 12:58:57 +00:00
|
|
|
static sc_context_t *ctx = NULL;
|
|
|
|
static sc_card_t *card = NULL;
|
|
|
|
static BIO * bp = NULL;
|
2010-12-01 20:08:42 +00:00
|
|
|
static EVP_PKEY * evpkey = NULL;
|
2006-02-14 22:09:10 +00:00
|
|
|
|
2010-02-05 06:16:37 +00:00
|
|
|
static int load_object(const char * object_id, const char * object_file)
|
|
|
|
{
|
2015-04-29 21:22:30 +00:00
|
|
|
FILE *fp = NULL;
|
2010-02-05 06:16:37 +00:00
|
|
|
sc_path_t path;
|
|
|
|
size_t derlen;
|
|
|
|
u8 *der = NULL;
|
|
|
|
u8 *body;
|
|
|
|
size_t bodylen;
|
2015-04-29 21:22:30 +00:00
|
|
|
int r = -1;
|
2010-02-05 06:16:37 +00:00
|
|
|
struct stat stat_buf;
|
|
|
|
|
2015-01-22 19:29:33 +00:00
|
|
|
if(!object_file || (fp=fopen(object_file, "r")) == NULL){
|
2012-04-02 22:00:56 +00:00
|
|
|
printf("Cannot open object file, %s %s\n",
|
2010-02-05 06:16:37 +00:00
|
|
|
(object_file)?object_file:"", strerror(errno));
|
2015-04-29 21:22:30 +00:00
|
|
|
goto err;
|
2010-02-05 06:16:37 +00:00
|
|
|
}
|
2012-04-02 22:00:56 +00:00
|
|
|
|
2015-01-28 03:45:08 +00:00
|
|
|
if (0 != stat(object_file, &stat_buf)) {
|
|
|
|
printf("unable to read file %s\n",object_file);
|
2015-04-29 21:22:30 +00:00
|
|
|
goto err;
|
2015-01-28 03:45:08 +00:00
|
|
|
}
|
2010-02-05 06:16:37 +00:00
|
|
|
derlen = stat_buf.st_size;
|
|
|
|
der = malloc(derlen);
|
|
|
|
if (der == NULL) {
|
|
|
|
printf("file %s is too big, %lu\n",
|
|
|
|
object_file, (unsigned long)derlen);
|
2015-04-29 21:22:30 +00:00
|
|
|
goto err;
|
2010-02-05 06:16:37 +00:00
|
|
|
}
|
|
|
|
if (1 != fread(der, derlen, 1, fp)) {
|
|
|
|
printf("unable to read file %s\n",object_file);
|
2015-04-29 21:22:30 +00:00
|
|
|
goto err;
|
2010-02-05 06:16:37 +00:00
|
|
|
}
|
|
|
|
/* check if tag and length are valid */
|
|
|
|
body = (u8 *)sc_asn1_find_tag(card->ctx, der, derlen, 0x53, &bodylen);
|
|
|
|
if (body == NULL || derlen != body - der + bodylen) {
|
|
|
|
fprintf(stderr, "object tag or length not valid\n");
|
2015-04-29 21:22:30 +00:00
|
|
|
goto err;
|
2010-02-05 06:16:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sc_format_path(object_id, &path);
|
2012-04-02 22:00:56 +00:00
|
|
|
|
2010-02-05 06:16:37 +00:00
|
|
|
r = sc_select_file(card, &path, NULL);
|
|
|
|
if (r < 0) {
|
|
|
|
fprintf(stderr, "select file failed\n");
|
2015-04-29 21:22:30 +00:00
|
|
|
r = -1;
|
|
|
|
goto err;
|
2010-02-05 06:16:37 +00:00
|
|
|
}
|
2012-04-02 22:00:56 +00:00
|
|
|
/* leave 8 bits for flags, and pass in total length */
|
2010-02-05 06:16:37 +00:00
|
|
|
r = sc_write_binary(card, 0, der, derlen, derlen<<8);
|
|
|
|
|
2015-04-29 21:22:30 +00:00
|
|
|
err:
|
|
|
|
free(der);
|
|
|
|
if (fp)
|
|
|
|
fclose(fp);
|
|
|
|
|
2010-02-05 06:16:37 +00:00
|
|
|
return r;
|
2012-04-02 22:00:56 +00:00
|
|
|
}
|
2010-02-05 06:16:37 +00:00
|
|
|
|
2006-02-14 22:09:10 +00:00
|
|
|
|
2007-03-10 10:46:32 +00:00
|
|
|
static int load_cert(const char * cert_id, const char * cert_file,
|
|
|
|
int compress)
|
2006-02-14 22:09:10 +00:00
|
|
|
{
|
|
|
|
X509 * cert = NULL;
|
2015-04-29 21:22:30 +00:00
|
|
|
FILE *fp = NULL;
|
2006-02-14 22:09:10 +00:00
|
|
|
u8 buf[1];
|
|
|
|
size_t buflen = 1;
|
|
|
|
sc_path_t path;
|
|
|
|
u8 *der = NULL;
|
|
|
|
u8 *p;
|
|
|
|
size_t derlen;
|
2015-04-29 21:22:30 +00:00
|
|
|
int r = -1;
|
2006-02-14 22:09:10 +00:00
|
|
|
|
2015-02-04 18:19:57 +00:00
|
|
|
if (!cert_file) {
|
|
|
|
printf("Missing cert file\n");
|
2015-04-29 21:22:30 +00:00
|
|
|
goto err;
|
2015-02-04 18:19:57 +00:00
|
|
|
}
|
|
|
|
|
2006-02-14 22:09:10 +00:00
|
|
|
if((fp=fopen(cert_file, "r"))==NULL){
|
2012-04-02 22:00:56 +00:00
|
|
|
printf("Cannot open cert file, %s %s\n",
|
2015-02-04 18:19:57 +00:00
|
|
|
cert_file, strerror(errno));
|
2015-04-29 21:22:30 +00:00
|
|
|
goto err;
|
2006-02-14 22:09:10 +00:00
|
|
|
}
|
2007-03-10 10:46:32 +00:00
|
|
|
if (compress) { /* file is gziped already */
|
|
|
|
struct stat stat_buf;
|
|
|
|
|
2015-01-28 03:45:08 +00:00
|
|
|
if (0 != stat(cert_file, &stat_buf)) {
|
|
|
|
printf("unable to read file %s\n",cert_file);
|
2015-04-29 21:22:30 +00:00
|
|
|
goto err;
|
2015-01-28 03:45:08 +00:00
|
|
|
}
|
2007-03-10 10:46:32 +00:00
|
|
|
derlen = stat_buf.st_size;
|
|
|
|
der = malloc(derlen);
|
|
|
|
if (der == NULL) {
|
2008-05-05 13:00:01 +00:00
|
|
|
printf("file %s is too big, %lu\n",
|
|
|
|
cert_file, (unsigned long)derlen);
|
2015-04-29 21:22:30 +00:00
|
|
|
goto err;
|
2007-03-10 10:46:32 +00:00
|
|
|
}
|
|
|
|
if (1 != fread(der, derlen, 1, fp)) {
|
|
|
|
printf("unable to read file %s\n",cert_file);
|
2015-04-29 21:22:30 +00:00
|
|
|
goto err;
|
2007-03-10 10:46:32 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
cert = PEM_read_X509(fp, &cert, NULL, NULL);
|
|
|
|
if(cert == NULL){
|
|
|
|
printf("file %s does not conatin PEM-encoded certificate\n",
|
|
|
|
cert_file);
|
2015-04-29 21:22:30 +00:00
|
|
|
goto err;
|
2007-03-10 10:46:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
derlen = i2d_X509(cert, NULL);
|
Do not cast the return value of malloc(3) and calloc(3)
From http://en.wikipedia.org/wiki/Malloc#Casting_and_type_safety
" Casting and type safety
malloc returns a void pointer (void *), which indicates that it is a
pointer to a region of unknown data type. One may "cast" (see type
conversion) this pointer to a specific type, as in
int *ptr = (int*)malloc(10 * sizeof (int));
When using C, this is considered bad practice; it is redundant under the
C standard. Moreover, putting in a cast may mask failure to include the
header stdlib.h, in which the prototype for malloc is found. In the
absence of a prototype for malloc, the C compiler will assume that
malloc returns an int, and will issue a warning in a context such as the
above, provided the error is not masked by a cast. On certain
architectures and data models (such as LP64 on 64 bit systems, where
long and pointers are 64 bit and int is 32 bit), this error can actually
result in undefined behavior, as the implicitly declared malloc returns
a 32 bit value whereas the actually defined function returns a 64 bit
value. Depending on calling conventions and memory layout, this may
result in stack smashing.
The returned pointer need not be explicitly cast to a more specific
pointer type, since ANSI C defines an implicit conversion between the
void pointer type and other pointers to objects. An explicit cast of
malloc's return value is sometimes performed because malloc originally
returned a char *, but this cast is unnecessary in standard C
code.[4][5] Omitting the cast, however, creates an incompatibility with
C++, which does require it.
The lack of a specific pointer type returned from malloc is type-unsafe
behaviour: malloc allocates based on byte count but not on type. This
distinguishes it from the C++ new operator that returns a pointer whose
type relies on the operand. (see C Type Safety). "
See also
http://www.opensc-project.org/pipermail/opensc-devel/2010-August/014586.html
git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@4636 c6295689-39f2-0310-b995-f0e70906c6a9
2010-08-18 15:08:51 +00:00
|
|
|
der = malloc(derlen);
|
2017-04-20 19:08:49 +00:00
|
|
|
if (!der) {
|
|
|
|
goto err;
|
|
|
|
}
|
2007-03-10 10:46:32 +00:00
|
|
|
p = der;
|
|
|
|
i2d_X509(cert, &p);
|
|
|
|
}
|
2006-02-14 22:09:10 +00:00
|
|
|
sc_hex_to_bin(cert_id, buf,&buflen);
|
2012-04-02 22:00:56 +00:00
|
|
|
|
2006-02-14 22:09:10 +00:00
|
|
|
switch (buf[0]) {
|
|
|
|
case 0x9a: sc_format_path("0101",&path); break;
|
|
|
|
case 0x9c: sc_format_path("0100",&path); break;
|
|
|
|
case 0x9d: sc_format_path("0102",&path); break;
|
2013-05-23 13:47:15 +00:00
|
|
|
case 0x9e: sc_format_path("0500",&path); break;
|
2006-02-14 22:09:10 +00:00
|
|
|
default:
|
2013-05-23 13:47:15 +00:00
|
|
|
fprintf(stderr,"cert must be 9A, 9C, 9D or 9E\n");
|
2015-04-29 21:22:30 +00:00
|
|
|
r = 2;
|
|
|
|
goto err;
|
2006-02-14 22:09:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
r = sc_select_file(card, &path, NULL);
|
|
|
|
if (r < 0) {
|
|
|
|
fprintf(stderr, "select file failed\n");
|
2015-04-29 21:22:30 +00:00
|
|
|
goto err;
|
2006-02-14 22:09:10 +00:00
|
|
|
}
|
2010-02-05 06:16:37 +00:00
|
|
|
/* we pass length and 8 bits of flag to card-piv.c write_binary */
|
|
|
|
/* pass in its a cert and if needs compress */
|
2012-04-02 22:00:56 +00:00
|
|
|
r = sc_write_binary(card, 0, der, derlen, (derlen<<8) | (compress<<4) | 1);
|
|
|
|
|
2015-04-29 21:22:30 +00:00
|
|
|
err:
|
|
|
|
free(der);
|
|
|
|
if (fp)
|
|
|
|
fclose(fp);
|
2006-02-14 22:09:10 +00:00
|
|
|
|
2015-04-29 21:22:30 +00:00
|
|
|
return r;
|
2006-02-14 22:09:10 +00:00
|
|
|
}
|
2006-02-15 08:10:45 +00:00
|
|
|
static int admin_mode(const char* admin_info)
|
2006-02-14 22:09:10 +00:00
|
|
|
{
|
|
|
|
int r;
|
|
|
|
u8 opts[3];
|
|
|
|
size_t buflen = 2;
|
|
|
|
|
2012-04-02 22:00:56 +00:00
|
|
|
|
|
|
|
if (strlen(admin_info) == 7 &&
|
2006-02-14 22:09:10 +00:00
|
|
|
(admin_info[0] == 'A' || admin_info[0] == 'M') &&
|
|
|
|
admin_info[1] == ':' &&
|
|
|
|
(sc_hex_to_bin(admin_info+2, opts+1, &buflen) == 0) &&
|
|
|
|
buflen == 2) {
|
|
|
|
opts[0] = admin_info[0];
|
|
|
|
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, " admin_mode params <M|A>:<keyref>:<alg>\n");
|
|
|
|
return -1;
|
|
|
|
}
|
2012-04-02 22:00:56 +00:00
|
|
|
|
2010-09-24 20:37:22 +00:00
|
|
|
r = sc_card_ctl(card, SC_CARDCTL_PIV_AUTHENTICATE, &opts);
|
2006-02-14 22:09:10 +00:00
|
|
|
if (r)
|
|
|
|
fprintf(stderr, " admin_mode failed %d\n", r);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* generate a new key pair, and save public key in newkey */
|
2006-02-15 08:10:45 +00:00
|
|
|
static int gen_key(const char * key_info)
|
2006-02-14 22:09:10 +00:00
|
|
|
{
|
|
|
|
int r;
|
|
|
|
u8 buf[2];
|
|
|
|
size_t buflen = 2;
|
2010-09-24 20:37:22 +00:00
|
|
|
sc_cardctl_piv_genkey_info_t
|
2010-12-01 20:08:42 +00:00
|
|
|
keydata = {0, 0, 0, 0, NULL, 0, NULL, 0, NULL, 0};
|
2006-02-14 22:09:10 +00:00
|
|
|
unsigned long expl;
|
|
|
|
u8 expc[4];
|
2010-12-06 14:41:15 +00:00
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined(OPENSSL_NO_EC)
|
2011-02-07 16:23:54 +00:00
|
|
|
int nid = -1;
|
2012-04-02 22:00:56 +00:00
|
|
|
#endif
|
2006-02-14 22:09:10 +00:00
|
|
|
sc_hex_to_bin(key_info, buf, &buflen);
|
|
|
|
if (buflen != 2) {
|
|
|
|
fprintf(stderr, "<keyref>:<algid> invalid, example: 9A:06\n");
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
switch (buf[0]) {
|
|
|
|
case 0x9a:
|
|
|
|
case 0x9c:
|
|
|
|
case 0x9d:
|
2013-05-10 13:14:20 +00:00
|
|
|
case 0x9e:
|
2006-02-14 22:09:10 +00:00
|
|
|
keydata.key_num = buf[0];
|
|
|
|
break;
|
|
|
|
default:
|
2013-05-10 13:14:20 +00:00
|
|
|
fprintf(stderr, "<keyref>:<algid> must be 9A, 9C, 9D or 9E\n");
|
2006-02-14 22:09:10 +00:00
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (buf[1]) {
|
2010-09-24 20:37:22 +00:00
|
|
|
case 0x05: keydata.key_bits = 3072; break;
|
|
|
|
case 0x06: keydata.key_bits = 1024; break;
|
|
|
|
case 0x07: keydata.key_bits = 2048; break;
|
2012-04-02 22:00:56 +00:00
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined(OPENSSL_NO_EC)
|
|
|
|
case 0x11: keydata.key_bits = 0;
|
2010-12-01 20:08:42 +00:00
|
|
|
nid = NID_X9_62_prime256v1; /* We only support one curve per algid */
|
2012-04-02 22:00:56 +00:00
|
|
|
break;
|
|
|
|
case 0x14: keydata.key_bits = 0;
|
|
|
|
nid = NID_secp384r1;
|
2010-12-01 20:08:42 +00:00
|
|
|
break;
|
2010-12-06 14:41:15 +00:00
|
|
|
#endif
|
2006-02-14 22:09:10 +00:00
|
|
|
default:
|
2010-12-01 20:08:42 +00:00
|
|
|
fprintf(stderr, "<keyref>:<algid> algid=RSA - 05, 06, 07 for 3072, 1024, 2048;EC - 11, 14 for 256, 384\n");
|
2006-02-14 22:09:10 +00:00
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
2010-09-24 20:37:22 +00:00
|
|
|
keydata.key_algid = buf[1];
|
|
|
|
|
|
|
|
|
|
|
|
r = sc_card_ctl(card, SC_CARDCTL_PIV_GENERATE_KEY, &keydata);
|
2006-02-14 22:09:10 +00:00
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "gen_key failed %d\n", r);
|
|
|
|
return r;
|
|
|
|
}
|
2010-09-24 20:37:22 +00:00
|
|
|
|
2010-12-01 20:08:42 +00:00
|
|
|
evpkey = EVP_PKEY_new();
|
|
|
|
|
2010-09-24 20:37:22 +00:00
|
|
|
if (keydata.key_bits > 0) { /* RSA key */
|
|
|
|
RSA * newkey = NULL;
|
2016-01-06 14:40:59 +00:00
|
|
|
BIGNUM *newkey_n, *newkey_e;
|
2012-04-02 22:00:56 +00:00
|
|
|
|
2010-09-24 20:37:22 +00:00
|
|
|
newkey = RSA_new();
|
2012-04-02 22:00:56 +00:00
|
|
|
if (newkey == NULL) {
|
2010-09-24 20:37:22 +00:00
|
|
|
fprintf(stderr, "gen_key RSA_new failed %d\n",r);
|
|
|
|
return -1;
|
|
|
|
}
|
2016-01-06 14:40:59 +00:00
|
|
|
newkey_n = BN_bin2bn(keydata.pubkey, keydata.pubkey_len, NULL);
|
2010-09-24 20:37:22 +00:00
|
|
|
expl = keydata.exponent;
|
|
|
|
expc[3] = (u8) expl & 0xff;
|
|
|
|
expc[2] = (u8) (expl >>8) & 0xff;
|
|
|
|
expc[1] = (u8) (expl >>16) & 0xff;
|
|
|
|
expc[0] = (u8) (expl >>24) & 0xff;
|
2016-01-06 14:40:59 +00:00
|
|
|
newkey_e = BN_bin2bn(expc, 4, NULL);
|
|
|
|
|
|
|
|
if (RSA_set0_key(newkey, newkey_n, newkey_e, NULL) != 1) {
|
|
|
|
fprintf(stderr, "gen_key unable to set RSA values");
|
|
|
|
return -1;
|
|
|
|
}
|
2012-04-02 22:00:56 +00:00
|
|
|
|
|
|
|
if (verbose)
|
|
|
|
RSA_print_fp(stdout, newkey,0);
|
2010-09-24 20:37:22 +00:00
|
|
|
|
2010-12-01 20:08:42 +00:00
|
|
|
EVP_PKEY_assign_RSA(evpkey, newkey);
|
|
|
|
|
|
|
|
} else { /* EC key */
|
2010-12-06 14:41:15 +00:00
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined(OPENSSL_NO_EC)
|
2010-12-01 20:08:42 +00:00
|
|
|
int i;
|
|
|
|
BIGNUM *x;
|
|
|
|
BIGNUM *y;
|
|
|
|
EC_KEY * eckey = NULL;
|
|
|
|
EC_GROUP * ecgroup = NULL;
|
|
|
|
EC_POINT * ecpoint = NULL;
|
|
|
|
|
|
|
|
ecgroup = EC_GROUP_new_by_curve_name(nid);
|
|
|
|
EC_GROUP_set_asn1_flag(ecgroup, OPENSSL_EC_NAMED_CURVE);
|
|
|
|
ecpoint = EC_POINT_new(ecgroup);
|
2012-04-02 22:00:56 +00:00
|
|
|
|
2010-12-01 20:08:42 +00:00
|
|
|
/* PIV returns 04||x||y and x and y are the same size */
|
|
|
|
i = (keydata.ecpoint_len - 1)/2;
|
2011-02-07 16:23:54 +00:00
|
|
|
x = BN_bin2bn(keydata.ecpoint + 1, i, NULL);
|
|
|
|
y = BN_bin2bn(keydata.ecpoint + 1 + i, i, NULL) ;
|
2010-12-01 20:08:42 +00:00
|
|
|
r = EC_POINT_set_affine_coordinates_GFp(ecgroup, ecpoint, x, y, NULL);
|
|
|
|
eckey = EC_KEY_new();
|
|
|
|
r = EC_KEY_set_group(eckey, ecgroup);
|
|
|
|
r = EC_KEY_set_public_key(eckey, ecpoint);
|
|
|
|
|
|
|
|
if (verbose)
|
|
|
|
EC_KEY_print_fp(stdout, eckey, 0);
|
|
|
|
|
|
|
|
EVP_PKEY_assign_EC_KEY(evpkey, eckey);
|
2010-12-03 15:15:29 +00:00
|
|
|
#else
|
2010-12-03 15:44:29 +00:00
|
|
|
fprintf(stderr, "This build of OpenSSL does not support EC keys\n");
|
2012-04-02 22:00:56 +00:00
|
|
|
r = 1;
|
2010-12-03 15:15:29 +00:00
|
|
|
#endif /* OPENSSL_NO_EC */
|
2006-02-14 22:09:10 +00:00
|
|
|
|
2010-09-24 20:37:22 +00:00
|
|
|
}
|
2010-12-01 20:08:42 +00:00
|
|
|
if (bp)
|
|
|
|
r = i2d_PUBKEY_bio(bp, evpkey);
|
2006-02-14 22:09:10 +00:00
|
|
|
|
2010-12-01 20:08:42 +00:00
|
|
|
if (evpkey)
|
|
|
|
EVP_PKEY_free(evpkey);
|
2006-02-14 22:09:10 +00:00
|
|
|
|
2010-12-01 20:08:42 +00:00
|
|
|
return r;
|
2006-02-14 22:09:10 +00:00
|
|
|
}
|
|
|
|
|
2011-04-11 12:02:47 +00:00
|
|
|
|
2006-02-14 22:09:10 +00:00
|
|
|
static int send_apdu(void)
|
|
|
|
{
|
|
|
|
sc_apdu_t apdu;
|
2011-04-26 07:29:53 +00:00
|
|
|
u8 buf[SC_MAX_APDU_BUFFER_SIZE+3];
|
|
|
|
u8 rbuf[8192];
|
|
|
|
size_t len0, r;
|
2006-02-14 22:09:10 +00:00
|
|
|
int c;
|
|
|
|
|
|
|
|
for (c = 0; c < opt_apdu_count; c++) {
|
|
|
|
len0 = sizeof(buf);
|
|
|
|
sc_hex_to_bin(opt_apdus[c], buf, &len0);
|
2011-04-26 07:29:53 +00:00
|
|
|
|
|
|
|
r = sc_bytes2apdu(card->ctx, buf, len0, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "Invalid APDU: %s\n", sc_strerror(r));
|
2006-02-14 22:09:10 +00:00
|
|
|
return 2;
|
|
|
|
}
|
2011-04-26 07:29:53 +00:00
|
|
|
|
2006-02-14 22:09:10 +00:00
|
|
|
apdu.resp = rbuf;
|
|
|
|
apdu.resplen = sizeof(rbuf);
|
2011-04-26 07:29:53 +00:00
|
|
|
|
2012-07-23 18:38:32 +00:00
|
|
|
printf("Sending: ");
|
|
|
|
for (r = 0; r < len0; r++)
|
|
|
|
printf("%02X ", buf[r]);
|
|
|
|
printf("\n");
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2006-02-14 22:09:10 +00:00
|
|
|
printf("Received (SW1=0x%02X, SW2=0x%02X)%s\n", apdu.sw1, apdu.sw2,
|
|
|
|
apdu.resplen ? ":" : "");
|
|
|
|
if (apdu.resplen)
|
2008-03-06 16:06:59 +00:00
|
|
|
util_hex_dump_asc(stdout, apdu.resp, apdu.resplen, -1);
|
2006-02-14 22:09:10 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void print_serial(sc_card_t *in_card)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
sc_serial_number_t serial;
|
|
|
|
|
|
|
|
r = sc_card_ctl(in_card, SC_CARDCTL_GET_SERIALNR, &serial);
|
2007-06-21 07:07:49 +00:00
|
|
|
if (r < 0)
|
|
|
|
fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_GET_SERIALNR, *) failed %d\n", r);
|
2006-02-14 22:09:10 +00:00
|
|
|
else
|
2008-03-06 16:06:59 +00:00
|
|
|
util_hex_dump_asc(stdout, serial.value, serial.len, -1);
|
2006-02-14 22:09:10 +00:00
|
|
|
}
|
|
|
|
|
2017-08-02 21:12:58 +00:00
|
|
|
int main(int argc, char *argv[])
|
2006-02-14 22:09:10 +00:00
|
|
|
{
|
|
|
|
int err = 0, r, c, long_optind = 0;
|
|
|
|
int do_send_apdu = 0;
|
|
|
|
int do_admin_mode = 0;
|
|
|
|
int do_gen_key = 0;
|
|
|
|
int do_load_cert = 0;
|
2010-02-05 06:16:37 +00:00
|
|
|
int do_load_object = 0;
|
2007-03-10 10:46:32 +00:00
|
|
|
int compress_cert = 0;
|
2006-02-14 22:09:10 +00:00
|
|
|
int do_print_serial = 0;
|
|
|
|
int do_print_name = 0;
|
|
|
|
int action_count = 0;
|
|
|
|
const char *opt_driver = NULL;
|
|
|
|
const char *out_file = NULL;
|
|
|
|
const char *in_file = NULL;
|
|
|
|
const char *cert_id = NULL;
|
2010-02-05 06:16:37 +00:00
|
|
|
const char *object_id = NULL;
|
2006-02-14 22:09:10 +00:00
|
|
|
const char *key_info = NULL;
|
|
|
|
const char *admin_info = NULL;
|
2011-02-09 14:45:51 +00:00
|
|
|
sc_context_param_t ctx_param;
|
|
|
|
|
2006-02-14 22:09:10 +00:00
|
|
|
setbuf(stderr, NULL);
|
|
|
|
setbuf(stdout, NULL);
|
|
|
|
|
|
|
|
while (1) {
|
2010-04-13 11:29:45 +00:00
|
|
|
c = getopt_long(argc, argv, "nA:G:O:Z:C:i:o:fvs:c:w", options, &long_optind);
|
2006-02-14 22:09:10 +00:00
|
|
|
if (c == -1)
|
|
|
|
break;
|
|
|
|
if (c == '?')
|
2012-05-26 16:53:09 +00:00
|
|
|
util_print_usage_and_die(app_name, options, option_help, NULL);
|
2006-02-14 22:09:10 +00:00
|
|
|
switch (c) {
|
2007-03-18 17:55:15 +00:00
|
|
|
case OPT_SERIAL:
|
|
|
|
do_print_serial = 1;
|
|
|
|
action_count++;
|
|
|
|
break;
|
2006-02-14 22:09:10 +00:00
|
|
|
case 's':
|
|
|
|
opt_apdus = (char **) realloc(opt_apdus,
|
|
|
|
(opt_apdu_count + 1) * sizeof(char *));
|
2017-04-20 19:08:49 +00:00
|
|
|
if (!opt_apdus) {
|
|
|
|
err = 1;
|
|
|
|
goto end;
|
|
|
|
}
|
2006-02-14 22:09:10 +00:00
|
|
|
opt_apdus[opt_apdu_count] = optarg;
|
|
|
|
do_send_apdu++;
|
|
|
|
if (opt_apdu_count == 0)
|
|
|
|
action_count++;
|
|
|
|
opt_apdu_count++;
|
|
|
|
break;
|
|
|
|
case 'n':
|
|
|
|
do_print_name = 1;
|
|
|
|
action_count++;
|
|
|
|
break;
|
|
|
|
case 'A':
|
|
|
|
do_admin_mode = 1;
|
|
|
|
admin_info = optarg;
|
|
|
|
action_count++;
|
|
|
|
break;
|
|
|
|
case 'G':
|
|
|
|
do_gen_key = 1;
|
|
|
|
key_info = optarg;
|
|
|
|
action_count++;
|
|
|
|
break;
|
2010-02-05 06:16:37 +00:00
|
|
|
case 'O':
|
|
|
|
do_load_object = 1;
|
|
|
|
object_id = optarg;
|
|
|
|
action_count++;
|
|
|
|
break;
|
2007-03-10 10:46:32 +00:00
|
|
|
case 'Z':
|
|
|
|
compress_cert = 1;
|
2015-02-16 16:11:24 +00:00
|
|
|
/* fall through */
|
2006-02-14 22:09:10 +00:00
|
|
|
case 'C':
|
|
|
|
do_load_cert = 1;
|
|
|
|
cert_id = optarg;
|
|
|
|
action_count++;
|
|
|
|
break;
|
|
|
|
case 'i':
|
|
|
|
in_file = optarg;
|
|
|
|
break;
|
|
|
|
case 'o':
|
|
|
|
out_file = optarg;
|
|
|
|
break;
|
|
|
|
case 'r':
|
2010-01-24 15:29:47 +00:00
|
|
|
opt_reader = optarg;
|
2006-02-14 22:09:10 +00:00
|
|
|
break;
|
|
|
|
case 'v':
|
|
|
|
verbose++;
|
|
|
|
break;
|
|
|
|
case 'c':
|
|
|
|
opt_driver = optarg;
|
|
|
|
break;
|
|
|
|
case 'w':
|
|
|
|
opt_wait = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (action_count == 0)
|
2012-05-26 16:53:09 +00:00
|
|
|
util_print_usage_and_die(app_name, options, option_help, NULL);
|
2006-02-14 22:09:10 +00:00
|
|
|
|
2017-05-07 19:42:32 +00:00
|
|
|
|
|
|
|
//#if (OPENSSL_VERSION_NUMBER >= 0x00907000L && OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
|
|
|
|
// OPENSSL_config(NULL);
|
|
|
|
//#endif
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
|
|
|
|
OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS
|
|
|
|
| OPENSSL_INIT_ADD_ALL_CIPHERS
|
|
|
|
| OPENSSL_INIT_ADD_ALL_DIGESTS,
|
|
|
|
NULL);
|
|
|
|
#else
|
|
|
|
/* OpenSSL magic */
|
2016-01-06 14:40:59 +00:00
|
|
|
OPENSSL_malloc_init();
|
2006-02-14 22:09:10 +00:00
|
|
|
ERR_load_crypto_strings();
|
|
|
|
OpenSSL_add_all_algorithms();
|
|
|
|
|
2017-05-07 19:42:32 +00:00
|
|
|
#endif
|
2006-02-14 22:09:10 +00:00
|
|
|
|
|
|
|
if (out_file) {
|
|
|
|
bp = BIO_new(BIO_s_file());
|
2016-01-06 14:40:59 +00:00
|
|
|
if (!BIO_write_filename(bp, (char *)out_file))
|
|
|
|
goto end;
|
2006-02-14 22:09:10 +00:00
|
|
|
} else {
|
|
|
|
bp = BIO_new(BIO_s_file());
|
|
|
|
BIO_set_fp(bp,stdout,BIO_NOCLOSE);
|
|
|
|
}
|
|
|
|
|
2011-02-09 14:45:51 +00:00
|
|
|
memset(&ctx_param, 0, sizeof(sc_context_param_t));
|
|
|
|
ctx_param.app_name = app_name;
|
|
|
|
|
|
|
|
r = sc_context_create(&ctx, &ctx_param);
|
|
|
|
if (r != SC_SUCCESS) {
|
2006-02-14 22:09:10 +00:00
|
|
|
fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2010-03-15 12:17:13 +00:00
|
|
|
|
2010-12-01 20:08:42 +00:00
|
|
|
/* Only change if not in opensc.conf */
|
2012-04-02 22:00:56 +00:00
|
|
|
if (verbose > 1 && ctx->debug == 0) {
|
2010-10-20 07:53:19 +00:00
|
|
|
ctx->debug = verbose;
|
2011-04-18 10:01:27 +00:00
|
|
|
sc_ctx_log_to_file(ctx, "stderr");
|
2010-10-20 07:53:19 +00:00
|
|
|
}
|
|
|
|
|
2006-02-14 22:09:10 +00:00
|
|
|
if (action_count <= 0)
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
if (opt_driver != NULL) {
|
|
|
|
err = sc_set_card_driver(ctx, opt_driver);
|
|
|
|
if (err) {
|
|
|
|
fprintf(stderr, "Driver '%s' not found!\n", opt_driver);
|
|
|
|
err = 1;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-24 15:29:47 +00:00
|
|
|
err = util_connect_card(ctx, &card, opt_reader, opt_wait, verbose);
|
2006-02-14 22:09:10 +00:00
|
|
|
if (err)
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
if (do_admin_mode) {
|
|
|
|
if ((err = admin_mode(admin_info)))
|
|
|
|
goto end;
|
|
|
|
action_count--;
|
|
|
|
}
|
|
|
|
if (do_send_apdu) { /* can use pin before load cert for a beta card */
|
|
|
|
if ((err = send_apdu()))
|
|
|
|
goto end;
|
|
|
|
action_count--;
|
|
|
|
}
|
|
|
|
if (do_gen_key) {
|
|
|
|
if ((err = gen_key(key_info)))
|
|
|
|
goto end;
|
|
|
|
action_count--;
|
|
|
|
}
|
2010-02-05 06:16:37 +00:00
|
|
|
if (do_load_object) {
|
|
|
|
if ((err = load_object(object_id, in_file)))
|
|
|
|
goto end;
|
|
|
|
action_count--;
|
|
|
|
}
|
2006-02-14 22:09:10 +00:00
|
|
|
if (do_load_cert) {
|
2007-03-10 10:46:32 +00:00
|
|
|
if ((err = load_cert(cert_id, in_file, compress_cert)))
|
2006-02-14 22:09:10 +00:00
|
|
|
goto end;
|
|
|
|
action_count--;
|
|
|
|
}
|
|
|
|
if (do_print_serial) {
|
|
|
|
if (verbose)
|
|
|
|
printf("Card serial number:");
|
|
|
|
print_serial(card);
|
|
|
|
action_count--;
|
|
|
|
}
|
|
|
|
if (do_print_name) {
|
|
|
|
if (verbose)
|
|
|
|
printf("Card name: ");
|
|
|
|
printf("%s\n", card->name);
|
|
|
|
action_count--;
|
|
|
|
}
|
|
|
|
end:
|
2012-04-02 22:00:56 +00:00
|
|
|
if (bp)
|
2006-02-14 22:09:10 +00:00
|
|
|
BIO_free(bp);
|
|
|
|
if (card) {
|
|
|
|
sc_unlock(card);
|
2010-01-24 15:29:47 +00:00
|
|
|
sc_disconnect_card(card);
|
2006-02-14 22:09:10 +00:00
|
|
|
}
|
|
|
|
if (ctx)
|
|
|
|
sc_release_context(ctx);
|
2010-12-01 20:08:42 +00:00
|
|
|
|
|
|
|
ERR_print_errors_fp(stderr);
|
2006-02-14 22:09:10 +00:00
|
|
|
return err;
|
|
|
|
}
|