Backport minidriver changes

* Port ykpiv_auth_getchallenge and ykpiv_auth_verifyresponse
 - Commit 8fde607b50b19c57a662c53c6b276b54a78606d8
 - Commit 6046b98e477cfef59a590ce2177336d694813e7e
 - Commit 422cea11745dc67d15039e242ed21ecb5208ae55
 - Commit 1d31647e5a27bd2df6bda76512c7d673980f0bec
* Rename connect2() and done2() to connect_with_external_card(), etc.
* Select applet in ykpiv_change_pin, change_puk, and unblock_pin
This commit is contained in:
Trevor Bentley
2017-10-10 15:36:28 +02:00
parent ef81054dc2
commit f903a432e3
3 changed files with 151 additions and 37 deletions
+4
View File
@@ -795,6 +795,10 @@ ykpiv_rc ykpiv_util_generate_key(ykpiv_state *state, uint8_t slot, uint8_t algor
}
}
}
else if (sw == SW_ERR_SECURITY_STATUS) {
res = YKPIV_AUTHENTICATION_ERROR;
if (state->verbose) { fprintf(stderr, "not authenticated)\n"); }
}
else {
res = YKPIV_GENERIC_ERROR;
if (state->verbose) { fprintf(stderr, "error %x)\n", sw); }
+145 -33
View File
@@ -167,13 +167,22 @@ ykpiv_rc ykpiv_init(ykpiv_state **state, int verbose) {
return ykpiv_init_with_allocator(state, verbose, &_default_allocator);
}
ykpiv_rc ykpiv_done(ykpiv_state *state) {
ykpiv_disconnect(state);
static ykpiv_rc _done_internal(ykpiv_state *state, bool disconnect) {
if (disconnect)
ykpiv_disconnect(state);
_cache_pin(state, NULL, 0);
_ykpiv_free(state, state);
return YKPIV_OK;
}
ykpiv_rc ykpiv_done_with_external_card(ykpiv_state *state) {
return _done_internal(state, false);
}
ykpiv_rc ykpiv_done(ykpiv_state *state) {
return _done_internal(state, true);
}
ykpiv_rc ykpiv_disconnect(ykpiv_state *state) {
if(state->card) {
SCardDisconnect(state->card, SCARD_RESET_CARD);
@@ -265,15 +274,19 @@ static ykpiv_rc _connect_internal(ykpiv_state *state, uint64_t context, uint64_t
state->context = context;
state->card = card;
// transact the connect operation
if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR;
res = _ykpiv_ensure_application_selected(state);
_ykpiv_end_transaction(state);
/*
** Do not select the applet here, as we need to accommodate commands that are
** sensitive to re-select (custom apdu/auth). All commands that can handle explicit
** selection already check the applet state and select accordingly anyway.
** ykpiv_verify_select is supplied for those who want to select explicitly.
**
** The applet _is_ selected by ykpiv_connect(), but is not selected when bypassing
** it with ykpiv_connect_with_external_card().
*/
return res;
}
ykpiv_rc ykpiv_connect_with_card(ykpiv_state *state, uint64_t context, uint64_t card) {
ykpiv_rc ykpiv_connect_with_external_card(ykpiv_state *state, uint64_t context, uint64_t card, bool select) {
return _connect_internal(state, context, card);
}
@@ -313,8 +326,16 @@ ykpiv_rc ykpiv_connect(ykpiv_state *state, const char *wanted) {
}
// at this point, card should not equal state->card, to allow _connect_internal() to determine device type
if (!_connect_internal(state, state->context, card)) {
return YKPIV_OK;
if (YKPIV_OK == _connect_internal(state, state->context, card)) {
/*
* Select applet. This is done here instead of in _connect_internal() because
* you may not want to select the applet when connecting to a card handle that
* was supplied by an external library.
*/
if (YKPIV_OK != (ret = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR;
ret = _ykpiv_ensure_application_selected(state);
_ykpiv_end_transaction(state);
return ret;
}
}
@@ -926,7 +947,6 @@ ykpiv_rc ykpiv_verify(ykpiv_state *state, const char *pin, int *tries) {
if(len > 8) {
return YKPIV_SIZE_ERROR;
}
memset(apdu.raw, 0, sizeof(apdu.raw));
apdu.st.ins = YKPIV_INS_VERIFY;
apdu.st.p1 = 0x00;
@@ -957,6 +977,19 @@ ykpiv_rc ykpiv_verify(ykpiv_state *state, const char *pin, int *tries) {
}
}
ykpiv_rc ykpiv_verify_select(ykpiv_state *state, const char *pin, const size_t pin_len, int *tries, bool force_select) {
ykpiv_rc res = YKPIV_OK;
if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) goto Cleanup;
if (force_select) {
if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup;
}
res = ykpiv_verify(state, pin, tries);
Cleanup:
_ykpiv_end_transaction(state);
return res;
}
ykpiv_rc ykpiv_get_pin_retries(ykpiv_state *state, int* tries) {
ykpiv_rc res;
ykpiv_rc ykrc;
@@ -1054,21 +1087,47 @@ static ykpiv_rc change_pin_internal(ykpiv_state *state, int action, const char *
}
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 res = change_pin_internal(state, CHREF_ACT_CHANGE_PIN, current_pin, current_pin_len, new_pin, new_pin_len, tries);
ykpiv_rc res = YKPIV_GENERIC_ERROR;
if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR;
if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup;
res = change_pin_internal(state, CHREF_ACT_CHANGE_PIN, current_pin, current_pin_len, new_pin, new_pin_len, tries);
if (res == YKPIV_OK && new_pin != NULL) {
// Intentionally ignore errors. If the PIN fails to save, it will only
// be a problem if a reconnect is attempted. Failure deferred until then.
_cache_pin(state, new_pin, new_pin_len + 1);
}
Cleanup:
_ykpiv_end_transaction(state);
return res;
}
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 res = YKPIV_GENERIC_ERROR;
if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR;
if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup;
res = change_pin_internal(state, CHREF_ACT_CHANGE_PUK, current_puk, current_puk_len, new_puk, new_puk_len, tries);
Cleanup:
_ykpiv_end_transaction(state);
return res;
}
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_UNBLOCK_PIN, puk, puk_len, new_pin, new_pin_len, tries);
ykpiv_rc res = YKPIV_GENERIC_ERROR;
if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR;
if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup;
res = change_pin_internal(state, CHREF_ACT_UNBLOCK_PIN, puk, puk_len, new_pin, new_pin_len, tries);
Cleanup:
_ykpiv_end_transaction(state);
return res;
}
ykpiv_rc ykpiv_fetch_object(ykpiv_state *state, int object_id,
@@ -1295,27 +1354,80 @@ ykpiv_rc ykpiv_attest(ykpiv_state *state, const unsigned char key, unsigned char
}
// TREV TODO: remove these, fix minidriver
ykpiv_rc ykpiv_done2(ykpiv_state *state, bool disconnect) {
// TODO: why is this needed? windows unit tests pass without it
if (disconnect)
ykpiv_disconnect(state);
_cache_pin(state, NULL, 0);
_ykpiv_free(state, state);
return YKPIV_OK;
}
ykpiv_rc ykpiv_verify_select(ykpiv_state *state, const char *pin, const size_t pin_len, int *tries, bool force_select) {
ykpiv_rc ykpiv_auth_getchallenge(ykpiv_state *state, uint8_t *challenge, const size_t challenge_len) {
ykpiv_rc res = YKPIV_OK;
if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) goto Cleanup;
#if 0
// TODO when is this needed? windows unit tests pass without it
if (force_select) {
if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup;
APDU apdu = { 0 };
unsigned char data[261] = { 0 };
unsigned long recv_len = sizeof(data);
int sw = 0;
if (NULL == state) return YKPIV_GENERIC_ERROR;
if (NULL == challenge) return YKPIV_GENERIC_ERROR;
if (8 != challenge_len) return YKPIV_SIZE_ERROR;
if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR;
if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup;
/* get a challenge from the card */
memset(apdu.raw, 0, sizeof(apdu));
apdu.st.ins = YKPIV_INS_AUTHENTICATE;
apdu.st.p1 = YKPIV_ALGO_3DES; /* triple des */
apdu.st.p2 = YKPIV_KEY_CARDMGM; /* management key */
apdu.st.lc = 0x04;
apdu.st.data[0] = 0x7c;
apdu.st.data[1] = 0x02;
apdu.st.data[2] = 0x81; //0x80;
if ((res = _send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) {
goto Cleanup;
}
#endif
res = ykpiv_verify(state, pin, tries);
else if (sw != SW_SUCCESS) {
res = YKPIV_AUTHENTICATION_ERROR;
goto Cleanup;
}
memcpy(challenge, data + 4, 8);
Cleanup:
_ykpiv_end_transaction(state);
return res;
}
ykpiv_rc ykpiv_auth_verifyresponse(ykpiv_state *state, uint8_t *response, const size_t response_len) {
ykpiv_rc res = YKPIV_OK;
APDU apdu = { 0 };
unsigned char data[261] = { 0 };
unsigned long recv_len = sizeof(data);
int sw = 0;
unsigned char *dataptr = apdu.st.data;
if (NULL == state) return YKPIV_GENERIC_ERROR;
if (NULL == response) return YKPIV_GENERIC_ERROR;
if (8 != response_len) return YKPIV_SIZE_ERROR;
if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR;
/* note: do not select the applet here, as it resets the challenge state */
/* send the response to the card and a challenge of our own. */
recv_len = sizeof(data);
memset(apdu.raw, 0, sizeof(apdu));
apdu.st.ins = YKPIV_INS_AUTHENTICATE;
apdu.st.p1 = YKPIV_ALGO_3DES; /* triple des */
apdu.st.p2 = YKPIV_KEY_CARDMGM; /* management key */
*dataptr++ = 0x7c;
*dataptr++ = 0x0a; /* 2 + 8 */
*dataptr++ = 0x82;
*dataptr++ = 8;
memcpy(dataptr, response, response_len);
dataptr += 8;
apdu.st.lc = (unsigned char)(dataptr - apdu.st.data);
if ((res = _send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) {
goto Cleanup;
}
else if (sw != SW_SUCCESS) {
res = YKPIV_AUTHENTICATION_ERROR;
goto Cleanup;
}
Cleanup:
_ykpiv_end_transaction(state);
+2 -4
View File
@@ -499,10 +499,8 @@ extern "C"
*/
uint32_t ykpiv_util_slot_object(uint8_t slot);
ykpiv_rc ykpiv_connect_with_card(ykpiv_state *state, uint64_t context, uint64_t card);
// TREV TODO: remove
ykpiv_rc ykpiv_done2(ykpiv_state *state, bool disconnect);
ykpiv_rc ykpiv_connect_with_exteral_card(ykpiv_state *state, uint64_t context, uint64_t card, bool select);
ykpiv_rc ykpiv_done_with_external_card(ykpiv_state *state);
ykpiv_rc ykpiv_verify_select(ykpiv_state *state, const char *pin, const size_t pin_len, int *tries, bool force_select);
#ifdef __cplusplus