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 { else {
res = YKPIV_GENERIC_ERROR; res = YKPIV_GENERIC_ERROR;
if (state->verbose) { fprintf(stderr, "error %x)\n", sw); } 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); return ykpiv_init_with_allocator(state, verbose, &_default_allocator);
} }
ykpiv_rc ykpiv_done(ykpiv_state *state) { static ykpiv_rc _done_internal(ykpiv_state *state, bool disconnect) {
ykpiv_disconnect(state); if (disconnect)
ykpiv_disconnect(state);
_cache_pin(state, NULL, 0); _cache_pin(state, NULL, 0);
_ykpiv_free(state, state); _ykpiv_free(state, state);
return YKPIV_OK; 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) { ykpiv_rc ykpiv_disconnect(ykpiv_state *state) {
if(state->card) { if(state->card) {
SCardDisconnect(state->card, SCARD_RESET_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->context = context;
state->card = card; state->card = card;
// transact the connect operation /*
** Do not select the applet here, as we need to accommodate commands that are
if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; ** sensitive to re-select (custom apdu/auth). All commands that can handle explicit
res = _ykpiv_ensure_application_selected(state); ** selection already check the applet state and select accordingly anyway.
_ykpiv_end_transaction(state); ** 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; 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); 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 // at this point, card should not equal state->card, to allow _connect_internal() to determine device type
if (!_connect_internal(state, state->context, card)) { if (YKPIV_OK == _connect_internal(state, state->context, card)) {
return YKPIV_OK; /*
* 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) { if(len > 8) {
return YKPIV_SIZE_ERROR; return YKPIV_SIZE_ERROR;
} }
memset(apdu.raw, 0, sizeof(apdu.raw)); memset(apdu.raw, 0, sizeof(apdu.raw));
apdu.st.ins = YKPIV_INS_VERIFY; apdu.st.ins = YKPIV_INS_VERIFY;
apdu.st.p1 = 0x00; 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 ykpiv_get_pin_retries(ykpiv_state *state, int* tries) {
ykpiv_rc res; ykpiv_rc res;
ykpiv_rc ykrc; 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 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) { if (res == YKPIV_OK && new_pin != NULL) {
// Intentionally ignore errors. If the PIN fails to save, it will only // Intentionally ignore errors. If the PIN fails to save, it will only
// be a problem if a reconnect is attempted. Failure deferred until then. // be a problem if a reconnect is attempted. Failure deferred until then.
_cache_pin(state, new_pin, new_pin_len + 1); _cache_pin(state, new_pin, new_pin_len + 1);
} }
Cleanup:
_ykpiv_end_transaction(state);
return res; 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) { 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) { 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, 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_auth_getchallenge(ykpiv_state *state, uint8_t *challenge, const size_t challenge_len) {
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 res = YKPIV_OK; ykpiv_rc res = YKPIV_OK;
if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) goto Cleanup; APDU apdu = { 0 };
#if 0 unsigned char data[261] = { 0 };
// TODO when is this needed? windows unit tests pass without it unsigned long recv_len = sizeof(data);
if (force_select) { int sw = 0;
if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup;
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 else if (sw != SW_SUCCESS) {
res = ykpiv_verify(state, pin, tries); 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: Cleanup:
_ykpiv_end_transaction(state); _ykpiv_end_transaction(state);
+2 -4
View File
@@ -499,10 +499,8 @@ extern "C"
*/ */
uint32_t ykpiv_util_slot_object(uint8_t slot); uint32_t ykpiv_util_slot_object(uint8_t slot);
ykpiv_rc ykpiv_connect_with_card(ykpiv_state *state, uint64_t context, uint64_t card); 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);
// TREV TODO: remove
ykpiv_rc ykpiv_done2(ykpiv_state *state, bool disconnect);
ykpiv_rc ykpiv_verify_select(ykpiv_state *state, const char *pin, const size_t pin_len, int *tries, bool force_select); ykpiv_rc ykpiv_verify_select(ykpiv_state *state, const char *pin, const size_t pin_len, int *tries, bool force_select);
#ifdef __cplusplus #ifdef __cplusplus