Merge branch 'master' into attestation2

This commit is contained in:
Klas Lindfors
2016-03-10 15:34:25 +01:00
32 changed files with 2405 additions and 689 deletions
+4 -5
View File
@@ -33,7 +33,7 @@ AM_CPPFLAGS += -I$(top_srcdir)/lib -I$(top_builddir)/lib
bin_PROGRAMS = yubico-piv-tool
yubico_piv_tool_SOURCES = yubico-piv-tool.c yubico-piv-tool.h2m
yubico_piv_tool_LDADD = $(OPENSSL_LIBS) ../lib/libykpiv.la
yubico_piv_tool_LDADD = $(OPENSSL_LIBS) $(top_builddir)/lib/libykpiv.la
yubico_piv_tool_LDADD += libpiv_cmd.la libpiv_util.la
noinst_LTLIBRARIES = libpiv_cmd.la libpiv_util.la
@@ -41,9 +41,9 @@ libpiv_cmd_la_SOURCES = cmdline.ggo cmdline.c cmdline.h
libpiv_cmd_la_CFLAGS =
libpiv_util_la_SOURCES = util.c util.h
libpiv_util_la_LIBADD = $(OPENSSL_LIBS)
libpiv_util_la_LIBADD = $(top_builddir)/lib/libykpiv.la $(OPENSSL_LIBS)
cmdline.c cmdline.h: cmdline.ggo Makefile.am
cmdline.c cmdline.h: cmdline.ggo Makefile.am $(top_srcdir)/configure.ac
$(GENGETOPT) --input $^
BUILT_SOURCES = cmdline.c cmdline.h
@@ -54,8 +54,7 @@ MAINTAINERCLEANFILES = $(BUILT_SOURCES)
dist_man_MANS = yubico-piv-tool.1
MAINTAINERCLEANFILES += $(dist_man_MANS)
yubico-piv-tool.1: $(yubico_piv_tool_SOURCES) \
$(top_srcdir)/configure.ac
yubico-piv-tool.1: $(yubico_piv_tool_SOURCES) $(libpiv_cmd_la_SOURCES)
$(HELP2MAN) --no-info \
--name="Yubico PIV tool" \
--include=$(srcdir)/yubico-piv-tool.h2m \
+6 -1
View File
@@ -32,7 +32,8 @@ option "action" a "Action to take" values="version","generate","set-mgm-key",
"reset","pin-retries","import-key","import-certificate","set-chuid",
"request-certificate","verify-pin","change-pin","change-puk","unblock-pin",
"selfsign-certificate","delete-certificate","read-certificate","status",
"test-signature","test-decipher","list-readers","attest" enum multiple
"test-signature","test-decipher","list-readers","set-ccc","write-object",
"read-object","attest" enum multiple
text "
Multiple actions may be given at once and will be executed in order
for example --action=verify-pin --action=request-certificate\n"
@@ -56,8 +57,12 @@ option "subject" S "The subject to use for certificate request" string optional
text "
The subject must be written as:
/CN=host.example.com/OU=test/O=example.com/\n"
option "serial" - "Serial number of the self-signed certificate" int optional default="1"
option "valid-days" - "Time (in days) until the self-signed certificate expires" int optional default="365"
option "pin" P "Pin/puk code for verification" string optional
option "new-pin" N "New pin/puk code for changing" string optional dependon="pin"
option "pin-policy" - "Set pin policy for action generate or import-key" values="never","once","always" enum optional
option "touch-policy" - "Set touch policy for action generate, import-key or set-mgm-key" values="never","always" enum optional
option "id" - "Id of object for write/read object" int optional
option "format" f "Format of data for write/read object" values="hex","base64","binary" enum optional default="hex"
option "sign" - "Sign data" flag off hidden
+2 -1
View File
@@ -37,8 +37,9 @@ AM_CPPFLAGS += $(OPENSSL_CFLAGS)
AM_LDFLAGS = -no-install
parse_name_LDADD = ../libpiv_util.la $(OPENSSL_LIBS)
test_inout_LDADD = ../libpiv_util.la
check_PROGRAMS = parse_name
check_PROGRAMS = parse_name test_inout
TESTS = basic.sh $(check_PROGRAMS)
if ENABLE_COV
+63
View File
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2015 Yubico AB
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include "util.h"
#ifdef _WIN32
#define pipe(fds) _pipe(fds,4096, 0)
#endif
static void test_inout(enum enum_format format) {
const unsigned char buf[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
unsigned char buf2[sizeof(buf)];
int pipefd[2];
FILE *tmp1, *tmp2;
assert(pipe(pipefd) == 0);
tmp1 = fdopen(pipefd[1], "w");
dump_data(buf, sizeof(buf), tmp1, false, format);
fclose(tmp1);
tmp2 = fdopen(pipefd[0], "r");
read_data(buf2, sizeof(buf2), tmp2, format);
assert(memcmp(buf, buf2, sizeof(buf)) == 0);
fclose(tmp2);
}
int main(void) {
test_inout(format_arg_base64);
test_inout(format_arg_hex);
test_inout(format_arg_binary);
exit(0);
}
+143 -14
View File
@@ -148,12 +148,80 @@ parse_err:
return NULL;
}
void dump_hex(const unsigned char *buf, unsigned int len, FILE *output, bool space) {
unsigned int i;
for (i = 0; i < len; i++) {
fprintf(output, "%02x%s", buf[i], space == true ? " " : "");
size_t read_data(unsigned char *buf, size_t len, FILE* input, enum enum_format format) {
char raw_buf[3072 * 2];
size_t raw_len = sizeof(raw_buf);
raw_len = fread(raw_buf, 1, raw_len, input);
switch(format) {
case format_arg_hex:
if(raw_buf[raw_len - 1] == '\n') {
raw_len -= 1;
}
if(ykpiv_hex_decode(raw_buf, raw_len, buf, &len) != YKPIV_OK) {
return 0;
}
return len;
case format_arg_base64:
{
int read;
BIO *b64 = BIO_new(BIO_f_base64());
BIO *bio = BIO_new_mem_buf(raw_buf, raw_len);
BIO_push(b64, bio);
read = BIO_read(b64, buf, len);
BIO_free_all(b64);
if(read <= 0) {
return 0;
} else {
return (size_t)read;
}
}
break;
case format_arg_binary:
if(raw_len > len) {
return 0;
}
memcpy(buf, raw_buf, raw_len);
return raw_len;
case format__NULL:
default:
return 0;
}
}
void dump_data(const unsigned char *buf, unsigned int len, FILE *output, bool space, enum enum_format format) {
switch(format) {
case format_arg_hex:
{
char tmp[3072 * 3 + 1];
unsigned int i;
unsigned int step = 2;
if(space) step += 1;
if(len > 3072) {
return;
}
for (i = 0; i < len; i++) {
sprintf(tmp + i * step, "%02x%s", buf[i], space == true ? " " : "");
}
fprintf(output, "%s\n", tmp);
}
return;
case format_arg_base64:
{
BIO *b64 = BIO_new(BIO_f_base64());
BIO *bio = BIO_new_fp(output, BIO_NOCLOSE);
BIO_push(b64, bio);
BIO_write(b64, buf, (int)len);
BIO_flush(b64);
BIO_free_all(b64);
}
return;
case format_arg_binary:
fwrite(buf, 1, len, output);
return;
case format__NULL:
default:
return;
}
fprintf(output, "\n");
}
int get_length(const unsigned char *buffer, int *len) {
@@ -276,33 +344,94 @@ int key_to_object_id(int key) {
int object;
switch(key) {
case 0x9a:
case YKPIV_KEY_AUTHENTICATION:
object = YKPIV_OBJ_AUTHENTICATION;
break;
case 0x9c:
case YKPIV_KEY_CARDMGM:
object = YKPIV_OBJ_SIGNATURE;
break;
case 0x9d:
case YKPIV_KEY_KEYMGM:
object = YKPIV_OBJ_KEY_MANAGEMENT;
break;
case 0x9e:
case YKPIV_KEY_CARDAUTH:
object = YKPIV_OBJ_CARD_AUTH;
break;
case YKPIV_KEY_RETIRED1:
object = YKPIV_OBJ_RETIRED1;
break;
case YKPIV_KEY_RETIRED2:
object = YKPIV_OBJ_RETIRED2;
break;
case YKPIV_KEY_RETIRED3:
object = YKPIV_OBJ_RETIRED3;
break;
case YKPIV_KEY_RETIRED4:
object = YKPIV_OBJ_RETIRED4;
break;
case YKPIV_KEY_RETIRED5:
object = YKPIV_OBJ_RETIRED5;
break;
case YKPIV_KEY_RETIRED6:
object = YKPIV_OBJ_RETIRED6;
break;
case YKPIV_KEY_RETIRED7:
object = YKPIV_OBJ_RETIRED7;
break;
case YKPIV_KEY_RETIRED8:
object = YKPIV_OBJ_RETIRED8;
break;
case YKPIV_KEY_RETIRED9:
object = YKPIV_OBJ_RETIRED9;
break;
case YKPIV_KEY_RETIRED10:
object = YKPIV_OBJ_RETIRED10;
break;
case YKPIV_KEY_RETIRED11:
object = YKPIV_OBJ_RETIRED11;
break;
case YKPIV_KEY_RETIRED12:
object = YKPIV_OBJ_RETIRED12;
break;
case YKPIV_KEY_RETIRED13:
object = YKPIV_OBJ_RETIRED13;
break;
case YKPIV_KEY_RETIRED14:
object = YKPIV_OBJ_RETIRED14;
break;
case YKPIV_KEY_RETIRED15:
object = YKPIV_OBJ_RETIRED15;
break;
case YKPIV_KEY_RETIRED16:
object = YKPIV_OBJ_RETIRED16;
break;
case YKPIV_KEY_RETIRED17:
object = YKPIV_OBJ_RETIRED17;
break;
case YKPIV_KEY_RETIRED18:
object = YKPIV_OBJ_RETIRED18;
break;
case YKPIV_KEY_RETIRED19:
object = YKPIV_OBJ_RETIRED19;
break;
case YKPIV_KEY_RETIRED20:
object = YKPIV_OBJ_RETIRED20;
break;
default:
object = 0;
}
return object;
}
bool set_component_with_len(unsigned char **in_ptr, const BIGNUM *bn, int element_len) {
bool set_component(unsigned char *in_ptr, const BIGNUM *bn, int element_len) {
int real_len = BN_num_bytes(bn);
*in_ptr += set_length(*in_ptr, element_len);
if(real_len > element_len) {
return false;
}
memset(*in_ptr, 0, (size_t)(element_len - real_len));
*in_ptr += element_len - real_len;
*in_ptr += BN_bn2bin(bn, *in_ptr);
memset(in_ptr, 0, (size_t)(element_len - real_len));
in_ptr += element_len - real_len;
BN_bn2bin(bn, in_ptr);
return true;
}
+5 -2
View File
@@ -31,6 +31,8 @@
#ifndef YUBICO_PIV_TOOL_INTERNAL_H
#define YUBICO_PIV_TOOL_INTERNAL_H
#include <stdbool.h>
#include <openssl/x509.h>
#include "cmdline.h"
@@ -38,7 +40,8 @@
#define INPUT 1
#define OUTPUT 2
void dump_hex(unsigned const char*, unsigned int, FILE*, bool);
size_t read_data(unsigned char*, size_t, FILE*, enum enum_format);
void dump_data(unsigned const char*, unsigned int, FILE*, bool, enum enum_format);
int set_length(unsigned char*, int);
int get_length(const unsigned char*, int*);
X509_NAME *parse_name(const char*);
@@ -46,7 +49,7 @@ unsigned char get_algorithm(EVP_PKEY*);
FILE *open_file(const char*, int);
int get_object_id(enum enum_slot slot);
int key_to_object_id(int key);
bool set_component_with_len(unsigned char**, const BIGNUM*, int);
bool set_component(unsigned char *in_ptr, const BIGNUM *bn, int element_len);
bool prepare_rsa_signature(const unsigned char*, unsigned int, unsigned char*,
unsigned int*, int);
bool read_pw(const char*, char*, size_t, int);
+353 -202
View File
@@ -5,15 +5,15 @@
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
*
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@@ -63,6 +63,17 @@ unsigned const char chuid_tmpl[] = {
};
#define CHUID_GUID_OFFS 29
unsigned const char ccc_tmpl[] = {
0xf0, 0x15, 0xa0, 0x00, 0x00, 0x01, 0x16, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf1, 0x01, 0x21,
0xf2, 0x01, 0x21, 0xf3, 0x00, 0xf4, 0x01, 0x00, 0xf5, 0x01, 0x10, 0xf6, 0x00,
0xf7, 0x00, 0xfa, 0x00, 0xfb, 0x00, 0xfc, 0x00, 0xfd, 0x00, 0xfe, 0x00
};
#define CCC_ID_OFFS 9
#define CHUID 0
#define CCC 1
#define MAX_OID_LEN 19
#define KEY_LEN 24
@@ -71,7 +82,7 @@ static void print_version(ykpiv_state *state, const char *output_file_name) {
char version[7];
FILE *output_file = open_file(output_file_name, OUTPUT);
if(!output_file) {
fprintf(stderr, "Failed opening output_file_name\n");
return;
}
if(ykpiv_get_version(state, version, sizeof(version)) == YKPIV_OK) {
@@ -316,14 +327,15 @@ static bool set_pin_retries(ykpiv_state *state, int pin_retries, int puk_retries
}
static bool import_key(ykpiv_state *state, enum enum_key_format key_format,
const char *input_file_name, const char *slot, char *password,
enum enum_pin_policy pin_policy, enum enum_touch_policy touch_policy) {
const char *input_file_name, const char *slot, char *password,
enum enum_pin_policy pin_policy, enum enum_touch_policy touch_policy) {
int key = 0;
FILE *input_file = NULL;
EVP_PKEY *private_key = NULL;
PKCS12 *p12 = NULL;
X509 *cert = NULL;
bool ret = false;
ykpiv_rc rc = YKPIV_GENERIC_ERROR;
sscanf(slot, "%2x", &key);
@@ -360,119 +372,123 @@ static bool import_key(ykpiv_state *state, enum enum_key_format key_format,
{
unsigned char algorithm = get_algorithm(private_key);
unsigned char pp = YKPIV_PINPOLICY_DEFAULT;
unsigned char tp = YKPIV_TOUCHPOLICY_DEFAULT;
if(algorithm == 0) {
goto import_out;
}
{
unsigned char data[0xff];
unsigned long recv_len = sizeof(data);
unsigned char in_data[1024];
unsigned char *in_ptr = in_data;
unsigned char templ[] = {0, YKPIV_INS_IMPORT_KEY, algorithm, key};
int sw;
if(YKPIV_IS_RSA(algorithm)) {
RSA *rsa_private_key = EVP_PKEY_get1_RSA(private_key);
unsigned char e[4];
unsigned char *e_ptr = e;
int element_len = 128;
if(algorithm == YKPIV_ALGO_RSA1024) {
element_len = 64;
}
if((set_component_with_len(&e_ptr, rsa_private_key->e, 3) == false) ||
!(e[1] == 0x01 && e[2] == 0x00 && e[3] == 0x01)) {
fprintf(stderr, "Invalid public exponent for import (only 0x10001 supported)\n");
goto import_out;
}
if(pin_policy != pin_policy__NULL) {
pp = get_pin_policy(pin_policy);
}
*in_ptr++ = 0x01;
if(set_component_with_len(&in_ptr, rsa_private_key->p, element_len) == false) {
fprintf(stderr, "Failed setting p component.\n");
goto import_out;
}
if(touch_policy != touch_policy__NULL) {
tp = get_touch_policy(touch_policy);
}
*in_ptr++ = 0x02;
if(set_component_with_len(&in_ptr, rsa_private_key->q, element_len) == false) {
fprintf(stderr, "Failed setting q component.\n");
goto import_out;
}
if(YKPIV_IS_RSA(algorithm)) {
RSA *rsa_private_key = EVP_PKEY_get1_RSA(private_key);
unsigned char e[4];
unsigned char p[128];
unsigned char q[128];
unsigned char dmp1[128];
unsigned char dmq1[128];
unsigned char iqmp[128];
*in_ptr++ = 0x03;
if(set_component_with_len(&in_ptr, rsa_private_key->dmp1, element_len) == false) {
fprintf(stderr, "Failed setting dmp1 component.\n");
goto import_out;
}
*in_ptr++ = 0x04;
if(set_component_with_len(&in_ptr, rsa_private_key->dmq1, element_len) == false) {
fprintf(stderr, "Failed setting dmq1 component.\n");
goto import_out;
}
*in_ptr++ = 0x05;
if(set_component_with_len(&in_ptr, rsa_private_key->iqmp, element_len) == false) {
fprintf(stderr, "Failed setting iqmp component.\n");
goto import_out;
}
} else if(YKPIV_IS_EC(algorithm)) {
EC_KEY *ec = EVP_PKEY_get1_EC_KEY(private_key);
const BIGNUM *s = EC_KEY_get0_private_key(ec);
int element_len = 32;
if(algorithm == YKPIV_ALGO_ECCP384) {
element_len = 48;
}
*in_ptr++ = 0x06;
if(set_component_with_len(&in_ptr, s, element_len) == false) {
fprintf(stderr, "Failed setting ec private key.\n");
goto import_out;
}
int element_len = 128;
if(algorithm == YKPIV_ALGO_RSA1024) {
element_len = 64;
}
if(pin_policy != pin_policy__NULL) {
*in_ptr++ = YKPIV_PINPOLICY_TAG;
*in_ptr++ = 1;
*in_ptr++ = get_pin_policy(pin_policy);
}
if(touch_policy != touch_policy__NULL) {
*in_ptr++ = YKPIV_TOUCHPOLICY_TAG;
*in_ptr++ = 1;
*in_ptr++ = get_touch_policy(touch_policy);
if((set_component(e, rsa_private_key->e, 3) == false) ||
!(e[0] == 0x01 && e[1] == 0x00 && e[2] == 0x01)) {
fprintf(stderr, "Invalid public exponent for import (only 0x10001 supported)\n");
goto import_out;
}
if(ykpiv_transfer_data(state, templ, in_data, in_ptr - in_data, data,
&recv_len, &sw) != YKPIV_OK) {
return false;
} else if(sw == 0x6a80) {
fprintf(stderr, "Failed import.");
if(pin_policy != pin_policy__NULL) {
fprintf(stderr, "Maybe pin-policy is not supported on this key?\n");
} else if(touch_policy != touch_policy__NULL) {
fprintf(stderr, "Maybe touch-policy is not supported on this key?\n");
} else {
fprintf(stderr, "Maybe algorithm is not supported on this key?\n");
}
} else if(sw != 0x9000) {
fprintf(stderr, "Failed import command with code %x.\n", sw);
} else {
ret = true;
if(set_component(p, rsa_private_key->p, element_len) == false) {
fprintf(stderr, "Failed setting p component.\n");
goto import_out;
}
if(set_component(q, rsa_private_key->q, element_len) == false) {
fprintf(stderr, "Failed setting q component.\n");
goto import_out;
}
if(set_component(dmp1, rsa_private_key->dmp1, element_len) == false) {
fprintf(stderr, "Failed setting dmp1 component.\n");
goto import_out;
}
if(set_component(dmq1, rsa_private_key->dmq1, element_len) == false) {
fprintf(stderr, "Failed setting dmq1 component.\n");
goto import_out;
}
if(set_component(iqmp, rsa_private_key->iqmp, element_len) == false) {
fprintf(stderr, "Failed setting iqmp component.\n");
goto import_out;
}
rc = ykpiv_import_private_key(state, key, algorithm,
p, element_len,
q, element_len,
dmp1, element_len,
dmq1, element_len,
iqmp, element_len,
NULL, 0,
pp, tp);
}
else if(YKPIV_IS_EC(algorithm)) {
EC_KEY *ec = EVP_PKEY_get1_EC_KEY(private_key);
const BIGNUM *s = EC_KEY_get0_private_key(ec);
unsigned char s_ptr[48];
int element_len = 32;
if(algorithm == YKPIV_ALGO_ECCP384) {
element_len = 48;
}
if(set_component(s_ptr, s, element_len) == false) {
fprintf(stderr, "Failed setting ec private key.\n");
goto import_out;
}
rc = ykpiv_import_private_key(state, key, algorithm,
NULL, 0,
NULL, 0,
NULL, 0,
NULL, 0,
NULL, 0,
s_ptr, element_len,
pp, tp);
}
ret = true;
if(rc != YKPIV_OK) {
ret = false;
}
}
import_out:
if(private_key) {
EVP_PKEY_free(private_key);
}
if(p12) {
PKCS12_free(p12);
}
if(cert) {
X509_free(cert);
}
if(input_file != stdin) {
fclose(input_file);
}
return ret;
}
@@ -536,15 +552,16 @@ static bool import_cert(ykpiv_state *state, enum enum_key_format cert_format,
}
{
unsigned char certdata[2100];
unsigned char certdata[3072];
unsigned char *certptr = certdata;
int object = get_object_id(slot);
ykpiv_rc res;
if(cert_len > 2048) {
fprintf(stderr, "Certificate to large, maximum 2048 bytes (was %d bytes).\n", cert_len);
if(4 + cert_len + 5 > 3072) { /* 4 is prefix size, 5 is postfix size */
fprintf(stderr, "Certificate is to large to fit in buffer.\n");
goto import_cert_out;
}
*certptr++ = 0x70;
certptr += set_length(certptr, cert_len);
if (compress) {
@@ -587,20 +604,36 @@ import_cert_out:
return ret;
}
static bool set_chuid(ykpiv_state *state, int verbose) {
unsigned char chuid[sizeof(chuid_tmpl)];
static bool set_dataobject(ykpiv_state *state, int verbose, int type) {
unsigned char obj[1024];
ykpiv_rc res;
size_t offs, rand_len, len;
const unsigned char *tmpl;
int id;
memcpy(chuid, chuid_tmpl, sizeof(chuid));
if(RAND_pseudo_bytes(chuid + CHUID_GUID_OFFS, 0x10) == -1) {
if(type == CHUID) {
offs = CHUID_GUID_OFFS;
len = sizeof(chuid_tmpl);
rand_len = 0x10;
tmpl = chuid_tmpl;
id = YKPIV_OBJ_CHUID;
} else {
offs = CCC_ID_OFFS;
rand_len = 0xe;
len = sizeof(ccc_tmpl);
tmpl = ccc_tmpl;
id = YKPIV_OBJ_CAPABILITY;
}
memcpy(obj, tmpl, len);
if(RAND_pseudo_bytes(obj + offs, rand_len) == -1) {
fprintf(stderr, "error: no randomness.\n");
return false;
}
if(verbose) {
fprintf(stderr, "Setting the CHUID to: ");
dump_hex(chuid, sizeof(chuid), stderr, true);
fprintf(stderr, "Setting the %s to: ", type == CHUID ? "CHUID" : "CCC");
dump_data(obj, len, stderr, true, format_arg_hex);
}
if((res = ykpiv_save_object(state, YKPIV_OBJ_CHUID, chuid, sizeof(chuid))) != YKPIV_OK) {
if((res = ykpiv_save_object(state, id, obj, len)) != YKPIV_OK) {
fprintf(stderr, "Failed communicating with device: %s\n", ykpiv_strerror(res));
return false;
}
@@ -689,7 +722,7 @@ static bool request_certificate(ykpiv_state *state, enum enum_key_format key_for
memcpy(digest, oid, oid_len);
/* XXX: this should probably use X509_REQ_digest() but that's buggy */
if(!ASN1_item_digest(ASN1_ITEM_rptr(X509_REQ_INFO), md, req->req_info,
digest + oid_len, &digest_len)) {
digest + oid_len, &digest_len)) {
fprintf(stderr, "Failed doing digest of request.\n");
goto request_out;
}
@@ -748,7 +781,7 @@ request_out:
static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_format,
const char *input_file_name, const char *slot, char *subject, enum enum_hash hash,
const char *output_file_name) {
int serial, int validDays, const char *output_file_name) {
FILE *input_file = NULL;
FILE *output_file = NULL;
bool ret = false;
@@ -814,7 +847,7 @@ static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_fo
fprintf(stderr, "Failed to set the certificate public key.\n");
goto selfsign_out;
}
if(!ASN1_INTEGER_set(X509_get_serialNumber(x509), 1)) {
if(!ASN1_INTEGER_set(X509_get_serialNumber(x509), serial)) {
fprintf(stderr, "Failed to set certificate serial.\n");
goto selfsign_out;
}
@@ -822,7 +855,7 @@ static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_fo
fprintf(stderr, "Failed to set certificate notBefore.\n");
goto selfsign_out;
}
if(!X509_gmtime_adj(X509_get_notAfter(x509), 31536000L)) {
if(!X509_gmtime_adj(X509_get_notAfter(x509), 60L * 60L * 24L * validDays)) {
fprintf(stderr, "Failed to set certificate notAfter.\n");
goto selfsign_out;
}
@@ -856,7 +889,7 @@ static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_fo
memcpy(digest, oid, oid_len);
/* XXX: this should probably use X509_digest() but that looks buggy */
if(!ASN1_item_digest(ASN1_ITEM_rptr(X509_CINF), md, x509->cert_info,
digest + oid_len, &digest_len)) {
digest + oid_len, &digest_len)) {
fprintf(stderr, "Failed doing digest of certificate.\n");
goto selfsign_out;
}
@@ -936,17 +969,16 @@ static bool verify_pin(ykpiv_state *state, const char *pin) {
* since they're very similar in what data they use. */
static bool change_pin(ykpiv_state *state, enum enum_action action, const char *pin,
const char *new_pin) {
unsigned char templ[] = {0, YKPIV_INS_CHANGE_REFERENCE, 0, 0x80};
unsigned char indata[0x10];
unsigned char data[0xff];
unsigned long recv_len = sizeof(data);
char pinbuf[9] = {0};
char new_pinbuf[9] = {0};
const char *name = action == action_arg_changeMINUS_pin ? "pin" : "puk";
const char *new_name = action == action_arg_changeMINUS_puk ? "new puk" : "new pin";
int sw;
int (*op)(ykpiv_state *state, const char * puk, size_t puk_len,
const char * new_pin, size_t new_pin_len, int *tries) = ykpiv_change_pin;
size_t pin_len;
size_t new_len;
int tries;
ykpiv_rc res;
if(!pin) {
if (!read_pw(name, pinbuf, sizeof(pinbuf), false)) {
@@ -969,38 +1001,33 @@ static bool change_pin(ykpiv_state *state, enum enum_action action, const char *
}
if(action == action_arg_unblockMINUS_pin) {
templ[1] = YKPIV_INS_RESET_RETRY;
op = ykpiv_unblock_pin;
}
else if(action == action_arg_changeMINUS_puk) {
templ[3] = 0x81;
op = ykpiv_change_puk;
}
memcpy(indata, pin, pin_len);
if(pin_len < 8) {
memset(indata + pin_len, 0xff, 8 - pin_len);
}
memcpy(indata + 8, new_pin, new_len);
if(new_len < 8) {
memset(indata + 8 + new_len, 0xff, 16 - new_len);
}
if(ykpiv_transfer_data(state, templ, indata, sizeof(indata), data, &recv_len, &sw) != YKPIV_OK) {
return false;
} else if(sw != 0x9000) {
if((sw >> 8) == 0x63) {
int tries = sw & 0xf;
res = op(state, pin, pin_len, new_pin, new_len, &tries);
switch (res) {
case YKPIV_OK:
return true;
case YKPIV_WRONG_PIN:
fprintf(stderr, "Failed verifying %s code, now %d tries left before blocked.\n",
name, tries);
} else if(sw == 0x6983) {
name, tries);
return false;
case YKPIV_PIN_LOCKED:
if(action == action_arg_changeMINUS_pin) {
fprintf(stderr, "The pin code is blocked, use the unblock-pin action to unblock it.\n");
} else {
fprintf(stderr, "The puk code is blocked, you will have to reinitialize the application.\n");
}
} else {
fprintf(stderr, "Failed changing/unblocking code, error: %x\n", sw);
}
return false;
return false;
default:
fprintf(stderr, "Failed changing/unblocking code, error: %x\n", res);
return false;
}
return true;
}
static bool delete_certificate(ykpiv_state *state, enum enum_slot slot) {
@@ -1019,7 +1046,7 @@ static bool read_certificate(ykpiv_state *state, enum enum_slot slot,
enum enum_key_format key_format, const char *output_file_name) {
FILE *output_file;
int object = get_object_id(slot);
unsigned char data[2048];
unsigned char data[3072];
const unsigned char *ptr = data;
unsigned long len = sizeof(data);
int cert_len;
@@ -1100,6 +1127,9 @@ static bool sign_file(ykpiv_state *state, const char *input, const char *output,
output_file = open_file(output, OUTPUT);
if(!output_file) {
if(input_file && input_file != stdin) {
fclose(input_file);
}
return false;
}
@@ -1127,7 +1157,7 @@ static bool sign_file(ykpiv_state *state, const char *input, const char *output,
if(verbosity) {
fprintf(stderr, "file hashed as: ");
dump_hex(hashed, hash_len, stderr, true);
dump_data(hashed, hash_len, stderr, true, format_arg_hex);
}
EVP_MD_CTX_destroy(mdctx);
}
@@ -1146,7 +1176,7 @@ static bool sign_file(ykpiv_state *state, const char *input, const char *output,
if(verbosity) {
fprintf(stderr, "file signed as: ");
dump_hex(buf, len, stderr, true);
dump_data(buf, len, stderr, true, format_arg_hex);
}
fwrite(buf, 1, len, output_file);
ret = true;
@@ -1167,7 +1197,8 @@ out:
static void print_cert_info(ykpiv_state *state, enum enum_slot slot, const EVP_MD *md,
FILE *output) {
int object = get_object_id(slot);
unsigned char data[2048];
int slot_name;
unsigned char data[3072];
const unsigned char *ptr = data;
unsigned long len = sizeof(data);
int cert_len;
@@ -1176,10 +1207,18 @@ static void print_cert_info(ykpiv_state *state, enum enum_slot slot, const EVP_M
BIO *bio = NULL;
if(ykpiv_fetch_object(state, object, data, &len) != YKPIV_OK) {
fprintf(output, "No data available.\n");
return;
}
if (slot == slot_arg_9a)
slot_name = 0x9a;
else if (slot >= slot_arg_9c && slot <= slot_arg_9e)
slot_name = 0x9b + slot;
else
slot_name = 0x82 + (slot - slot_arg_82);
fprintf(output, "Slot %x:\t", slot_name);
if(*ptr++ == 0x70) {
unsigned int md_len = sizeof(data);
ASN1_TIME *not_before, *not_after;
@@ -1237,7 +1276,7 @@ static void print_cert_info(ykpiv_state *state, enum enum_slot slot, const EVP_M
fprintf(output, "\n");
X509_digest(x509, md, data, &md_len);
fprintf(output, "\tFingerprint:\t");
dump_hex(data, md_len, output, false);
dump_data(data, md_len, output, false, format_arg_hex);
bio = BIO_new_fp(output, BIO_NOCLOSE | BIO_FP_TEXT);
not_before = X509_get_notBefore(x509);
@@ -1266,10 +1305,12 @@ cert_out:
}
static bool status(ykpiv_state *state, enum enum_hash hash,
const char *output_file_name) {
enum enum_slot slot,
const char *output_file_name) {
const EVP_MD *md;
unsigned char chuid[2048];
long unsigned len = sizeof(chuid);
unsigned char buf[3072];
long unsigned len = sizeof(buf);
int i;
FILE *output_file = open_file(output_file_name, OUTPUT);
if(!output_file) {
return false;
@@ -1281,20 +1322,26 @@ static bool status(ykpiv_state *state, enum enum_hash hash,
}
fprintf(output_file, "CHUID:\t");
if(ykpiv_fetch_object(state, YKPIV_OBJ_CHUID, chuid, &len) != YKPIV_OK) {
if(ykpiv_fetch_object(state, YKPIV_OBJ_CHUID, buf, &len) != YKPIV_OK) {
fprintf(output_file, "No data available\n");
} else {
dump_hex(chuid, len, output_file, false);
dump_data(buf, len, output_file, false, format_arg_hex);
}
fprintf(output_file, "Slot 9a:\t");
print_cert_info(state, slot_arg_9a, md, output_file);
fprintf(output_file, "Slot 9c:\t");
print_cert_info(state, slot_arg_9c, md, output_file);
fprintf(output_file, "Slot 9d:\t");
print_cert_info(state, slot_arg_9d, md, output_file);
fprintf(output_file, "Slot 9e:\t");
print_cert_info(state, slot_arg_9e, md, output_file);
len = sizeof(buf);
fprintf(output_file, "CCC:\t");
if(ykpiv_fetch_object(state, YKPIV_OBJ_CAPABILITY, buf, &len) != YKPIV_OK) {
fprintf(output_file, "No data available\n");
} else {
dump_data(buf, len, output_file, false, format_arg_hex);
}
if (slot == slot__NULL)
for (i = 0; i < 24; i++) {
print_cert_info(state, i, md, output_file);
}
else
print_cert_info(state, slot, md, output_file);
{
int tries;
@@ -1360,7 +1407,7 @@ static bool test_signature(ykpiv_state *state, enum enum_slot slot,
EVP_DigestFinal_ex(mdctx, data, &data_len);
if(verbose) {
fprintf(stderr, "Test data hashes as: ");
dump_hex(data, data_len, stderr, true);
dump_data(data, data_len, stderr, true, format_arg_hex);
}
}
@@ -1516,9 +1563,9 @@ static bool test_decipher(ykpiv_state *state, enum enum_slot slot,
if(len == sizeof(secret)) {
if(verbose) {
fprintf(stderr, "Generated nonce: ");
dump_hex(secret, sizeof(secret), stderr, true);
dump_data(secret, sizeof(secret), stderr, true, format_arg_hex);
fprintf(stderr, "Decrypted nonce: ");
dump_hex(secret2, sizeof(secret2), stderr, true);
dump_data(secret2, sizeof(secret2), stderr, true, format_arg_hex);
}
if(memcmp(secret, secret2, sizeof(secret)) == 0) {
fprintf(stderr, "Successfully performed RSA decryption!\n");
@@ -1558,9 +1605,9 @@ static bool test_decipher(ykpiv_state *state, enum enum_slot slot,
}
if(verbose) {
fprintf(stderr, "ECDH host generated: ");
dump_hex(secret, len, stderr, true);
dump_data(secret, len, stderr, true, format_arg_hex);
fprintf(stderr, "ECDH card generated: ");
dump_hex(secret2, len, stderr, true);
dump_data(secret2, len, stderr, true, format_arg_hex);
}
if(memcmp(secret, secret2, key_len) == 0) {
fprintf(stderr, "Successfully performed ECDH exchange with card.\n");
@@ -1618,11 +1665,6 @@ static bool attest(ykpiv_state *state, const char *slot,
return false;
}
output_file = open_file(output_file_name, OUTPUT);
if(!output_file) {
return false;
}
if(ykpiv_transfer_data(state, templ, NULL, 0, data, &len, &sw) != YKPIV_OK) {
fprintf(stderr, "Failed to communicate.\n");
goto attest_out;
@@ -1660,7 +1702,73 @@ attest_out:
if(x509) {
X509_free(x509);
}
return ret;
}
static bool write_object(ykpiv_state *state, int id,
const char *input_file_name, int verbosity, enum enum_format format) {
bool ret = false;
FILE *input_file = NULL;
unsigned char data[3072];
size_t len = sizeof(data);
ykpiv_rc res;
input_file = open_file(input_file_name, INPUT);
if(!input_file) {
return false;
}
if(isatty(fileno(input_file))) {
fprintf(stderr, "Please paste the data...\n");
}
len = read_data(data, len, input_file, format);
if(len == 0) {
fprintf(stderr, "Failed reading data\n");
goto write_out;
}
if(verbosity) {
fprintf(stderr, "Writing %lu bytes of data to object %x.\n", len, id);
}
if((res = ykpiv_save_object(state, id, data, len)) != YKPIV_OK) {
fprintf(stderr, "Failed writing data to device: %s\n", ykpiv_strerror(res));
} else {
ret = true;
}
write_out:
if(input_file != stdin) {
fclose(input_file);
}
return ret;
}
static bool read_object(ykpiv_state *state, int id, const char *output_file_name,
enum enum_format format) {
FILE *output_file = NULL;
unsigned char data[3072];
unsigned long len = sizeof(data);
bool ret = false;
output_file = open_file(output_file_name, OUTPUT);
if(!output_file) {
return false;
}
if(ykpiv_fetch_object(state, id, data, &len) != YKPIV_OK) {
fprintf(stderr, "Failed fetching object.\n");
goto read_out;
}
dump_data(data, len, output_file, false, format);
ret = true;
read_out:
if(output_file != stdout) {
fclose(output_file);
}
return ret;
}
@@ -1671,12 +1779,16 @@ int main(int argc, char *argv[]) {
enum enum_action action;
unsigned int i;
int ret = EXIT_SUCCESS;
bool authed = false;
char pwbuf[128];
char *password;
if(cmdline_parser(argc, argv, &args_info) != 0) {
return EXIT_FAILURE;
}
verbosity = args_info.verbose_arg + (int)args_info.verbose_given;
password = args_info.password_arg;
for(i = 0; i < args_info.action_given; i++) {
action = *(args_info.action_arg + i);
@@ -1709,12 +1821,21 @@ int main(int argc, char *argv[]) {
return EXIT_FAILURE;
}
break;
case action_arg_writeMINUS_object:
case action_arg_readMINUS_object:
if(!args_info.id_given) {
fprintf(stderr, "The '%s' action needs the --id argument.\n",
cmdline_parser_action_values[action]);
return EXIT_FAILURE;
}
break;
case action_arg_changeMINUS_pin:
case action_arg_changeMINUS_puk:
case action_arg_unblockMINUS_pin:
case action_arg_verifyMINUS_pin:
case action_arg_setMINUS_mgmMINUS_key:
case action_arg_setMINUS_chuid:
case action_arg_setMINUS_ccc:
case action_arg_version:
case action_arg_reset:
case action_arg_status:
@@ -1736,20 +1857,60 @@ int main(int argc, char *argv[]) {
}
for(i = 0; i < args_info.action_given; i++) {
bool needs_auth = false;
action = *(args_info.action_arg + i);
switch(action) {
case action_arg_importMINUS_key:
case action_arg_importMINUS_certificate:
if(args_info.key_format_arg == key_format_arg_PKCS12 && !password) {
if(verbosity) {
fprintf(stderr, "Asking for password since '%s' needs it.\n", cmdline_parser_action_values[action]);
}
if(!read_pw("Password", pwbuf, sizeof(pwbuf), false)) {
fprintf(stderr, "Failed to get password.\n");
return false;
}
password = pwbuf;
}
case action_arg_generate:
case action_arg_setMINUS_mgmMINUS_key:
case action_arg_pinMINUS_retries:
case action_arg_importMINUS_key:
case action_arg_importMINUS_certificate:
case action_arg_setMINUS_chuid:
case action_arg_setMINUS_ccc:
case action_arg_deleteMINUS_certificate:
if(verbosity) {
fprintf(stderr, "Authenticating since action '%s' needs that.\n", cmdline_parser_action_values[action]);
case action_arg_writeMINUS_object:
if(!authed) {
unsigned char key[KEY_LEN];
size_t key_len = sizeof(key);
char keybuf[KEY_LEN*2+1];
char *key_ptr = args_info.key_arg;
if(verbosity) {
fprintf(stderr, "Authenticating since action '%s' needs that.\n", cmdline_parser_action_values[action]);
}
if(args_info.key_given && args_info.key_orig == NULL) {
if(!read_pw("management key", keybuf, sizeof(keybuf), false)) {
fprintf(stderr, "Failed to read management key from stdin,\n");
return EXIT_FAILURE;
}
key_ptr = keybuf;
}
if(ykpiv_hex_decode(key_ptr, strlen(key_ptr), key, &key_len) != YKPIV_OK) {
fprintf(stderr, "Failed decoding key!\n");
return EXIT_FAILURE;
}
if(ykpiv_authenticate(state, key) != YKPIV_OK) {
fprintf(stderr, "Failed authentication with the application.\n");
return EXIT_FAILURE;
}
if(verbosity) {
fprintf(stderr, "Successful application authentication.\n");
}
authed = true;
} else {
if(verbosity) {
fprintf(stderr, "Skipping authentication for '%s' since it's already done.\n", cmdline_parser_action_values[action]);
}
}
needs_auth = true;
break;
case action_arg_version:
case action_arg_reset:
@@ -1765,44 +1926,20 @@ int main(int argc, char *argv[]) {
case action_arg_testMINUS_decipher:
case action_arg_listMINUS_readers:
case action_arg_attest:
case action_arg_readMINUS_object:
case action__NULL:
default:
if(verbosity) {
fprintf(stderr, "Action '%s' does not need authentication.\n", cmdline_parser_action_values[action]);
}
continue;
}
if(needs_auth) {
unsigned char key[KEY_LEN];
size_t key_len = sizeof(key);
char keybuf[KEY_LEN*2+1];
char *key_ptr = args_info.key_arg;
if(args_info.key_given && args_info.key_orig == NULL) {
if(!read_pw("management key", keybuf, sizeof(keybuf), false)) {
fprintf(stderr, "Failed to read management key from stdin,\n");
return EXIT_FAILURE;
}
key_ptr = keybuf;
}
if(ykpiv_hex_decode(key_ptr, strlen(key_ptr), key, &key_len) != YKPIV_OK) {
fprintf(stderr, "Failed decoding key!\n");
return EXIT_FAILURE;
}
if(ykpiv_authenticate(state, key) != YKPIV_OK) {
fprintf(stderr, "Failed authentication with the application.\n");
return EXIT_FAILURE;
}
if(verbosity) {
fprintf(stderr, "Successful application authentication.\n");
}
break;
}
}
/* openssl setup.. */
OpenSSL_add_all_algorithms();
for(i = 0; i < args_info.action_given; i++) {
char new_keybuf[KEY_LEN*2+1] = {0};
char *new_mgm_key = args_info.new_key_arg;
@@ -1855,7 +1992,7 @@ int main(int argc, char *argv[]) {
break;
case action_arg_reset:
if(reset(state) == false) {
fprintf(stderr, "Reset failed, are pincodes blocked?\n");
fprintf(stderr, "Reset failed, are pincodes blocked?\n");
ret = EXIT_FAILURE;
} else {
fprintf(stderr, "Successfully reset the application.\n");
@@ -1871,25 +2008,27 @@ int main(int argc, char *argv[]) {
}
break;
case action_arg_importMINUS_key:
if(import_key(state, args_info.key_format_arg, args_info.input_arg, args_info.slot_orig, args_info.password_arg,
if(import_key(state, args_info.key_format_arg, args_info.input_arg, args_info.slot_orig, password,
args_info.pin_policy_arg, args_info.touch_policy_arg) == false) {
fprintf(stderr, "Unable to import private key\n");
ret = EXIT_FAILURE;
} else {
fprintf(stderr, "Successfully imported a new private key.\n");
}
break;
case action_arg_importMINUS_certificate:
if(import_cert(state, args_info.key_format_arg, args_info.input_arg, args_info.slot_arg, args_info.password_arg) == false) {
if(import_cert(state, args_info.key_format_arg, args_info.input_arg, args_info.slot_arg, password) == false) {
ret = EXIT_FAILURE;
} else {
fprintf(stderr, "Successfully imported a new certificate.\n");
}
break;
case action_arg_setMINUS_ccc:
case action_arg_setMINUS_chuid:
if(set_chuid(state, verbosity) == false) {
if(set_dataobject(state, verbosity, action == action_arg_setMINUS_chuid ? CHUID : CCC) == false) {
ret = EXIT_FAILURE;
} else {
fprintf(stderr, "Successfully set new CHUID.\n");
fprintf(stderr, "Successfully set new %s.\n", action == action_arg_setMINUS_chuid ? "CHUID" : "CCC");
}
break;
case action_arg_requestMINUS_certificate:
@@ -1925,6 +2064,7 @@ int main(int argc, char *argv[]) {
case action_arg_selfsignMINUS_certificate:
if(selfsign_certificate(state, args_info.key_format_arg, args_info.input_arg,
args_info.slot_orig, args_info.subject_arg, args_info.hash_arg,
args_info.serial_arg, args_info.valid_days_arg,
args_info.output_arg) == false) {
ret = EXIT_FAILURE;
} else {
@@ -1943,7 +2083,7 @@ int main(int argc, char *argv[]) {
}
break;
case action_arg_status:
if(status(state, args_info.hash_arg, args_info.output_arg) == false) {
if(status(state, args_info.hash_arg, args_info.slot_arg, args_info.output_arg) == false) {
ret = EXIT_FAILURE;
}
break;
@@ -1963,6 +2103,17 @@ int main(int argc, char *argv[]) {
if(list_readers(state) == false) {
ret = EXIT_FAILURE;
}
case action_arg_writeMINUS_object:
if(write_object(state, args_info.id_arg, args_info.input_arg, verbosity,
args_info.format_arg) == false) {
ret = EXIT_FAILURE;
}
break;
case action_arg_readMINUS_object:
if(read_object(state, args_info.id_arg, args_info.output_arg,
args_info.format_arg) == false) {
ret = EXIT_FAILURE;
}
break;
case action_arg_attest:
if(attest(state, args_info.slot_orig, args_info.key_format_arg,