diff --git a/NEWS b/NEWS index d0818a1..24f2f82 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ yubico-piv-tool NEWS -- History of user-visible changes. -*- outline -*- -* Version 1.0.2 (unreleased) +* Version 1.1.0 (unreleased) + +** Support for YubiKey 4 stuff +ECCP384, touch, retired keys etc * Version 1.0.1 (released 2015-07-10) diff --git a/configure.ac b/configure.ac index ec89a0d..12c68f0 100644 --- a/configure.ac +++ b/configure.ac @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -AC_INIT([yubico-piv-tool], [1.0.2]) +AC_INIT([yubico-piv-tool], [1.1.0]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) @@ -34,9 +34,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], 7) -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 ffa6fcd..6452363 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -452,6 +452,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); @@ -478,7 +482,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; @@ -524,7 +534,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, bool padding) { unsigned char indata[1024]; @@ -532,63 +542,37 @@ 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 pad_len = 0; + size_t key_len = 0; int sw; size_t bytes; size_t len = 0; ykpiv_rc res; switch(algorithm) { - case YKPIV_ALGO_RSA1024: - pad_len = 128; - - case YKPIV_ALGO_RSA2048: - if(pad_len == 0) { - pad_len = 256; - } - if(!decipher) { - // Signature - if (padding) { - // Padding required - if(in_len + RSA_PKCS1_PADDING_SIZE > pad_len) { - return YKPIV_SIZE_ERROR; - } - - // Add padding and copy data - RSA_padding_add_PKCS1_type_1(sign_in, pad_len, raw_in, in_len); - in_len = pad_len; + case YKPIV_ALGO_RSA1024: + key_len = 128; + case YKPIV_ALGO_RSA2048: + if(key_len == 0) { + key_len = 256; } - else { - // No padding required - if (in_len != pad_len) - return YKPIV_SIZE_ERROR; - - // Just copy data - memcpy(sign_in, raw_in, in_len); + if(in_len != key_len) { + return YKPIV_SIZE_ERROR; } - } - else { - // Decryption - if(in_len != pad_len) { - return YKPIV_SIZE_ERROR; + break; + case YKPIV_ALGO_ECCP256: + key_len = 32; + case YKPIV_ALGO_ECCP384: + if(key_len == 0) { + key_len = 48; } - - // Just copy data - memcpy(sign_in, raw_in, in_len); - } - break; - - case YKPIV_ALGO_ECCP256: - if(!decipher && in_len > 32) { - return YKPIV_SIZE_ERROR; - } else if(decipher && in_len != 65) { - return YKPIV_SIZE_ERROR; - } - memcpy(sign_in, raw_in, in_len); - break; - default: - return YKPIV_ALGORITHM_ERROR; + if(!decipher && in_len > key_len) { + return YKPIV_SIZE_ERROR; + } else if(decipher && in_len != (key_len * 2) + 1) { + return YKPIV_SIZE_ERROR; + } + break; + default: + return YKPIV_ALGORITHM_ERROR; } if(in_len < 0x80) { @@ -603,7 +587,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++ = YKPIV_IS_EC(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 b581fa1..3de24c4 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -88,6 +88,8 @@ extern "C" ykpiv_rc ykpiv_verify(ykpiv_state *state, const char *pin, int *tries); ykpiv_rc ykpiv_fetch_object(ykpiv_state *state, int object_id, unsigned char *data, unsigned long *len); + ykpiv_rc ykpiv_set_mgmkey2(ykpiv_state *state, const unsigned char *new_key, + const unsigned char touch); ykpiv_rc ykpiv_save_object(ykpiv_state *state, int object_id, unsigned char *indata, size_t len); @@ -95,6 +97,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 @@ -116,6 +119,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 @@ -131,6 +155,18 @@ 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 YKPIV_TOUCHPOLICY_TAG 0xab +#define YKPIV_TOUCHPOLICY_NEVER 1 +#define YKPIV_TOUCHPOLICY_ALWAYS 2 + +#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 } #endif diff --git a/lib/ykpiv.map b/lib/ykpiv.map index c4a5888..0a09fa6 100644 --- a/lib/ykpiv.map +++ b/lib/ykpiv.map @@ -56,3 +56,9 @@ global: ykpiv_connect2; ykpiv_sign_data2; } 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 ec984ff..d777748 100644 --- a/tool/cmdline.ggo +++ b/tool/cmdline.ggo @@ -36,14 +36,15 @@ 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" -option "algorithm" A "What algorithm to use" values="RSA1024","RSA2048","ECCP256" enum optional default="RSA2048" -option "hash" H "Hash to use for signatures" values="SHA1","SHA256","SHA512" enum optional default="SHA256" + 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 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" @@ -57,4 +58,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 "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 "sign" - "Sign data" flag off hidden diff --git a/tool/util.c b/tool/util.c index c497dcf..17c4b12 100644 --- a/tool/util.c +++ b/tool/util.c @@ -82,6 +82,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; @@ -200,6 +202,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; @@ -284,3 +346,134 @@ bool read_pw(const char *name, char *pwbuf, size_t pwbuflen, int verify) { } 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; + } +} + +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; + } +} + +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 4f3c65e..f6d984d 100644 --- a/tool/util.h +++ b/tool/util.h @@ -50,5 +50,10 @@ bool set_component_with_len(unsigned char**, const BIGNUM*, int); bool prepare_rsa_signature(const unsigned char*, unsigned int, unsigned char*, unsigned int*, int); bool read_pw(const char*, char*, size_t, 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); +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 a1b278d..1fcbb25 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -63,20 +63,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 @@ -100,8 +87,10 @@ 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, + 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}; unsigned long recv_len = sizeof(data); @@ -124,31 +113,46 @@ 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; - 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__NULL: - default: - fprintf(stderr, "Unexepcted algorithm.\n"); - goto generate_out; + *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(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"); 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) { + 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); + } goto generate_out; } @@ -187,24 +191,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; } @@ -283,7 +297,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, enum enum_touch_policy touch_policy) { int key = 0; FILE *input_file = NULL; EVP_PKEY *private_key = NULL; @@ -336,7 +351,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(YKPIV_IS_RSA(algorithm)) { RSA *rsa_private_key = EVP_PKEY_get1_RSA(private_key); unsigned char e[4]; unsigned char *e_ptr = e; @@ -380,20 +395,45 @@ 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(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, 32) == false) { + if(set_component_with_len(&in_ptr, s, element_len) == false) { fprintf(stderr, "Failed setting ec private key.\n"); goto import_out; } } + 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(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 { @@ -559,7 +599,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; @@ -597,25 +637,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); @@ -651,48 +675,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: - 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; + nid = get_hashnid(hash, algorithm); + if(nid == 0) { + fprintf(stderr, "Unsupported algorithm %x or hash %x\n", algorithm, hash); + goto request_out; } + if(YKPIV_IS_RSA(algorithm)) { + signinput = digest; + len = oid_len + digest_len; + } else { + signinput = digest + oid_len; + len = digest_len; + } + req->sig_alg->algorithm = OBJ_nid2obj(nid); { unsigned char signature[1024]; @@ -743,7 +738,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; @@ -781,27 +776,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; @@ -843,48 +821,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: - 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(YKPIV_IS_RSA(algorithm)) { + 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); @@ -1138,37 +1086,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(); @@ -1187,7 +1115,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(YKPIV_IS_RSA(algo)) { prepare_rsa_signature(hashed, hash_len, hashed, &hash_len, EVP_MD_type(md)); } @@ -1268,6 +1196,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"); } @@ -1328,19 +1259,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"); @@ -1404,19 +1325,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; } { @@ -1456,7 +1367,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(YKPIV_IS_RSA(algorithm)) { prepare_rsa_signature(data, data_len, encoded, &enc_len, EVP_MD_type(md)); ptr = encoded; } else { @@ -1490,6 +1401,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) { @@ -1561,7 +1473,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(YKPIV_IS_RSA(algorithm)) { unsigned char secret[32]; unsigned char secret2[32]; unsigned char data[256]; @@ -1602,20 +1514,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(YKPIV_IS_EC(algorithm)) { + 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; } @@ -1625,7 +1547,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 { @@ -1786,7 +1708,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, args_info.touch_policy_arg) == false) { ret = EXIT_FAILURE; } else { fprintf(stderr, "Successfully generated a new private key.\n"); @@ -1807,8 +1730,12 @@ int main(int argc, char *argv[]) { if(ykpiv_hex_decode(new_mgm_key, strlen(new_mgm_key), 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) { - fprintf(stderr, "Failed setting the new key!\n"); + } 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!"); + 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"); @@ -1835,7 +1762,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, args_info.touch_policy_arg) == false) { ret = EXIT_FAILURE; } else { fprintf(stderr, "Successfully imported a new private key.\n");