diff --git a/lib/error.c b/lib/error.c index a4dd16f..6505ab6 100644 --- a/lib/error.c +++ b/lib/error.c @@ -46,6 +46,8 @@ static const err_t errors[] = { ERR (YKPIV_PCSC_ERROR, "Error in PCSC call"), ERR (YKPIV_SIZE_ERROR, "Wrong buffer size"), ERR (YKPIV_APPLET_ERROR, "No PIV applet found"), + ERR (YKPIV_AUTHENTICATION_ERROR, "Error during authentication"), + ERR (YKPIV_RANDOMNESS_ERROR, "Error getting randomness"), }; /** diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 0db5dca..de2a434 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -32,6 +32,9 @@ #include #include +#include +#include + #include "internal.h" #include "ykpiv.h" @@ -270,3 +273,89 @@ ykpiv_rc ykpiv_send_data(ykpiv_state *state, unsigned char *apdu, } return YKPIV_OK; } + +ykpiv_rc ykpiv_authenticate(ykpiv_state *state, unsigned const char *key) { + APDU apdu; + unsigned char data[0xff]; + DES_cblock challenge; + unsigned long recv_len = sizeof(data); + int sw; + ykpiv_rc res; + + DES_key_schedule ks1, ks2, ks3; + + /* set up our key */ + { + const_DES_cblock key_tmp; + memcpy(key_tmp, key, 8); + DES_set_key_unchecked(&key_tmp, &ks1); + memcpy(key_tmp, key + 8, 8); + DES_set_key_unchecked(&key_tmp, &ks2); + memcpy(key_tmp, key + 16, 8); + DES_set_key_unchecked(&key_tmp, &ks3); + } + + /* get a challenge from the card */ + { + memset(apdu.raw, 0, sizeof(apdu)); + apdu.st.ins = 0x87; + apdu.st.p1 = 0x03; /* triple des */ + apdu.st.p2 = 0x9b; /* management key */ + apdu.st.lc = 0x04; + apdu.st.data[0] = 0x7c; + apdu.st.data[1] = 0x02; + apdu.st.data[2] = 0x80; + if((res = ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw)) != YKPIV_OK) { + return res; + } else if(sw != 0x9000) { + return YKPIV_AUTHENTICATION_ERROR; + } + memcpy(challenge, data + 4, 8); + } + + /* send a response to the cards challenge and a challenge of our own. */ + { + unsigned char *dataptr = apdu.st.data; + DES_cblock response; + DES_ecb3_encrypt(&challenge, &response, &ks1, &ks2, &ks3, 0); + + recv_len = 0xff; + memset(apdu.raw, 0, sizeof(apdu)); + apdu.st.ins = 0x87; + apdu.st.p1 = 0x03; /* triple des */ + apdu.st.p2 = 0x9b; /* management key */ + *dataptr++ = 0x7c; + *dataptr++ = 20; /* 2 + 8 + 2 +8 */ + *dataptr++ = 0x80; + *dataptr++ = 8; + memcpy(dataptr, response, 8); + dataptr += 8; + *dataptr++ = 0x81; + *dataptr++ = 8; + if(RAND_pseudo_bytes(dataptr, 8) == -1) { + if(state->verbose) { + fprintf(stderr, "Failed getting randomness for authentication.\n"); + } + return YKPIV_RANDOMNESS_ERROR; + } + memcpy(challenge, dataptr, 8); + dataptr += 8; + apdu.st.lc = dataptr - apdu.st.data; + if((res = ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw)) != YKPIV_OK) { + return res; + } else if(sw != 0x9000) { + return YKPIV_AUTHENTICATION_ERROR; + } + } + + /* compare the response from the card with our challenge */ + { + DES_cblock response; + DES_ecb3_encrypt(&challenge, &response, &ks1, &ks2, &ks3, 1); + if(memcmp(response, data + 4, 8) == 0) { + return YKPIV_OK; + } else { + return YKPIV_AUTHENTICATION_ERROR; + } + } +} diff --git a/lib/ykpiv.h b/lib/ykpiv.h index 5e65243..7253198 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -45,6 +45,8 @@ extern "C" YKPIV_PCSC_ERROR = -2, YKPIV_SIZE_ERROR = -3, YKPIV_APPLET_ERROR = -4, + YKPIV_AUTHENTICATION_ERROR = -5, + YKPIV_RANDOMNESS_ERROR = -6, } ykpiv_rc; const char *ykpiv_strerror(ykpiv_rc err); @@ -58,6 +60,7 @@ extern "C" unsigned char *out_data, unsigned long *out_len, int *sw); ykpiv_rc ykpiv_send_data(ykpiv_state *state, unsigned char *apdu, unsigned char *data, unsigned long *recv_len, int *sw); + ykpiv_rc ykpiv_authenticate(ykpiv_state *state, const unsigned char *key); #ifdef __cplusplus } diff --git a/lib/ykpiv.map b/lib/ykpiv.map index 1554d62..a6812ce 100644 --- a/lib/ykpiv.map +++ b/lib/ykpiv.map @@ -35,6 +35,7 @@ global: ykpiv_connect; ykpiv_send_data; ykpiv_transfer_data; + ykpiv_authenticate; local: *; diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 6c73355..3bbcd37 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -90,89 +90,6 @@ static bool sign_data(ykpiv_state*, unsigned char*, int, unsigned char, unsigned ASN1_BIT_STRING*); static int get_object_id(enum enum_slot slot); -static bool authenticate(ykpiv_state *state, unsigned const char *key) { - APDU apdu; - unsigned char data[0xff]; - DES_cblock challenge; - unsigned long recv_len = sizeof(data); - int sw; - - DES_key_schedule ks1, ks2, ks3; - - /* set up our key */ - { - const_DES_cblock key_tmp; - memcpy(key_tmp, key, 8); - DES_set_key_unchecked(&key_tmp, &ks1); - memcpy(key_tmp, key + 8, 8); - DES_set_key_unchecked(&key_tmp, &ks2); - memcpy(key_tmp, key + 16, 8); - DES_set_key_unchecked(&key_tmp, &ks3); - } - - /* get a challenge from the card */ - { - memset(apdu.raw, 0, sizeof(apdu)); - apdu.st.ins = 0x87; - apdu.st.p1 = 0x03; /* triple des */ - apdu.st.p2 = 0x9b; /* management key */ - apdu.st.lc = 0x04; - apdu.st.data[0] = 0x7c; - apdu.st.data[1] = 0x02; - apdu.st.data[2] = 0x80; - if(ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw) != YKPIV_OK) { - return false; - } else if(sw != 0x9000) { - return false; - } - memcpy(challenge, data + 4, 8); - } - - /* send a response to the cards challenge and a challenge of our own. */ - { - unsigned char *dataptr = apdu.st.data; - DES_cblock response; - DES_ecb3_encrypt(&challenge, &response, &ks1, &ks2, &ks3, 0); - - recv_len = 0xff; - memset(apdu.raw, 0, sizeof(apdu)); - apdu.st.ins = 0x87; - apdu.st.p1 = 0x03; /* triple des */ - apdu.st.p2 = 0x9b; /* management key */ - *dataptr++ = 0x7c; - *dataptr++ = 20; /* 2 + 8 + 2 +8 */ - *dataptr++ = 0x80; - *dataptr++ = 8; - memcpy(dataptr, response, 8); - dataptr += 8; - *dataptr++ = 0x81; - *dataptr++ = 8; - if(RAND_pseudo_bytes(dataptr, 8) == -1) { - fprintf(stderr, "Failed getting randomness for authentication.\n"); - return false; - } - memcpy(challenge, dataptr, 8); - dataptr += 8; - apdu.st.lc = dataptr - apdu.st.data; - if(ykpiv_send_data(state, apdu.raw, data, &recv_len, &sw) != YKPIV_OK) { - return false; - } else if(sw != 0x9000) { - return false; - } - } - - /* compare the response from the card with our challenge */ - { - DES_cblock response; - DES_ecb3_encrypt(&challenge, &response, &ks1, &ks2, &ks3, 1); - if(memcmp(response, data + 4, 8) == 0) { - return true; - } else { - return false; - } - } -} - static void print_version(ykpiv_state *state) { APDU apdu; unsigned char data[0xff]; @@ -1288,7 +1205,7 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - if(authenticate(state, key) == false) { + if(ykpiv_authenticate(state, key) != YKPIV_OK) { fprintf(stderr, "Failed authentication with the applet.\n"); return EXIT_FAILURE; }