From f17d09f19c03900c21a63ac089287c77ca440235 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Wed, 27 May 2015 21:01:58 +0200 Subject: [PATCH 01/15] start adding secp384r1 --- lib/ykpiv.c | 25 ++++++++++++--------- lib/ykpiv.h | 1 + tool/cmdline.ggo | 2 +- tool/util.c | 2 ++ tool/yubico-piv-tool.c | 49 +++++++++++++++++++++++++++++++----------- 5 files changed, 56 insertions(+), 23 deletions(-) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index a02edd8..e907b19 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -508,7 +508,7 @@ static ykpiv_rc _general_authenticate(ykpiv_state *state, unsigned char templ[] = {0, YKPIV_INS_AUTHENTICATE, algorithm, key}; unsigned long recv_len = sizeof(data); unsigned char sign_in[256]; - size_t pad_len = 0; + size_t key_len = 0; int sw; size_t bytes; size_t len = 0; @@ -516,28 +516,33 @@ static ykpiv_rc _general_authenticate(ykpiv_state *state, switch(algorithm) { case YKPIV_ALGO_RSA1024: - pad_len = 128; + key_len = 128; case YKPIV_ALGO_RSA2048: - if(pad_len == 0) { - pad_len = 256; + if(key_len == 0) { + key_len = 256; } if(!decipher) { - if(in_len + RSA_PKCS1_PADDING_SIZE > pad_len) { + if(in_len + RSA_PKCS1_PADDING_SIZE > key_len) { return YKPIV_SIZE_ERROR; } - RSA_padding_add_PKCS1_type_1(sign_in, pad_len, raw_in, in_len); - in_len = pad_len; + RSA_padding_add_PKCS1_type_1(sign_in, key_len, raw_in, in_len); + in_len = key_len; } else { - if(in_len != pad_len) { + if(in_len != key_len) { return YKPIV_SIZE_ERROR; } memcpy(sign_in, raw_in, in_len); } break; case YKPIV_ALGO_ECCP256: - if(!decipher && in_len > 32) { + key_len = 32; + case YKPIV_ALGO_ECCP384: + if(key_len == 0) { + key_len = 48; + } + if(!decipher && in_len > key_len) { return YKPIV_SIZE_ERROR; - } else if(decipher && in_len != 65) { + } else if(decipher && in_len != (key_len * 2) + 1) { return YKPIV_SIZE_ERROR; } memcpy(sign_in, raw_in, in_len); diff --git a/lib/ykpiv.h b/lib/ykpiv.h index 26a4a93..8333a86 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -89,6 +89,7 @@ extern "C" #define YKPIV_ALGO_RSA1024 0x06 #define YKPIV_ALGO_RSA2048 0x07 #define YKPIV_ALGO_ECCP256 0x11 +#define YKPIV_ALGO_ECCP384 0x14 #define YKPIV_KEY_AUTHENTICATION 0x9a #define YKPIV_KEY_CARDMGM 0x9b diff --git a/tool/cmdline.ggo b/tool/cmdline.ggo index 16fbff6..2696b39 100644 --- a/tool/cmdline.ggo +++ b/tool/cmdline.ggo @@ -41,7 +41,7 @@ text " 9c is for Digital Signature (PIN always checked) 9d is for Key Management 9e is for Card Authentication (PIN never checked)\n" -option "algorithm" A "What algorithm to use" values="RSA1024","RSA2048","ECCP256" enum optional default="RSA2048" +option "algorithm" A "What algorithm to use" values="RSA1024","RSA2048","ECCP256","ECCP384" enum optional default="RSA2048" option "hash" H "Hash to use for signatures" values="SHA1","SHA256","SHA512" enum optional default="SHA256" option "new-key" n "New authentication key to use" string optional option "pin-retries" - "Number of retries before the pin code is blocked" int optional dependon="puk-retries" diff --git a/tool/util.c b/tool/util.c index f08af50..f46b27d 100644 --- a/tool/util.c +++ b/tool/util.c @@ -80,6 +80,8 @@ unsigned char get_algorithm(EVP_PKEY *key) { int curve = EC_GROUP_get_curve_name(group); if(curve == NID_X9_62_prime256v1) { return YKPIV_ALGO_ECCP256; + } else if(curve == NID_secp384r1) { + return YKPIV_ALGO_ECCP384; } else { fprintf(stderr, "Unknown EC curve %d\n", curve); return 0; diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index a9b6139..fd7036d 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -138,6 +138,9 @@ static bool generate_key(ykpiv_state *state, const char *slot, case algorithm_arg_ECCP256: in_data[4] = YKPIV_ALGO_ECCP256; break; + case algorithm_arg_ECCP384: + in_data[4] = YKPIV_ALGO_ECCP384; + break; case algorithm__NULL: default: fprintf(stderr, "Unexepcted algorithm.\n"); @@ -189,24 +192,34 @@ static bool generate_key(ykpiv_state *state, const char *slot, rsa->n = bignum_n; rsa->e = bignum_e; EVP_PKEY_set1_RSA(public_key, rsa); - } else if(algorithm == algorithm_arg_ECCP256) { + } else if(algorithm == algorithm_arg_ECCP256 || algorithm == algorithm_arg_ECCP384) { EC_GROUP *group; unsigned char *data_ptr = data + 3; + int nid; + size_t len; + + if(algorithm == algorithm_arg_ECCP256) { + nid = NID_X9_62_prime256v1; + len = 65; + } else { + nid = NID_secp384r1; + len = 97; + } eckey = EC_KEY_new(); - group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); - EC_GROUP_set_asn1_flag(group, NID_X9_62_prime256v1); + group = EC_GROUP_new_by_curve_name(nid); + EC_GROUP_set_asn1_flag(group, nid); EC_KEY_set_group(eckey, group); point = EC_POINT_new(group); if(*data_ptr++ != 0x86) { fprintf(stderr, "Failed to parse public key structure.\n"); goto generate_out; } - if(*data_ptr++ != 65) { /* the curve point should always be 65 bytes */ + if(*data_ptr++ != len) { /* the curve point should always be 65 bytes */ fprintf(stderr, "Unexpected length.\n"); goto generate_out; } - if(!EC_POINT_oct2point(group, point, data_ptr, 65, NULL)) { + if(!EC_POINT_oct2point(group, point, data_ptr, len, NULL)) { fprintf(stderr, "Failed to load public point.\n"); goto generate_out; } @@ -674,6 +687,7 @@ static bool request_certificate(ykpiv_state *state, enum enum_key_format key_for } break; case YKPIV_ALGO_ECCP256: + case YKPIV_ALGO_ECCP384: signinput = digest + oid_len; len = digest_len; switch(hash) { @@ -866,6 +880,7 @@ static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_fo } break; case YKPIV_ALGO_ECCP256: + case YKPIV_ALGO_ECCP384: signinput = digest + oid_len; len = md_len; switch(hash) { @@ -1577,20 +1592,30 @@ static bool test_decipher(ykpiv_state *state, enum enum_slot slot, } else { fprintf(stderr, "Failed unwrapping PKCS1 envelope.\n"); } - } else { - unsigned char secret[32]; - unsigned char secret2[32]; - unsigned char public_key[65]; + } else if(algorithm == YKPIV_ALGO_ECCP256 || algorithm == YKPIV_ALGO_ECCP384) { + unsigned char secret[48]; + unsigned char secret2[48]; + unsigned char public_key[97]; unsigned char *ptr = public_key; size_t len = sizeof(secret); EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pubkey); + int nid; + size_t key_len; - tmpkey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + if(algorithm == YKPIV_ALGO_ECCP256) { + nid = NID_X9_62_prime256v1; + key_len = 32; + } else { + nid = NID_secp384r1; + key_len = 48; + } + + tmpkey = EC_KEY_new_by_curve_name(nid); EC_KEY_generate_key(tmpkey); ECDH_compute_key(secret, len, EC_KEY_get0_public_key(ec), tmpkey, NULL); i2o_ECPublicKey(tmpkey, &ptr); - if(ykpiv_decipher_data(state, public_key, sizeof(public_key), secret2, &len, algorithm, key) != YKPIV_OK) { + if(ykpiv_decipher_data(state, public_key, (key_len * 2) + 1, secret2, &len, algorithm, key) != YKPIV_OK) { fprintf(stderr, "Failed ECDH exchange!\n"); goto decipher_out; } @@ -1600,7 +1625,7 @@ static bool test_decipher(ykpiv_state *state, enum enum_slot slot, fprintf(stderr, "ECDH card generated: "); dump_hex(secret2, len, stderr, true); } - if(memcmp(secret, secret2, 32) == 0) { + if(memcmp(secret, secret2, key_len) == 0) { fprintf(stderr, "Successfully performed ECDH exchange with card.\n"); ret = true; } else { From d06852959cf6fcb0d0fa496edead1de4cc716f40 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Wed, 27 May 2015 21:38:46 +0200 Subject: [PATCH 02/15] add sha384 hash and refactor some common patterns --- tool/cmdline.ggo | 2 +- tool/util.c | 105 +++++++++++++++++ tool/util.h | 3 + tool/yubico-piv-tool.c | 252 ++++++++--------------------------------- 4 files changed, 155 insertions(+), 207 deletions(-) diff --git a/tool/cmdline.ggo b/tool/cmdline.ggo index 2696b39..25b606f 100644 --- a/tool/cmdline.ggo +++ b/tool/cmdline.ggo @@ -42,7 +42,7 @@ text " 9d is for Key Management 9e is for Card Authentication (PIN never checked)\n" option "algorithm" A "What algorithm to use" values="RSA1024","RSA2048","ECCP256","ECCP384" enum optional default="RSA2048" -option "hash" H "Hash to use for signatures" values="SHA1","SHA256","SHA512" enum optional default="SHA256" +option "hash" H "Hash to use for signatures" values="SHA1","SHA256","SHA384","SHA512" enum optional default="SHA256" option "new-key" n "New authentication key to use" string optional option "pin-retries" - "Number of retries before the pin code is blocked" int optional dependon="puk-retries" option "puk-retries" - "Number of retries before the puk code is blocked" int optional dependon="pin-retries" diff --git a/tool/util.c b/tool/util.c index f46b27d..ac1654e 100644 --- a/tool/util.c +++ b/tool/util.c @@ -239,3 +239,108 @@ bool prepare_rsa_signature(const unsigned char *in, unsigned int in_len, unsigne *out_len = (unsigned int)i2d_X509_SIG(&digestInfo, &out); return true; } + +static unsigned const char sha1oid[] = { + 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, + 0x04, 0x14 +}; + +static unsigned const char sha256oid[] = { + 0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, + 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 +}; + +static unsigned const char sha384oid[] = { + 0x30, 0x41, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, + 0x02, 0x02, 0x05, 0x00, 0x04, 0x30 +}; + +static unsigned const char sha512oid[] = { + 0x30, 0x51, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, + 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 +}; + +const EVP_MD *get_hash(enum enum_hash hash, const unsigned char **oid, size_t *oid_len) { + switch(hash) { + case hash_arg_SHA1: + if(oid) { + *oid = sha1oid; + *oid_len = sizeof(sha1oid); + } + return EVP_sha1(); + case hash_arg_SHA256: + if(oid) { + *oid = sha256oid; + *oid_len = sizeof(sha256oid); + } + return EVP_sha256(); + case hash_arg_SHA384: + if(oid) { + *oid = sha384oid; + *oid_len = sizeof(sha384oid); + } + return EVP_sha384(); + case hash_arg_SHA512: + if(oid) { + *oid = sha512oid; + *oid_len = sizeof(sha512oid); + } + return EVP_sha512(); + case hash__NULL: + default: + return NULL; + } +} + +int get_hashnid(enum enum_hash hash, unsigned char algorithm) { + switch(algorithm) { + case YKPIV_ALGO_RSA1024: + case YKPIV_ALGO_RSA2048: + switch(hash) { + case hash_arg_SHA1: + return NID_sha1WithRSAEncryption; + case hash_arg_SHA256: + return NID_sha256WithRSAEncryption; + case hash_arg_SHA384: + return NID_sha384WithRSAEncryption; + case hash_arg_SHA512: + return NID_sha512WithRSAEncryption; + case hash__NULL: + default: + return 0; + } + case YKPIV_ALGO_ECCP256: + case YKPIV_ALGO_ECCP384: + switch(hash) { + case hash_arg_SHA1: + return NID_ecdsa_with_SHA1; + case hash_arg_SHA256: + return NID_ecdsa_with_SHA256; + case hash_arg_SHA384: + return NID_ecdsa_with_SHA384; + case hash_arg_SHA512: + return NID_ecdsa_with_SHA512; + case hash__NULL: + default: + return 0; + } + default: + return 0; + } +} + +unsigned char get_piv_algorithm(enum enum_algorithm algorithm) { + switch(algorithm) { + case algorithm_arg_RSA2048: + return YKPIV_ALGO_RSA2048; + case algorithm_arg_RSA1024: + return YKPIV_ALGO_RSA1024; + case algorithm_arg_ECCP256: + return YKPIV_ALGO_ECCP256; + case algorithm_arg_ECCP384: + return YKPIV_ALGO_ECCP384; + case algorithm__NULL: + default: + return 0; + } +} diff --git a/tool/util.h b/tool/util.h index 2e619be..066d389 100644 --- a/tool/util.h +++ b/tool/util.h @@ -47,5 +47,8 @@ int get_object_id(enum enum_slot slot); bool set_component_with_len(unsigned char**, const BIGNUM*, int); bool prepare_rsa_signature(const unsigned char*, unsigned int, unsigned char*, unsigned int*, int); +const EVP_MD *get_hash(enum enum_hash, const unsigned char**, size_t*); +int get_hashnid(enum enum_hash, unsigned char); +unsigned char get_piv_algorithm(enum enum_algorithm); #endif diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index fd7036d..e781c75 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -62,20 +62,7 @@ unsigned const char chuid_tmpl[] = { }; #define CHUID_GUID_OFFS 29 -unsigned const char sha1oid[] = { - 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, - 0x04, 0x14 -}; - -unsigned const char sha256oid[] = { - 0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, - 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 -}; - -unsigned const char sha512oid[] = { - 0x30, 0x51, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, - 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 -}; +#define MAX_OID_LEN 19 #define KEY_LEN 24 @@ -128,23 +115,10 @@ static bool generate_key(ykpiv_state *state, const char *slot, in_data[1] = 3; in_data[2] = 0x80; in_data[3] = 1; - switch(algorithm) { - case algorithm_arg_RSA2048: - in_data[4] = YKPIV_ALGO_RSA2048; - break; - case algorithm_arg_RSA1024: - in_data[4] = YKPIV_ALGO_RSA1024; - break; - case algorithm_arg_ECCP256: - in_data[4] = YKPIV_ALGO_ECCP256; - break; - case algorithm_arg_ECCP384: - in_data[4] = YKPIV_ALGO_ECCP384; - break; - case algorithm__NULL: - default: - fprintf(stderr, "Unexepcted algorithm.\n"); - goto generate_out; + in_data[4] = get_piv_algorithm(algorithm); + if(in_data[4] == 0) { + fprintf(stderr, "Unexepcted algorithm.\n"); + goto generate_out; } if(ykpiv_transfer_data(state, templ, in_data, sizeof(in_data), data, &recv_len, &sw) != YKPIV_OK) { @@ -574,7 +548,7 @@ static bool request_certificate(ykpiv_state *state, enum enum_key_format key_for EVP_PKEY *public_key = NULL; const EVP_MD *md; bool ret = false; - unsigned char digest[EVP_MAX_MD_SIZE + sizeof(sha512oid)]; // maximum.. + unsigned char digest[EVP_MAX_MD_SIZE + MAX_OID_LEN]; unsigned int digest_len; unsigned int md_len; unsigned char algorithm; @@ -612,25 +586,9 @@ static bool request_certificate(ykpiv_state *state, enum enum_key_format key_for goto request_out; } - switch(hash) { - case hash_arg_SHA1: - md = EVP_sha1(); - oid = sha1oid; - oid_len = sizeof(sha1oid); - break; - case hash_arg_SHA256: - md = EVP_sha256(); - oid = sha256oid; - oid_len = sizeof(sha256oid); - break; - case hash_arg_SHA512: - md = EVP_sha512(); - oid = sha512oid; - oid_len = sizeof(sha512oid); - break; - case hash__NULL: - default: - goto request_out; + md = get_hash(hash, &oid, &oid_len); + if(md == NULL) { + goto request_out; } md_len = (unsigned int)EVP_MD_size(md); @@ -666,49 +624,19 @@ static bool request_certificate(ykpiv_state *state, enum enum_key_format key_for goto request_out; } - switch(algorithm) { - case YKPIV_ALGO_RSA1024: - case YKPIV_ALGO_RSA2048: - signinput = digest; - len = oid_len + digest_len; - switch(hash) { - case hash_arg_SHA1: - nid = NID_sha1WithRSAEncryption; - break; - case hash_arg_SHA256: - nid = NID_sha256WithRSAEncryption; - break; - case hash_arg_SHA512: - nid = NID_sha512WithRSAEncryption; - break; - case hash__NULL: - default: - goto request_out; - } - break; - case YKPIV_ALGO_ECCP256: - case YKPIV_ALGO_ECCP384: + nid = get_hashnid(hash, algorithm); + if(nid == 0) { + fprintf(stderr, "Unsupported algorithm %x or hash %x\n", algorithm, hash); + goto request_out; + } + if(algorithm == YKPIV_ALGO_RSA1024 || algorithm == YKPIV_ALGO_RSA2048) { + signinput = digest; + len = oid_len + digest_len; + } else { signinput = digest + oid_len; len = digest_len; - switch(hash) { - case hash_arg_SHA1: - nid = NID_ecdsa_with_SHA1; - break; - case hash_arg_SHA256: - nid = NID_ecdsa_with_SHA256; - break; - case hash_arg_SHA512: - nid = NID_ecdsa_with_SHA512; - break; - case hash__NULL: - default: - goto request_out; - } - break; - default: - fprintf(stderr, "Unsupported algorithm %x.\n", algorithm); - goto request_out; } + req->sig_alg->algorithm = OBJ_nid2obj(nid); { unsigned char signature[1024]; @@ -759,7 +687,7 @@ static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_fo X509 *x509 = NULL; X509_NAME *name = NULL; const EVP_MD *md; - unsigned char digest[EVP_MAX_MD_SIZE + sizeof(sha512oid)]; + unsigned char digest[EVP_MAX_MD_SIZE + MAX_OID_LEN]; unsigned int digest_len; unsigned char algorithm; int key = 0; @@ -797,27 +725,10 @@ static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_fo goto selfsign_out; } - switch(hash) { - case hash_arg_SHA1: - md = EVP_sha1(); - oid = sha1oid; - oid_len = sizeof(sha1oid); - break; - case hash_arg_SHA256: - md = EVP_sha256(); - oid = sha256oid; - oid_len = sizeof(sha256oid); - break; - case hash_arg_SHA512: - md = EVP_sha512(); - oid = sha512oid; - oid_len = sizeof(sha512oid); - break; - case hash__NULL: - default: - goto selfsign_out; + md = get_hash(hash, &oid, &oid_len); + if(md == NULL) { + goto selfsign_out; } - md_len = (unsigned int)EVP_MD_size(md); digest_len = sizeof(digest) - md_len; @@ -859,49 +770,18 @@ static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_fo fprintf(stderr, "Failed setting certificate issuer.\n"); goto selfsign_out; } - switch(algorithm) { - case YKPIV_ALGO_RSA1024: - case YKPIV_ALGO_RSA2048: - signinput = digest; - len = oid_len + md_len; - switch(hash) { - case hash_arg_SHA1: - nid = NID_sha1WithRSAEncryption; - break; - case hash_arg_SHA256: - nid = NID_sha256WithRSAEncryption; - break; - case hash_arg_SHA512: - nid = NID_sha512WithRSAEncryption; - break; - case hash__NULL: - default: - goto selfsign_out; - } - break; - case YKPIV_ALGO_ECCP256: - case YKPIV_ALGO_ECCP384: - signinput = digest + oid_len; - len = md_len; - switch(hash) { - case hash_arg_SHA1: - nid = NID_ecdsa_with_SHA1; - break; - case hash_arg_SHA256: - nid = NID_ecdsa_with_SHA256; - break; - case hash_arg_SHA512: - nid = NID_ecdsa_with_SHA512; - break; - case hash__NULL: - default: - goto selfsign_out; - } - break; - default: - fprintf(stderr, "Unsupported algorithm %x.\n", algorithm); - goto selfsign_out; + nid = get_hashnid(hash, algorithm); + if(nid == 0) { + goto selfsign_out; } + if(algorithm == YKPIV_ALGO_RSA1024 || algorithm == YKPIV_ALGO_RSA2048) { + signinput = digest; + len = oid_len + md_len; + } else { + signinput = digest + oid_len; + len = md_len; + } + x509->sig_alg->algorithm = OBJ_nid2obj(nid); x509->cert_info->signature->algorithm = x509->sig_alg->algorithm; memcpy(digest, oid, oid_len); @@ -1128,37 +1008,17 @@ static bool sign_file(ykpiv_state *state, const char *input, const char *output, return false; } - switch(algorithm) { - case algorithm_arg_RSA2048: - algo = YKPIV_ALGO_RSA2048; - break; - case algorithm_arg_RSA1024: - algo = YKPIV_ALGO_RSA1024; - break; - case algorithm_arg_ECCP256: - algo = YKPIV_ALGO_ECCP256; - break; - case algorithm__NULL: - default: - goto out; + algo = get_piv_algorithm(algorithm); + if(algo == 0) { + goto out; } { EVP_MD_CTX *mdctx; - switch(hash) { - case hash_arg_SHA1: - md = EVP_sha1(); - break; - case hash_arg_SHA256: - md = EVP_sha256(); - break; - case hash_arg_SHA512: - md = EVP_sha512(); - break; - case hash__NULL: - default: - goto out; + md = get_hash(hash, NULL, NULL); + if(md == NULL) { + goto out; } mdctx = EVP_MD_CTX_create(); @@ -1318,19 +1178,9 @@ static bool status(ykpiv_state *state, enum enum_hash hash, return false; } - switch(hash) { - case hash_arg_SHA1: - md = EVP_sha1(); - break; - case hash_arg_SHA256: - md = EVP_sha256(); - break; - case hash_arg_SHA512: - md = EVP_sha512(); - break; - case hash__NULL: - default: - return false; + md = get_hash(hash, NULL, NULL); + if(md == NULL) { + return false; } fprintf(output_file, "CHUID:\t"); @@ -1394,19 +1244,9 @@ static bool test_signature(ykpiv_state *state, enum enum_slot slot, goto test_out; } - switch(hash) { - case hash_arg_SHA1: - md = EVP_sha1(); - break; - case hash_arg_SHA256: - md = EVP_sha256(); - break; - case hash_arg_SHA512: - md = EVP_sha512(); - break; - case hash__NULL: - default: - return false; + md = get_hash(hash, NULL, NULL); + if(md == NULL) { + return false; } { From 15413628661f157dbc9e6fc18fda8c81c1795692 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Wed, 24 Jun 2015 13:03:14 +0200 Subject: [PATCH 03/15] add macros for IS_ECKEY and IS_RSAKEY, also fix tag on ECCP384 --- lib/ykpiv.c | 2 +- lib/ykpiv.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index e907b19..89fbe55 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -563,7 +563,7 @@ static ykpiv_rc _general_authenticate(ykpiv_state *state, dataptr += set_length(dataptr, in_len + bytes + 3); *dataptr++ = 0x82; *dataptr++ = 0x00; - *dataptr++ = algorithm == YKPIV_ALGO_ECCP256 && decipher ? 0x85 : 0x81; + *dataptr++ = IS_ECKEY(algorithm) && decipher ? 0x85 : 0x81; dataptr += set_length(dataptr, in_len); memcpy(dataptr, sign_in, (size_t)in_len); dataptr += in_len; diff --git a/lib/ykpiv.h b/lib/ykpiv.h index 8333a86..d213f5c 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -126,6 +126,9 @@ extern "C" #define YKPIV_INS_RESET 0xfb #define YKPIV_INS_SET_PIN_RETRIES 0xfa +#define IS_ECKEY(a) ((a == YKPIV_ALGO_ECCP256 || a == YKPIV_ALGO_ECCP384)) +#define IS_RSAKEY(a) ((a == YKPIV_ALGO_RSA1024 || a == YKPIV_ALGO_RSA2048)) + #ifdef __cplusplus } #endif From c2621960a9747d6ff43577511b4cd3812ca3843c Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Wed, 24 Jun 2015 13:04:06 +0200 Subject: [PATCH 04/15] use IS_RSAKEY and IS_ECKEY macros, fix minor stuff for ECCP384 --- tool/yubico-piv-tool.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index e781c75..382a889 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -325,7 +325,7 @@ static bool import_key(ykpiv_state *state, enum enum_key_format key_format, unsigned char *in_ptr = in_data; unsigned char templ[] = {0, YKPIV_INS_IMPORT_KEY, algorithm, key}; int sw; - if(algorithm == YKPIV_ALGO_RSA1024 || algorithm == YKPIV_ALGO_RSA2048) { + if(IS_RSAKEY(algorithm)) { RSA *rsa_private_key = EVP_PKEY_get1_RSA(private_key); unsigned char e[4]; unsigned char *e_ptr = e; @@ -369,12 +369,17 @@ static bool import_key(ykpiv_state *state, enum enum_key_format key_format, fprintf(stderr, "Failed setting iqmp component.\n"); goto import_out; } - } else if(algorithm == YKPIV_ALGO_ECCP256) { + } else if(IS_ECKEY(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, 32) == false) { + if(set_component_with_len(&in_ptr, s, element_len) == false) { fprintf(stderr, "Failed setting ec private key.\n"); goto import_out; } @@ -629,7 +634,7 @@ static bool request_certificate(ykpiv_state *state, enum enum_key_format key_for fprintf(stderr, "Unsupported algorithm %x or hash %x\n", algorithm, hash); goto request_out; } - if(algorithm == YKPIV_ALGO_RSA1024 || algorithm == YKPIV_ALGO_RSA2048) { + if(IS_RSAKEY(algorithm)) { signinput = digest; len = oid_len + digest_len; } else { @@ -774,7 +779,7 @@ static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_fo if(nid == 0) { goto selfsign_out; } - if(algorithm == YKPIV_ALGO_RSA1024 || algorithm == YKPIV_ALGO_RSA2048) { + if(IS_RSAKEY(algorithm)) { signinput = digest; len = oid_len + md_len; } else { @@ -1037,7 +1042,7 @@ static bool sign_file(ykpiv_state *state, const char *input, const char *output, EVP_MD_CTX_destroy(mdctx); } - if(algo == YKPIV_ALGO_RSA1024 || algo == YKPIV_ALGO_RSA2048) { + if(IS_RSAKEY(algo)) { prepare_rsa_signature(hashed, hash_len, hashed, &hash_len, EVP_MD_type(md)); } @@ -1118,6 +1123,9 @@ static void print_cert_info(ykpiv_state *state, enum enum_slot slot, const EVP_M case YKPIV_ALGO_ECCP256: fprintf(output, "ECCP256\n"); break; + case YKPIV_ALGO_ECCP384: + fprintf(output, "ECCP384\n"); + break; default: fprintf(output, "Unknown\n"); } @@ -1286,7 +1294,7 @@ static bool test_signature(ykpiv_state *state, enum enum_slot slot, goto test_out; } sscanf(cmdline_parser_slot_values[slot], "%2x", &key); - if(algorithm == YKPIV_ALGO_RSA1024 || algorithm == YKPIV_ALGO_RSA2048) { + if(IS_RSAKEY(algorithm)) { prepare_rsa_signature(data, data_len, encoded, &enc_len, EVP_MD_type(md)); ptr = encoded; } else { @@ -1320,6 +1328,7 @@ static bool test_signature(ykpiv_state *state, enum enum_slot slot, break; case YKPIV_ALGO_ECCP256: + case YKPIV_ALGO_ECCP384: { EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pubkey); if(ECDSA_verify(0, data, (int)data_len, signature, (int)sig_len, ec) == 1) { @@ -1391,7 +1400,7 @@ static bool test_decipher(ykpiv_state *state, enum enum_slot slot, goto decipher_out; } sscanf(cmdline_parser_slot_values[slot], "%2x", &key); - if(algorithm == YKPIV_ALGO_RSA1024 || algorithm == YKPIV_ALGO_RSA2048) { + if(IS_RSAKEY(algorithm)) { unsigned char secret[32]; unsigned char secret2[32]; unsigned char data[256]; @@ -1432,7 +1441,7 @@ static bool test_decipher(ykpiv_state *state, enum enum_slot slot, } else { fprintf(stderr, "Failed unwrapping PKCS1 envelope.\n"); } - } else if(algorithm == YKPIV_ALGO_ECCP256 || algorithm == YKPIV_ALGO_ECCP384) { + } else if(IS_ECKEY(algorithm)) { unsigned char secret[48]; unsigned char secret2[48]; unsigned char public_key[97]; From a73d708c2555cadf5a2f27fd8d3efcbb4d08f29a Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 23 Jun 2015 14:12:39 +0200 Subject: [PATCH 05/15] add support for retired key objects slots 82-95 --- lib/ykpiv.h | 21 +++++++++++++++++ tool/cmdline.ggo | 5 ++-- tool/util.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 2 deletions(-) diff --git a/lib/ykpiv.h b/lib/ykpiv.h index d213f5c..56c0081 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -111,6 +111,27 @@ extern "C" #define YKPIV_OBJ_KEY_HISTORY 0x5fc10c #define YKPIV_OBJ_IRIS 0x5fc121 +#define YKPIV_OBJ_RETIRED1 0x5fc10d +#define YKPIV_OBJ_RETIRED2 0x5fc10e +#define YKPIV_OBJ_RETIRED3 0x5fc10f +#define YKPIV_OBJ_RETIRED4 0x5fc110 +#define YKPIV_OBJ_RETIRED5 0x5fc111 +#define YKPIV_OBJ_RETIRED6 0x5fc112 +#define YKPIV_OBJ_RETIRED7 0x5fc113 +#define YKPIV_OBJ_RETIRED8 0x5fc114 +#define YKPIV_OBJ_RETIRED9 0x5fc115 +#define YKPIV_OBJ_RETIRED10 0x5fc116 +#define YKPIV_OBJ_RETIRED11 0x5fc117 +#define YKPIV_OBJ_RETIRED12 0x5fc118 +#define YKPIV_OBJ_RETIRED13 0x5fc119 +#define YKPIV_OBJ_RETIRED14 0x5fc11a +#define YKPIV_OBJ_RETIRED15 0x5fc11b +#define YKPIV_OBJ_RETIRED16 0x5fc11c +#define YKPIV_OBJ_RETIRED17 0x5fc11d +#define YKPIV_OBJ_RETIRED18 0x5fc11e +#define YKPIV_OBJ_RETIRED19 0x5fc11f +#define YKPIV_OBJ_RETIRED20 0x5fc120 + #define YKPIV_INS_VERIFY 0x20 #define YKPIV_INS_CHANGE_REFERENCE 0x24 #define YKPIV_INS_RESET_RETRY 0x2c diff --git a/tool/cmdline.ggo b/tool/cmdline.ggo index 25b606f..6439419 100644 --- a/tool/cmdline.ggo +++ b/tool/cmdline.ggo @@ -35,12 +35,13 @@ option "action" a "Action to take" values="version","generate","set-mgm-key", text " Multiple actions may be given at once and will be executed in order for example --action=verify-pin --action=request-certificate\n" -option "slot" s "What key slot to operate on" values="9a","9c","9d","9e" enum optional +option "slot" s "What key slot to operate on" values="9a","9c","9d","9e","82","83","84","85","86","87","88","89","8a","8b","8c","8d","8e","8f","90","91","92","93","94","95" enum optional text " 9a is for PIV Authentication 9c is for Digital Signature (PIN always checked) 9d is for Key Management - 9e is for Card Authentication (PIN never checked)\n" + 9e is for Card Authentication (PIN never checked) + 82-95 is for Retired Key Management\n" option "algorithm" A "What algorithm to use" values="RSA1024","RSA2048","ECCP256","ECCP384" enum optional default="RSA2048" option "hash" H "Hash to use for signatures" values="SHA1","SHA256","SHA384","SHA512" enum optional default="SHA256" option "new-key" n "New authentication key to use" string optional diff --git a/tool/util.c b/tool/util.c index ac1654e..61e749f 100644 --- a/tool/util.c +++ b/tool/util.c @@ -200,6 +200,66 @@ int get_object_id(enum enum_slot slot) { case slot_arg_9e: object = YKPIV_OBJ_CARD_AUTH; break; + case slot_arg_82: + object = YKPIV_OBJ_RETIRED1; + break; + case slot_arg_83: + object = YKPIV_OBJ_RETIRED2; + break; + case slot_arg_84: + object = YKPIV_OBJ_RETIRED3; + break; + case slot_arg_85: + object = YKPIV_OBJ_RETIRED4; + break; + case slot_arg_86: + object = YKPIV_OBJ_RETIRED5; + break; + case slot_arg_87: + object = YKPIV_OBJ_RETIRED6; + break; + case slot_arg_88: + object = YKPIV_OBJ_RETIRED7; + break; + case slot_arg_89: + object = YKPIV_OBJ_RETIRED8; + break; + case slot_arg_8a: + object = YKPIV_OBJ_RETIRED9; + break; + case slot_arg_8b: + object = YKPIV_OBJ_RETIRED10; + break; + case slot_arg_8c: + object = YKPIV_OBJ_RETIRED11; + break; + case slot_arg_8d: + object = YKPIV_OBJ_RETIRED12; + break; + case slot_arg_8e: + object = YKPIV_OBJ_RETIRED13; + break; + case slot_arg_8f: + object = YKPIV_OBJ_RETIRED14; + break; + case slot_arg_90: + object = YKPIV_OBJ_RETIRED15; + break; + case slot_arg_91: + object = YKPIV_OBJ_RETIRED16; + break; + case slot_arg_92: + object = YKPIV_OBJ_RETIRED17; + break; + case slot_arg_93: + object = YKPIV_OBJ_RETIRED18; + break; + case slot_arg_94: + object = YKPIV_OBJ_RETIRED19; + break; + case slot_arg_95: + object = YKPIV_OBJ_RETIRED20; + break; case slot__NULL: default: object = 0; From bc27d98bf78ffe12c1bad5d9978448b437ad7b65 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Wed, 24 Jun 2015 13:22:02 +0200 Subject: [PATCH 06/15] better errors for generate on non-supported algorithm or slot --- tool/yubico-piv-tool.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 382a889..6e92caa 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -125,7 +125,14 @@ static bool generate_key(ykpiv_state *state, const char *slot, fprintf(stderr, "Failed to communicate.\n"); goto generate_out; } else if(sw != 0x9000) { - fprintf(stderr, "Failed to generate new key.\n"); + fprintf(stderr, "Failed to generate new key ("); + if(sw == 0x6b00) { + fprintf(stderr, "slot not supported?)\n"); + } else if(sw == 0x6a80) { + fprintf(stderr, "algorithm not supported?)\n"); + } else { + fprintf(stderr, "error %x)\n", sw); + } goto generate_out; } /* to drop the 90 00 and the 7f 49 at the start */ From be8f37924d10d2723cc43a6e7e883675b975716c Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Wed, 24 Jun 2015 15:25:59 +0200 Subject: [PATCH 07/15] pin policy code for generate and import-key --- lib/ykpiv.h | 5 +++++ tool/cmdline.ggo | 1 + tool/util.c | 14 ++++++++++++++ tool/util.h | 1 + tool/yubico-piv-tool.c | 40 +++++++++++++++++++++++++++++----------- 5 files changed, 50 insertions(+), 11 deletions(-) diff --git a/lib/ykpiv.h b/lib/ykpiv.h index 56c0081..a14b0e9 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -147,6 +147,11 @@ extern "C" #define YKPIV_INS_RESET 0xfb #define YKPIV_INS_SET_PIN_RETRIES 0xfa +#define YKPIV_PINPOLICY_TAG 0xaa +#define YKPIV_PINPOLICY_NEVER 1 +#define YKPIV_PINPOLICY_ONCE 2 +#define YKPIV_PINPOLICY_ALWAYS 3 + #define IS_ECKEY(a) ((a == YKPIV_ALGO_ECCP256 || a == YKPIV_ALGO_ECCP384)) #define IS_RSAKEY(a) ((a == YKPIV_ALGO_RSA1024 || a == YKPIV_ALGO_RSA2048)) diff --git a/tool/cmdline.ggo b/tool/cmdline.ggo index 6439419..50d346c 100644 --- a/tool/cmdline.ggo +++ b/tool/cmdline.ggo @@ -58,3 +58,4 @@ text " 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 "sign" - "Sign data" flag off hidden +option "pin-policy" - "Set pin policy for action generate or import-key" values="never","once","always" enum optional diff --git a/tool/util.c b/tool/util.c index 61e749f..464b9ef 100644 --- a/tool/util.c +++ b/tool/util.c @@ -404,3 +404,17 @@ unsigned char get_piv_algorithm(enum enum_algorithm algorithm) { return 0; } } + +unsigned char get_pin_policy(enum enum_pin_policy policy) { + switch(policy) { + case pin_policy_arg_never: + return YKPIV_PINPOLICY_NEVER; + case pin_policy_arg_once: + return YKPIV_PINPOLICY_ONCE; + case pin_policy_arg_always: + return YKPIV_PINPOLICY_ALWAYS; + case pin_policy__NULL: + default: + return 0; + } +} diff --git a/tool/util.h b/tool/util.h index 066d389..ea29d87 100644 --- a/tool/util.h +++ b/tool/util.h @@ -50,5 +50,6 @@ bool prepare_rsa_signature(const unsigned char*, unsigned int, unsigned char*, const EVP_MD *get_hash(enum enum_hash, const unsigned char**, size_t*); int get_hashnid(enum enum_hash, unsigned char); unsigned char get_piv_algorithm(enum enum_algorithm); +unsigned char get_pin_policy(enum enum_pin_policy); #endif diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 6e92caa..055cdbd 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -86,8 +86,9 @@ static void print_version(ykpiv_state *state, const char *output_file_name) { static bool generate_key(ykpiv_state *state, const char *slot, enum enum_algorithm algorithm, const char *output_file_name, - enum enum_key_format key_format) { - unsigned char in_data[5]; + enum enum_key_format key_format, enum enum_pin_policy pin_policy) { + unsigned char in_data[8]; + unsigned char *in_ptr = in_data; unsigned char data[1024]; unsigned char templ[] = {0, YKPIV_INS_GENERATE_ASYMMERTRIC, 0, 0}; unsigned long recv_len = sizeof(data); @@ -111,16 +112,22 @@ static bool generate_key(ykpiv_state *state, const char *slot, return false; } - in_data[0] = 0xac; - in_data[1] = 3; - in_data[2] = 0x80; - in_data[3] = 1; - in_data[4] = get_piv_algorithm(algorithm); + *in_ptr++ = 0xac; + *in_ptr++ = 3; + *in_ptr++ = 0x80; + *in_ptr++ = 1; + *in_ptr++ = get_piv_algorithm(algorithm); if(in_data[4] == 0) { fprintf(stderr, "Unexepcted algorithm.\n"); goto generate_out; } - if(ykpiv_transfer_data(state, templ, in_data, sizeof(in_data), data, + if(pin_policy != pin_policy__NULL) { + in_data[1] += 3; + *in_ptr++ = YKPIV_PINPOLICY_TAG; + *in_ptr++ = 1; + *in_ptr++ = get_pin_policy(pin_policy); + } + if(ykpiv_transfer_data(state, templ, in_data, in_ptr - in_data, data, &recv_len, &sw) != YKPIV_OK) { fprintf(stderr, "Failed to communicate.\n"); goto generate_out; @@ -279,7 +286,8 @@ 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) { + const char *input_file_name, const char *slot, char *password, + enum enum_pin_policy pin_policy) { int key = 0; FILE *input_file = NULL; EVP_PKEY *private_key = NULL; @@ -392,9 +400,17 @@ static bool import_key(ykpiv_state *state, enum enum_key_format key_format, } } + if(pin_policy != pin_policy__NULL) { + *in_ptr++ = YKPIV_PINPOLICY_TAG; + *in_ptr++ = 1; + *in_ptr++ = get_pin_policy(pin_policy); + } + if(ykpiv_transfer_data(state, templ, in_data, in_ptr - in_data, data, &recv_len, &sw) != YKPIV_OK) { return false; + } else if(pin_policy != pin_policy__NULL && sw == 0x6a80) { + fprintf(stderr, "Failed import. Maybe pin-policy is not supported on this key?\n"); } else if(sw != 0x9000) { fprintf(stderr, "Failed import command with code %x.\n", sw); } else { @@ -1657,7 +1673,8 @@ int main(int argc, char *argv[]) { print_version(state, args_info.output_arg); break; case action_arg_generate: - if(generate_key(state, args_info.slot_orig, args_info.algorithm_arg, args_info.output_arg, args_info.key_format_arg) == false) { + if(generate_key(state, args_info.slot_orig, args_info.algorithm_arg, args_info.output_arg, args_info.key_format_arg, + args_info.pin_policy_arg) == false) { ret = EXIT_FAILURE; } else { fprintf(stderr, "Successfully generated a new private key.\n"); @@ -1698,7 +1715,8 @@ 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) == false) { + if(import_key(state, args_info.key_format_arg, args_info.input_arg, args_info.slot_orig, args_info.password_arg, + args_info.pin_policy_arg) == false) { ret = EXIT_FAILURE; } else { fprintf(stderr, "Successfully imported a new private key.\n"); From ca6a355b5dd78b46edea9852440b27a098628524 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Thu, 25 Jun 2015 12:04:05 +0200 Subject: [PATCH 08/15] add touch tlv for generate and import-key --- lib/ykpiv.h | 4 ++++ tool/cmdline.ggo | 1 + tool/util.c | 12 ++++++++++++ tool/util.h | 1 + tool/yubico-piv-tool.c | 22 +++++++++++++++++----- 5 files changed, 35 insertions(+), 5 deletions(-) diff --git a/lib/ykpiv.h b/lib/ykpiv.h index a14b0e9..0edf065 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -152,6 +152,10 @@ extern "C" #define YKPIV_PINPOLICY_ONCE 2 #define YKPIV_PINPOLICY_ALWAYS 3 +#define YKPIV_TOUCHPOLICY_TAG 0xab +#define YKPIV_TOUCHPOLICY_NEVER 1 +#define YKPIV_TOUCHPOLICY_ALWAYS 2 + #define IS_ECKEY(a) ((a == YKPIV_ALGO_ECCP256 || a == YKPIV_ALGO_ECCP384)) #define IS_RSAKEY(a) ((a == YKPIV_ALGO_RSA1024 || a == YKPIV_ALGO_RSA2048)) diff --git a/tool/cmdline.ggo b/tool/cmdline.ggo index 50d346c..40aa65e 100644 --- a/tool/cmdline.ggo +++ b/tool/cmdline.ggo @@ -59,3 +59,4 @@ 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 "sign" - "Sign data" flag off hidden 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 or import-key" values="never","always" enum optional diff --git a/tool/util.c b/tool/util.c index 464b9ef..82e5126 100644 --- a/tool/util.c +++ b/tool/util.c @@ -418,3 +418,15 @@ unsigned char get_pin_policy(enum enum_pin_policy policy) { return 0; } } + +unsigned char get_touch_policy(enum enum_touch_policy policy) { + switch(policy) { + case touch_policy_arg_never: + return YKPIV_TOUCHPOLICY_NEVER; + case touch_policy_arg_always: + return YKPIV_TOUCHPOLICY_ALWAYS; + case touch_policy__NULL: + default: + return 0; + } +} diff --git a/tool/util.h b/tool/util.h index ea29d87..c7c9ccb 100644 --- a/tool/util.h +++ b/tool/util.h @@ -51,5 +51,6 @@ const EVP_MD *get_hash(enum enum_hash, const unsigned char**, size_t*); int get_hashnid(enum enum_hash, unsigned char); unsigned char get_piv_algorithm(enum enum_algorithm); unsigned char get_pin_policy(enum enum_pin_policy); +unsigned char get_touch_policy(enum enum_touch_policy); #endif diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 055cdbd..92301b1 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -86,8 +86,9 @@ static void print_version(ykpiv_state *state, const char *output_file_name) { static bool generate_key(ykpiv_state *state, const char *slot, enum enum_algorithm algorithm, const char *output_file_name, - enum enum_key_format key_format, enum enum_pin_policy pin_policy) { - unsigned char in_data[8]; + enum enum_key_format key_format, enum enum_pin_policy pin_policy, + enum enum_touch_policy touch_policy) { + unsigned char in_data[11]; unsigned char *in_ptr = in_data; unsigned char data[1024]; unsigned char templ[] = {0, YKPIV_INS_GENERATE_ASYMMERTRIC, 0, 0}; @@ -127,6 +128,12 @@ static bool generate_key(ykpiv_state *state, const char *slot, *in_ptr++ = 1; *in_ptr++ = get_pin_policy(pin_policy); } + if(touch_policy != touch_policy__NULL) { + in_data[1] += 3; + *in_ptr++ = YKPIV_TOUCHPOLICY_TAG; + *in_ptr++ = 1; + *in_ptr++ = get_touch_policy(touch_policy); + } if(ykpiv_transfer_data(state, templ, in_data, in_ptr - in_data, data, &recv_len, &sw) != YKPIV_OK) { fprintf(stderr, "Failed to communicate.\n"); @@ -287,7 +294,7 @@ 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_pin_policy pin_policy, enum enum_touch_policy touch_policy) { int key = 0; FILE *input_file = NULL; EVP_PKEY *private_key = NULL; @@ -405,6 +412,11 @@ static bool import_key(ykpiv_state *state, enum enum_key_format key_format, *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(ykpiv_transfer_data(state, templ, in_data, in_ptr - in_data, data, &recv_len, &sw) != YKPIV_OK) { @@ -1674,7 +1686,7 @@ int main(int argc, char *argv[]) { break; case action_arg_generate: if(generate_key(state, args_info.slot_orig, args_info.algorithm_arg, args_info.output_arg, args_info.key_format_arg, - args_info.pin_policy_arg) == false) { + args_info.pin_policy_arg, args_info.touch_policy_arg) == false) { ret = EXIT_FAILURE; } else { fprintf(stderr, "Successfully generated a new private key.\n"); @@ -1716,7 +1728,7 @@ 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, - args_info.pin_policy_arg) == false) { + args_info.pin_policy_arg, args_info.touch_policy_arg) == false) { ret = EXIT_FAILURE; } else { fprintf(stderr, "Successfully imported a new private key.\n"); From 600b302c1d720cfa717223bf59953e3f2013e565 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Thu, 25 Jun 2015 12:04:20 +0200 Subject: [PATCH 09/15] add touch for set-mgm-key --- configure.ac | 8 ++++---- lib/ykpiv.c | 12 +++++++++++- lib/ykpiv.h | 2 ++ lib/ykpiv.map | 6 ++++++ tool/cmdline.ggo | 2 +- tool/yubico-piv-tool.c | 2 +- 6 files changed, 25 insertions(+), 7 deletions(-) diff --git a/configure.ac b/configure.ac index 2cf4764..5ad286d 100644 --- a/configure.ac +++ b/configure.ac @@ -24,7 +24,7 @@ # non-source form of such a combination shall include the source code # for the parts of OpenSSL used as well as that of the covered work. -AC_INIT([yubico-piv-tool], [1.0.1]) +AC_INIT([yubico-piv-tool], [1.1.0]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) @@ -32,9 +32,9 @@ AC_CONFIG_MACRO_DIR([m4]) # Interfaces changed/added/removed: CURRENT++ REVISION=0 # Interfaces added: AGE++ # Interfaces removed: AGE=0 -AC_SUBST([LT_CURRENT], 1) -AC_SUBST([LT_REVISION], 6) -AC_SUBST([LT_AGE], 0) +AC_SUBST([LT_CURRENT], 2) +AC_SUBST([LT_REVISION], 0) +AC_SUBST([LT_AGE], 1) AM_INIT_AUTOMAKE([-Wall -Werror foreign]) AM_SILENT_RULES([yes]) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 89fbe55..bf7d4a0 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -427,6 +427,10 @@ ykpiv_rc ykpiv_authenticate(ykpiv_state *state, unsigned const char *key) { } ykpiv_rc ykpiv_set_mgmkey(ykpiv_state *state, const unsigned char *new_key) { + return ykpiv_set_mgmkey2(state, new_key, 0); +} + +ykpiv_rc ykpiv_set_mgmkey2(ykpiv_state *state, const unsigned char *new_key, const unsigned char touch) { APDU apdu; unsigned char data[0xff]; unsigned long recv_len = sizeof(data); @@ -453,7 +457,13 @@ ykpiv_rc ykpiv_set_mgmkey(ykpiv_state *state, const unsigned char *new_key) { memset(apdu.raw, 0, sizeof(apdu)); apdu.st.ins = YKPIV_INS_SET_MGMKEY; apdu.st.p1 = 0xff; - apdu.st.p2 = 0xff; + if(touch == 0) { + apdu.st.p2 = 0xff; + } else if(touch == 1) { + apdu.st.p2 = 0xfe; + } else { + return YKPIV_GENERIC_ERROR; + } apdu.st.lc = DES_KEY_SZ * 3 + 3; apdu.st.data[0] = YKPIV_ALGO_3DES; apdu.st.data[1] = YKPIV_KEY_CARDMGM; diff --git a/lib/ykpiv.h b/lib/ykpiv.h index 0edf065..4585d4c 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -70,6 +70,8 @@ extern "C" unsigned char *out_data, unsigned long *out_len, int *sw); ykpiv_rc ykpiv_authenticate(ykpiv_state *state, const unsigned char *key); ykpiv_rc ykpiv_set_mgmkey(ykpiv_state *state, const unsigned char *new_key); + ykpiv_rc ykpiv_set_mgmkey2(ykpiv_state *state, const unsigned char *new_key, + const unsigned char touch); ykpiv_rc ykpiv_hex_decode(const char *hex_in, size_t in_len, unsigned char *hex_out, size_t *out_len); ykpiv_rc ykpiv_sign_data(ykpiv_state *state, const unsigned char *sign_in, diff --git a/lib/ykpiv.map b/lib/ykpiv.map index 19969cc..0e262f4 100644 --- a/lib/ykpiv.map +++ b/lib/ykpiv.map @@ -53,3 +53,9 @@ YKPIV_0.2.0 global: ykpiv_decipher_data; } YKPIV_0.1.0; + +YKPIV_1.1.0 +{ +global: + ykpiv_set_mgmkey2; +} YKPIV_0.1.0; diff --git a/tool/cmdline.ggo b/tool/cmdline.ggo index 40aa65e..848db95 100644 --- a/tool/cmdline.ggo +++ b/tool/cmdline.ggo @@ -59,4 +59,4 @@ 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 "sign" - "Sign data" flag off hidden 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 or import-key" values="never","always" enum optional +option "touch-policy" - "Set touch policy for action generatem, import-key or set-mgm-key" values="never","always" enum optional diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 92301b1..443402a 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -1699,7 +1699,7 @@ int main(int argc, char *argv[]) { if(ykpiv_hex_decode(args_info.new_key_arg, strlen(args_info.new_key_arg), new_key, &new_key_len) != YKPIV_OK) { fprintf(stderr, "Failed decoding new key!\n"); ret = EXIT_FAILURE; - } else if(ykpiv_set_mgmkey(state, new_key) != YKPIV_OK) { + } else if(ykpiv_set_mgmkey2(state, new_key, args_info.touch_policy_arg == touch_policy_arg_always ? 1 : 0) != YKPIV_OK) { fprintf(stderr, "Failed setting the new key!\n"); ret = EXIT_FAILURE; } else { From 6f5870d884ea09cdf4ea72061bdfcde37da42921 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Thu, 25 Jun 2015 12:37:06 +0200 Subject: [PATCH 10/15] better errors for fail on pin-policy and touch-policy --- tool/yubico-piv-tool.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 443402a..7d3d463 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -143,7 +143,13 @@ static bool generate_key(ykpiv_state *state, const char *slot, if(sw == 0x6b00) { fprintf(stderr, "slot not supported?)\n"); } else if(sw == 0x6a80) { - fprintf(stderr, "algorithm not supported?)\n"); + if(pin_policy != pin_policy__NULL) { + fprintf(stderr, "pin policy not supported?)\n"); + } else if(touch_policy != touch_policy__NULL) { + fprintf(stderr, "touch policy not supported?)\n"); + } else { + fprintf(stderr, "algorithm not supported?)\n"); + } } else { fprintf(stderr, "error %x)\n", sw); } @@ -421,8 +427,15 @@ static bool import_key(ykpiv_state *state, enum enum_key_format key_format, if(ykpiv_transfer_data(state, templ, in_data, in_ptr - in_data, data, &recv_len, &sw) != YKPIV_OK) { return false; - } else if(pin_policy != pin_policy__NULL && sw == 0x6a80) { - fprintf(stderr, "Failed import. Maybe pin-policy is not supported on this key?\n"); + } 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 { @@ -1700,7 +1713,11 @@ int main(int argc, char *argv[]) { fprintf(stderr, "Failed decoding new key!\n"); ret = EXIT_FAILURE; } else if(ykpiv_set_mgmkey2(state, new_key, args_info.touch_policy_arg == touch_policy_arg_always ? 1 : 0) != YKPIV_OK) { - fprintf(stderr, "Failed setting the new key!\n"); + fprintf(stderr, "Failed setting the new key!"); + if(args_info.touch_policy_arg != touch_policy__NULL) { + fprintf(stderr, " Maybe touch policy is not supported on this key?"); + } + fprintf(stderr, "\n"); ret = EXIT_FAILURE; } else { fprintf(stderr, "Successfully set new management key.\n"); From 642891e2b30a84c0a25d04441df196af4a0b493e Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Fri, 26 Jun 2015 11:03:08 +0200 Subject: [PATCH 11/15] move padding for signing to ykpiv_sign_data() and only pad if in_len != key_len --- lib/ykpiv.c | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index bf7d4a0..7dec9e5 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -509,7 +509,7 @@ ykpiv_rc ykpiv_hex_decode(const char *hex_in, size_t in_len, } static ykpiv_rc _general_authenticate(ykpiv_state *state, - const unsigned char *raw_in, size_t in_len, + const unsigned char *sign_in, size_t in_len, unsigned char *out, size_t *out_len, unsigned char algorithm, unsigned char key, bool decipher) { unsigned char indata[1024]; @@ -517,7 +517,6 @@ static ykpiv_rc _general_authenticate(ykpiv_state *state, unsigned char data[1024]; unsigned char templ[] = {0, YKPIV_INS_AUTHENTICATE, algorithm, key}; unsigned long recv_len = sizeof(data); - unsigned char sign_in[256]; size_t key_len = 0; int sw; size_t bytes; @@ -531,17 +530,8 @@ static ykpiv_rc _general_authenticate(ykpiv_state *state, if(key_len == 0) { key_len = 256; } - if(!decipher) { - if(in_len + RSA_PKCS1_PADDING_SIZE > key_len) { - return YKPIV_SIZE_ERROR; - } - RSA_padding_add_PKCS1_type_1(sign_in, key_len, raw_in, in_len); - in_len = key_len; - } else { - if(in_len != key_len) { - return YKPIV_SIZE_ERROR; - } - memcpy(sign_in, raw_in, in_len); + if(in_len != key_len) { + return YKPIV_SIZE_ERROR; } break; case YKPIV_ALGO_ECCP256: @@ -555,7 +545,6 @@ static ykpiv_rc _general_authenticate(ykpiv_state *state, } else if(decipher && in_len != (key_len * 2) + 1) { return YKPIV_SIZE_ERROR; } - memcpy(sign_in, raw_in, in_len); break; default: return YKPIV_ALGORITHM_ERROR; @@ -624,7 +613,24 @@ ykpiv_rc ykpiv_sign_data(ykpiv_state *state, unsigned char *sign_out, size_t *out_len, unsigned char algorithm, unsigned char key) { - return _general_authenticate(state, raw_in, in_len, sign_out, out_len, + unsigned char sign_in[256]; + size_t key_len = 0; + if(IS_RSAKEY(algorithm)) { + key_len = 128; + if(algorithm == YKPIV_ALGO_RSA2048) { + key_len = 256; + } + } + if(IS_RSAKEY(algorithm) && key_len != in_len) { + if(in_len + RSA_PKCS1_PADDING_SIZE > key_len) { + return YKPIV_SIZE_ERROR; + } + RSA_padding_add_PKCS1_type_1(sign_in, key_len, raw_in, in_len); + in_len = key_len; + } else { + memcpy(sign_in, raw_in, in_len); + } + return _general_authenticate(state, sign_in, in_len, sign_out, out_len, algorithm, key, false); } From f43c5781b90d0ebc87683e10dc36a9da83de8ea0 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Fri, 26 Jun 2015 13:00:21 +0200 Subject: [PATCH 12/15] fix indentation --- tool/yubico-piv-tool.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 7d3d463..601dcf6 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -686,8 +686,8 @@ static bool request_certificate(ykpiv_state *state, enum enum_key_format key_for signinput = digest; len = oid_len + digest_len; } else { - signinput = digest + oid_len; - len = digest_len; + signinput = digest + oid_len; + len = digest_len; } req->sig_alg->algorithm = OBJ_nid2obj(nid); From 80e6fe525ab494e6773795f1bd50b9a3f0dc09d9 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 30 Jun 2015 07:33:39 +0200 Subject: [PATCH 13/15] change IS_XXKEY macros to be YKPIV_IS_XX --- lib/ykpiv.c | 6 +++--- lib/ykpiv.h | 4 ++-- tool/yubico-piv-tool.c | 16 ++++++++-------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 7dec9e5..5897c2c 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -562,7 +562,7 @@ static ykpiv_rc _general_authenticate(ykpiv_state *state, dataptr += set_length(dataptr, in_len + bytes + 3); *dataptr++ = 0x82; *dataptr++ = 0x00; - *dataptr++ = IS_ECKEY(algorithm) && decipher ? 0x85 : 0x81; + *dataptr++ = YKPIV_IS_EC(algorithm) && decipher ? 0x85 : 0x81; dataptr += set_length(dataptr, in_len); memcpy(dataptr, sign_in, (size_t)in_len); dataptr += in_len; @@ -615,13 +615,13 @@ ykpiv_rc ykpiv_sign_data(ykpiv_state *state, unsigned char sign_in[256]; size_t key_len = 0; - if(IS_RSAKEY(algorithm)) { + if(YKPIV_IS_RSA(algorithm)) { key_len = 128; if(algorithm == YKPIV_ALGO_RSA2048) { key_len = 256; } } - if(IS_RSAKEY(algorithm) && key_len != in_len) { + if(YKPIV_IS_RSA(algorithm) && key_len != in_len) { if(in_len + RSA_PKCS1_PADDING_SIZE > key_len) { return YKPIV_SIZE_ERROR; } diff --git a/lib/ykpiv.h b/lib/ykpiv.h index 4585d4c..0570b9b 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -158,8 +158,8 @@ extern "C" #define YKPIV_TOUCHPOLICY_NEVER 1 #define YKPIV_TOUCHPOLICY_ALWAYS 2 -#define IS_ECKEY(a) ((a == YKPIV_ALGO_ECCP256 || a == YKPIV_ALGO_ECCP384)) -#define IS_RSAKEY(a) ((a == YKPIV_ALGO_RSA1024 || a == YKPIV_ALGO_RSA2048)) +#define YKPIV_IS_EC(a) ((a == YKPIV_ALGO_ECCP256 || a == YKPIV_ALGO_ECCP384)) +#define YKPIV_IS_RSA(a) ((a == YKPIV_ALGO_RSA1024 || a == YKPIV_ALGO_RSA2048)) #ifdef __cplusplus } diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 601dcf6..233dd51 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -353,7 +353,7 @@ static bool import_key(ykpiv_state *state, enum enum_key_format key_format, unsigned char *in_ptr = in_data; unsigned char templ[] = {0, YKPIV_INS_IMPORT_KEY, algorithm, key}; int sw; - if(IS_RSAKEY(algorithm)) { + if(YKPIV_IS_RSA(algorithm)) { RSA *rsa_private_key = EVP_PKEY_get1_RSA(private_key); unsigned char e[4]; unsigned char *e_ptr = e; @@ -397,7 +397,7 @@ static bool import_key(ykpiv_state *state, enum enum_key_format key_format, fprintf(stderr, "Failed setting iqmp component.\n"); goto import_out; } - } else if(IS_ECKEY(algorithm)) { + } 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; @@ -682,7 +682,7 @@ static bool request_certificate(ykpiv_state *state, enum enum_key_format key_for fprintf(stderr, "Unsupported algorithm %x or hash %x\n", algorithm, hash); goto request_out; } - if(IS_RSAKEY(algorithm)) { + if(YKPIV_IS_RSA(algorithm)) { signinput = digest; len = oid_len + digest_len; } else { @@ -827,7 +827,7 @@ static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_fo if(nid == 0) { goto selfsign_out; } - if(IS_RSAKEY(algorithm)) { + if(YKPIV_IS_RSA(algorithm)) { signinput = digest; len = oid_len + md_len; } else { @@ -1090,7 +1090,7 @@ static bool sign_file(ykpiv_state *state, const char *input, const char *output, EVP_MD_CTX_destroy(mdctx); } - if(IS_RSAKEY(algo)) { + if(YKPIV_IS_RSA(algo)) { prepare_rsa_signature(hashed, hash_len, hashed, &hash_len, EVP_MD_type(md)); } @@ -1342,7 +1342,7 @@ static bool test_signature(ykpiv_state *state, enum enum_slot slot, goto test_out; } sscanf(cmdline_parser_slot_values[slot], "%2x", &key); - if(IS_RSAKEY(algorithm)) { + if(YKPIV_IS_RSA(algorithm)) { prepare_rsa_signature(data, data_len, encoded, &enc_len, EVP_MD_type(md)); ptr = encoded; } else { @@ -1448,7 +1448,7 @@ static bool test_decipher(ykpiv_state *state, enum enum_slot slot, goto decipher_out; } sscanf(cmdline_parser_slot_values[slot], "%2x", &key); - if(IS_RSAKEY(algorithm)) { + if(YKPIV_IS_RSA(algorithm)) { unsigned char secret[32]; unsigned char secret2[32]; unsigned char data[256]; @@ -1489,7 +1489,7 @@ static bool test_decipher(ykpiv_state *state, enum enum_slot slot, } else { fprintf(stderr, "Failed unwrapping PKCS1 envelope.\n"); } - } else if(IS_ECKEY(algorithm)) { + } else if(YKPIV_IS_EC(algorithm)) { unsigned char secret[48]; unsigned char secret2[48]; unsigned char public_key[97]; From a775ac6e697f8294428cfd07d0119854742ea298 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 30 Jun 2015 07:46:21 +0200 Subject: [PATCH 14/15] move around texts for help --- tool/cmdline.ggo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tool/cmdline.ggo b/tool/cmdline.ggo index 848db95..a530326 100644 --- a/tool/cmdline.ggo +++ b/tool/cmdline.ggo @@ -57,6 +57,6 @@ text " /CN=host.example.com/OU=test/O=example.com/\n" 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 "sign" - "Sign data" flag off hidden 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 generatem, import-key or set-mgm-key" values="never","always" enum optional +option "touch-policy" - "Set touch policy for action generate, import-key or set-mgm-key" values="never","always" enum optional +option "sign" - "Sign data" flag off hidden From f9ef0d6bbd7e0618b0a3fb52716100b9fd9a2601 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Fri, 3 Jul 2015 09:41:42 +0200 Subject: [PATCH 15/15] NEWS for yk4beta --- NEWS | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 17a4cb7..7d3c344 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ yubico-piv-tool NEWS -- History of user-visible changes. -*- outline -*- -* Version 1.0.1 (unreleased) +* Version 1.1.0 (unreleased) + +** Support for YubiKey 4 stuff +ECCP384, touch, retired keys etc * Version 1.0.0 (released 2015-06-23)