From 6042a2140eda42ee642904ac25b708edc85828b7 Mon Sep 17 00:00:00 2001 From: Mikhail Denisenko Date: Thu, 10 Dec 2015 17:31:22 -0500 Subject: [PATCH] Implemented C_SetPIN --- lib/ykpiv.c | 62 ++++++++++++++++++++++++++++++++++++++++++ lib/ykpiv.h | 10 +++++++ lib/ykpiv.map | 8 ++++++ tool/yubico-piv-tool.c | 48 ++++++++++++++------------------ ykcs11/token_vendors.c | 1 + ykcs11/token_vendors.h | 2 ++ ykcs11/ykcs11.c | 32 +++++++++++++++++++++- ykcs11/yubico_token.c | 22 +++++++++++++++ ykcs11/yubico_token.h | 2 ++ 9 files changed, 159 insertions(+), 28 deletions(-) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 7936ee2..25ebb5d 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -716,6 +716,68 @@ ykpiv_rc ykpiv_verify(ykpiv_state *state, const char *pin, int *tries) { } } +#define CHREF_ACT_CHANGE_PIN 0 +#define CHREF_ACT_UNBLOCK_PIN 1 +#define CHREF_ACT_CHANGE_PUK 2 + +static ykpiv_rc _change_pin_internal(ykpiv_state *state, int action, const char * current_pin, size_t current_pin_len, const char * new_pin, size_t new_pin_len, int *tries) { + int sw; + unsigned char templ[] = {0, YKPIV_INS_CHANGE_REFERENCE, 0, 0x80}; + unsigned char indata[0x10]; + unsigned char data[0xff]; + unsigned long recv_len = sizeof(data); + ykpiv_rc res; + if (current_pin_len > 8) { + return YKPIV_SIZE_ERROR; + } + if (new_pin_len > 8) { + return YKPIV_SIZE_ERROR; + } + if(action == CHREF_ACT_UNBLOCK_PIN) { + templ[1] = YKPIV_INS_RESET_RETRY; + } + else if(action == CHREF_ACT_CHANGE_PUK) { + templ[3] = 0x81; + } + memcpy(indata, current_pin, current_pin_len); + if(current_pin_len < 8) { + memset(indata + current_pin_len, 0xff, 8 - current_pin_len); + } + memcpy(indata + 8, new_pin, new_pin_len); + if(new_pin_len < 8) { + memset(indata + 8 + new_pin_len, 0xff, 8 - new_pin_len); + } + res = ykpiv_transfer_data(state, templ, indata, sizeof(indata), data, &recv_len, &sw); + if(res != YKPIV_OK) { + return res; + } else if(sw != 0x9000) { + if((sw >> 8) == 0x63) { + *tries = sw & 0xf; + return YKPIV_WRONG_PIN; + } else if(sw == 0x6983) { + return YKPIV_PIN_LOCKED; + } else { + if(state->verbose) { + fprintf(stderr, "Failed changing pin, token response code: %x.\n", sw); + } + return YKPIV_GENERIC_ERROR; + } + } + return YKPIV_OK; +} + +ykpiv_rc ykpiv_change_pin(ykpiv_state *state, const char * current_pin, size_t current_pin_len, const char * new_pin, size_t new_pin_len, int *tries) { + return _change_pin_internal(state, CHREF_ACT_CHANGE_PIN, current_pin, current_pin_len, new_pin, new_pin_len, tries); +} + +ykpiv_rc ykpiv_change_puk(ykpiv_state *state, const char * current_puk, size_t current_puk_len, const char * new_puk, size_t new_puk_len, int *tries) { + return _change_pin_internal(state, CHREF_ACT_CHANGE_PUK, current_puk, current_puk_len, new_puk, new_puk_len, tries); +} + +ykpiv_rc ykpiv_unblock_pin(ykpiv_state *state, const char * puk, size_t puk_len, const char * new_pin, size_t new_pin_len, int *tries) { + return _change_pin_internal(state, CHREF_ACT_CHANGE_PUK, puk, puk_len, new_pin, new_pin_len, tries); +} + ykpiv_rc ykpiv_fetch_object(ykpiv_state *state, int object_id, unsigned char *data, unsigned long *len) { int sw; diff --git a/lib/ykpiv.h b/lib/ykpiv.h index a868966..a89a827 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -57,6 +57,7 @@ extern "C" YKPIV_WRONG_PIN = -10, YKPIV_INVALID_OBJECT = -11, YKPIV_ALGORITHM_ERROR = -12, + YKPIV_PIN_LOCKED = -13, } ykpiv_rc; const char *ykpiv_strerror(ykpiv_rc err); @@ -85,6 +86,15 @@ extern "C" unsigned char algorithm, unsigned char key); ykpiv_rc ykpiv_get_version(ykpiv_state *state, char *version, size_t len); ykpiv_rc ykpiv_verify(ykpiv_state *state, const char *pin, int *tries); + ykpiv_rc ykpiv_change_pin(ykpiv_state *state, const char * current_pin, size_t current_pin_len, + const char * new_pin, size_t new_pin_len, + int *tries); + ykpiv_rc ykpiv_change_puk(ykpiv_state *state, const char * current_puk, size_t current_puk_len, + const char * new_puk, size_t new_puk_len, + int *tries); + ykpiv_rc ykpiv_unblock_pin(ykpiv_state *state, const char * puk, size_t puk_len, + const char * new_pin, size_t new_pin_len, + 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, diff --git a/lib/ykpiv.map b/lib/ykpiv.map index fb2efb1..309cbf3 100644 --- a/lib/ykpiv.map +++ b/lib/ykpiv.map @@ -62,3 +62,11 @@ global: ykpiv_list_readers; ykpiv_import_private_key; } YKPIV_0.1.0; + +YKPIV_1.2.1 +{ +global: + ykpiv_change_pin; + ykpiv_change_puk; + ykpiv_unblock_pin; +} YKPIV_1.1.0; diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index d2b3383..0c00a59 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -969,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)) { @@ -1002,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, 8 - 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) { diff --git a/ykcs11/token_vendors.c b/ykcs11/token_vendors.c index aa50ad2..9cda842 100644 --- a/ykcs11/token_vendors.c +++ b/ykcs11/token_vendors.c @@ -290,6 +290,7 @@ token_vendor_t get_token_vendor(vendor_id_t vid) { v.token_import_cert = COMMON_token_import_cert; v.token_import_private_key = COMMON_token_import_private_key; v.token_delete_cert = COMMON_token_delete_cert; + v.token_change_pin = YUBICO_token_change_pin; break; case UNKNOWN: diff --git a/ykcs11/token_vendors.h b/ykcs11/token_vendors.h index a9be63d..528b2c5 100644 --- a/ykcs11/token_vendors.h +++ b/ykcs11/token_vendors.h @@ -18,6 +18,7 @@ typedef CK_RV (*get_t_mechanism_info_f)(CK_MECHANISM_TYPE, CK_MECHANISM_INFO_PTR typedef CK_RV (*get_t_objects_num_f)(ykpiv_state *, CK_ULONG_PTR, CK_ULONG_PTR); typedef CK_RV (*get_t_object_list_f)(ykpiv_state *, piv_obj_id_t *, CK_ULONG); typedef CK_RV (*get_t_raw_certificate_f)(ykpiv_state *, piv_obj_id_t, CK_BYTE_PTR, CK_ULONG_PTR); +typedef CK_RV (*t_change_pin_f)(ykpiv_state *, CK_USER_TYPE, CK_UTF8CHAR_PTR, CK_ULONG, CK_UTF8CHAR_PTR, CK_ULONG); // Common token functions below typedef CK_RV (*t_login_f)(ykpiv_state *, CK_USER_TYPE, CK_UTF8CHAR_PTR, CK_ULONG); @@ -53,6 +54,7 @@ typedef struct { t_import_cert_f token_import_cert; t_import_private_key_f token_import_private_key; t_delete_cert_f token_delete_cert; + t_change_pin_f token_change_pin; } token_vendor_t; token_vendor_t get_token_vendor(vendor_id_t vid); diff --git a/ykcs11/ykcs11.c b/ykcs11/ykcs11.c index e0e4497..23e290d 100644 --- a/ykcs11/ykcs11.c +++ b/ykcs11/ykcs11.c @@ -421,7 +421,36 @@ CK_DEFINE_FUNCTION(CK_RV, C_SetPIN)( ) { DIN; - DBG("TODO!!!"); + CK_RV rv; + token_vendor_t token; + + if (piv_state == NULL) { + DBG("libykpiv is not initialized or already finalized"); + return CKR_CRYPTOKI_NOT_INITIALIZED; + } + + if (session.handle == CK_INVALID_HANDLE) { + DBG("User called SetPIN on closed session"); + return CKR_SESSION_CLOSED; + } + + if (hSession != YKCS11_SESSION_ID) { + DBG("Unknown session %lu", hSession); + return CKR_SESSION_HANDLE_INVALID; + } + + CK_USER_TYPE user_type = CKU_USER; + if (session.info.state == CKS_RW_SO_FUNCTIONS) { + user_type = CKU_SO; + } + + token = get_token_vendor(session.slot->token->vid); + rv = token.token_change_pin(piv_state, user_type, pOldPin, ulOldLen, pNewPin, ulNewLen); + if (rv != CKR_OK) { + DBG("Pin change failed %lx", rv); + return rv; + } + DOUT; return CKR_OK; } @@ -979,6 +1008,7 @@ CK_DEFINE_FUNCTION(CK_RV, C_CreateObject)( DBG("Unable to store certificate data"); return CKR_FUNCTION_FAILED; } + *phObject = cert_id; break; diff --git a/ykcs11/yubico_token.c b/ykcs11/yubico_token.c index 4e89c85..a85a26a 100644 --- a/ykcs11/yubico_token.c +++ b/ykcs11/yubico_token.c @@ -334,3 +334,25 @@ CK_RV YUBICO_get_token_raw_certificate(ykpiv_state *state, piv_obj_id_t obj, CK_ return CKR_OK; } + +CK_RV YUBICO_token_change_pin(ykpiv_state *state, CK_USER_TYPE user_type, CK_UTF8CHAR_PTR pOldPin, CK_ULONG ulOldLen, CK_UTF8CHAR_PTR pNewPin, CK_ULONG ulNewLen) { + int tries; + ykpiv_rc res; + if (user_type != CKU_USER) { + DBG("TODO implement other users pin change"); + return CKR_FUNCTION_FAILED; + } + res = ykpiv_change_pin(state, pOldPin, ulOldLen, pNewPin, ulNewLen, &tries); + switch (res) { + case YKPIV_OK: + return CKR_OK; + case YKPIV_SIZE_ERROR: + return CKR_PIN_LEN_RANGE; + case YKPIV_WRONG_PIN: + return CKR_PIN_INCORRECT; + case YKPIV_PIN_LOCKED: + return CKR_PIN_LOCKED; + default: + return CKR_FUNCTION_FAILED; + } +} diff --git a/ykcs11/yubico_token.h b/ykcs11/yubico_token.h index b725123..37f90a2 100644 --- a/ykcs11/yubico_token.h +++ b/ykcs11/yubico_token.h @@ -17,5 +17,7 @@ CK_RV YUBICO_get_token_mechanism_info(CK_MECHANISM_TYPE mec, CK_MECHANISM_INFO_P CK_RV YUBICO_get_token_objects_num(ykpiv_state *state, CK_ULONG_PTR num, CK_ULONG_PTR num_certs); CK_RV YUBICO_get_token_object_list(ykpiv_state *state, piv_obj_id_t *obj, CK_ULONG num); CK_RV YUBICO_get_token_raw_certificate(ykpiv_state *state, piv_obj_id_t obj, CK_BYTE_PTR data, CK_ULONG_PTR len); +CK_RV YUBICO_token_change_pin(ykpiv_state *state, CK_USER_TYPE user_type, CK_UTF8CHAR_PTR pOldPin, CK_ULONG ulOldLen, + CK_UTF8CHAR_PTR pNewPin, CK_ULONG ulNewLen); #endif