From bf84ec110b0266463108070fd36dfc99c1a79f25 Mon Sep 17 00:00:00 2001 From: Aveen Ismail Date: Sun, 10 Nov 2024 11:50:33 +0100 Subject: [PATCH] SCP11: Add support for opening SCP11b connection and deriving session keys --- lib/internal.h | 28 +++ lib/ykpiv.c | 439 +++++++++++++++++++++++++++++++++++++++-- lib/ykpiv.h | 13 ++ tool/cmdline.ggo | 2 + tool/yubico-piv-tool.c | 2 +- 5 files changed, 467 insertions(+), 17 deletions(-) diff --git a/lib/internal.h b/lib/internal.h index d8b06fe1..bc06938c 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -124,6 +124,24 @@ extern "C" #define CB_PIN_MAX 8 + +#define SCP11B_KID 0x13 +#define SCP11B_KVN 0x1 +#define SCP11_KEY_USAGE 0x3c +#define SCP11_KEY_TYPE 0x88 +#define SCP11_SESSION_KEY_LEN 16 +#define SCP11_RECEIPT_LEN 16 +#define SCP11_CERTIFICATE_STORE_TAG 0xBF21 +#define SCP11_ePK_SD_ECKA_TAG 0x5F49 +#define SCP11_RECEIPT_TAG 0x86 +#define SCP11_KEY_AGREEMENT_TAG 0xa6 +#define SCP11_SCP_ID_TAG 0x90 +#define SCP11_SCP_ID 0x11 +#define SCP11_SCP11B_ID 0x00 +#define SCP11_KEY_USAGE_TAG 0x95 +#define SCP11_KEY_TYPE_TAG 0x80 +#define SCP11_KEY_LEN_TAG 0x81 + typedef enum { CIPHER_OK = 0, CIPHER_INVALID_PARAMETER = -1, @@ -148,6 +166,14 @@ typedef struct _ykpiv_version_t { uint8_t patch; } ykpiv_version_t; +struct ykpiv_scp11_state { + uint8_t security_level; + uint8_t senc[SCP11_SESSION_KEY_LEN]; + uint8_t smac[SCP11_SESSION_KEY_LEN]; + uint8_t srmac[SCP11_SESSION_KEY_LEN]; +// uint8_t dek[16]; +}; + struct ykpiv_state { SCARDCONTEXT context; SCARDHANDLE card; @@ -161,6 +187,7 @@ struct ykpiv_state { uint32_t model; ykpiv_version_t ver; uint32_t serial; + ykpiv_scp11_state scp11_state; }; union u_APDU { @@ -192,6 +219,7 @@ ykpiv_rc _ykpiv_begin_transaction(ykpiv_state *state); ykpiv_rc _ykpiv_end_transaction(ykpiv_state *state); ykpiv_rc _ykpiv_ensure_application_selected(ykpiv_state *state); ykpiv_rc _ykpiv_select_application(ykpiv_state *state); +ykpiv_rc _ykpiv_select_gp_application(ykpiv_state *state); size_t _ykpiv_get_length_size(size_t length); size_t _ykpiv_set_length(unsigned char *buffer, size_t length); size_t _ykpiv_get_length(const unsigned char *buffer, const unsigned char* end, size_t *len); diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 21344489..6ec3df66 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include "internal.h" #include "ykpiv.h" @@ -141,6 +143,10 @@ static unsigned const char mgmt_aid[] = { 0xa0, 0x00, 0x00, 0x05, 0x27, 0x47, 0x11, 0x17 }; +static unsigned const char sd_aid[] = { + 0xa0, 0x00, 0x00, 0x01, 0x51, 0x00, 0x00, 0x00 +}; + static void* _default_alloc(void *data, size_t cb) { (void)data; return calloc(cb, 1); @@ -362,6 +368,394 @@ ykpiv_rc ykpiv_disconnect(ykpiv_state *state) { return YKPIV_OK; } +static EVP_PKEY* get_ec_pubkey_from_bytes(uint8_t *pubkey, uint8_t pubkey_len) { + EC_GROUP *ecg = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); + EC_POINT *ecp = EC_POINT_new(ecg); + if (EC_POINT_oct2point(ecg, ecp, pubkey, pubkey_len, NULL) == 0) { + fprintf(stderr, "Failed to parse public key as ec_point.\n"); + return NULL; + } + + EC_KEY *eckey = EC_KEY_new(); + EC_KEY_set_group(eckey, ecg); + if(EC_KEY_set_public_key(eckey, ecp) == 0) { + fprintf(stderr, "Failed to convert sd pubkey point to eckey.\n"); + return NULL; + } + + EVP_PKEY *evp_pkey = EVP_PKEY_new(); + if(!EVP_PKEY_assign_EC_KEY(evp_pkey, eckey)) { + fprintf(stderr, "Failed to convert sd pubkey eckey to evp_pkey.\n"); + return NULL; + } + + return evp_pkey; +} + +static size_t derive_ecdh(EVP_PKEY *private_key, EVP_PKEY *peer_key, unsigned char **ecdh_key) { + + /* Create the context for the shared secret derivation */ + EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(private_key, NULL); + if (!ctx) { + DBG("Failed to create new openssl context for ECDH derivation"); + return 0; + } + size_t len = 0; + /* Initialize derivation function*/ + if (EVP_PKEY_derive_init(ctx) != 1) { + DBG("Failed to initialize openssl contex"); + goto derive_free; + } + /* Set the peer public key */ + if (EVP_PKEY_derive_set_peer(ctx, peer_key) != 1) { + DBG("Failed to set the peer public key in the openssl context"); + goto derive_free; + } + /* Determine buffer length for shared secret */ + if (EVP_PKEY_derive(ctx, NULL, &len) != 1) { + DBG("Failed to determine derived key expected size with openssl"); + goto derive_free; + } + /* Create the buffer */ + *ecdh_key = OPENSSL_malloc(len); + if (*ecdh_key == NULL) { + DBG("Failed to allocate the buffer to hold the ECDH key derived with " + "openssl"); + len = 0; + goto derive_free; + } + /* Derive the shared secret */ + if ((EVP_PKEY_derive(ctx, *ecdh_key, &len)) != 1) { + DBG("Failed to derive ECDH key with openssl"); + len = 0; + goto derive_free; + } + derive_free: + EVP_PKEY_CTX_free(ctx); + + return len; +} + +static EVP_PKEY* scp11_get_sd_pubkey(ykpiv_state *state) { + ykpiv_rc res; + + unsigned char apdu[] = {0x00, GP_INS_GET_DATA, SCP11_CERTIFICATE_STORE_TAG >> 8, SCP11_CERTIFICATE_STORE_TAG & 0xff}; + unsigned char data[6] = {0xa6, 0x04, 0x83, 0x2, SCP11B_KID, SCP11B_KVN}; + unsigned char recv[2048] = {0}; + unsigned long recv_len = sizeof(recv); + int sw = 0; + + if((res = _ykpiv_transfer_data(state, apdu, data, sizeof(data), recv, &recv_len, &sw)) != YKPIV_OK) { + DBG("Failed to communicate with device. res: %d sw: %08x", res, sw); + return NULL; + } + res = ykpiv_translate_sw_ex(__FUNCTION__, sw); + if(res != YKPIV_OK) { + DBG("Failed to get SCP11 SD public key (CERT.SD.ECKA). res: %d sw: %08x", res, sw); + return NULL; + } + + X509 *x509; + for(int i=100; i < recv_len-100; i++) { + const unsigned char *ptr = recv + i; + X509 *tmp = d2i_X509(NULL, &ptr, recv_len-i); + if(tmp) { + x509 = tmp; + } + } + + if(!x509) { + DBG("Failed to parse CERT.SD.ECKA"); + return NULL; + } + + return X509_get_pubkey(x509); +} + +static ykpiv_rc scp11_derive_session_keys(EC_KEY *ekeypair_oce, uint8_t *epubkey_sd_bytes, + size_t epubkey_sd_len, EVP_PKEY *pubkey_sd, uint8_t *session_keys) { + + EVP_PKEY *epubkey_sd = get_ec_pubkey_from_bytes(epubkey_sd_bytes, epubkey_sd_len); + if(epubkey_sd == NULL) { + fprintf(stderr, "Failed to convert sd pubkey bytes to evp_pkey.\n"); + return YKPIV_MEMORY_ERROR; + } + + EVP_PKEY *eprivkey_oce = EVP_PKEY_new(); + if (!EVP_PKEY_assign_EC_KEY(eprivkey_oce, ekeypair_oce)) { + DBG("Failed to parse OCE ephemeral private key (eSK.OCE.ECKA)"); + return YKPIV_AUTHENTICATION_ERROR; + } + + unsigned char *ecdh1 = NULL, *ecdh2 = NULL; + size_t ecdh_len = derive_ecdh(eprivkey_oce, epubkey_sd, &ecdh1); + if (ecdh_len != 32) { + DBG("Failed to derive ECDH shared key (ShSee)"); + return YKPIV_KEY_ERROR; + } + + ecdh_len = derive_ecdh(eprivkey_oce, pubkey_sd, &ecdh2); + if (ecdh_len != 32) { + DBG("Failed to derive ECDH shared key (SHSes)"); + return YKPIV_KEY_ERROR; + } + + uint8_t shared_data[] = {SCP11_KEY_USAGE, SCP11_KEY_TYPE, SCP11_SESSION_KEY_LEN}; + size_t hash_data_len = (ecdh_len * 2) + 4 + sizeof(shared_data); + uint8_t *hash_data = malloc(hash_data_len); + memcpy(hash_data, ecdh1, ecdh_len); + memcpy(hash_data + ecdh_len, ecdh2, ecdh_len); + memset(hash_data + (ecdh_len * 2), 0, 4); + memcpy(hash_data + (ecdh_len * 2) + 4, shared_data, sizeof(shared_data)); + size_t counter_index = (ecdh_len * 2) + 3; + for(int i=0; i<3; i++) { + hash_data[counter_index]++; + SHA256(hash_data, hash_data_len, session_keys + (i * 32)); + } + return YKPIV_OK; +} + +static ykpiv_rc scp11_internal_authenticate(ykpiv_state *state, uint8_t *data, size_t data_len, + uint8_t* epubkey_sd, size_t* epubkey_sd_len, uint8_t* receipt) { + + uint8_t apdu[] = {0x80, GP_INS_INTERNAL_AUTHENTICATE, SCP11B_KVN, SCP11B_KID}; + + ykpiv_rc res; + uint8_t recv[2048] = {0}; + size_t recv_len = sizeof(recv); + memset(recv, 0, recv_len); + int sw = 0; + if((res = _ykpiv_transfer_data(state, apdu, data, data_len, recv, &recv_len, &sw)) != YKPIV_OK) { + return res; + } + res = ykpiv_translate_sw_ex(__FUNCTION__, sw); + if(res != YKPIV_OK) { + DBG("Failed to get SCP11b public key. res: %d sw: %08x", res, sw); + return res; + } + + + if((recv[0] == (SCP11_ePK_SD_ECKA_TAG >> 8)) && (recv[1] == (SCP11_ePK_SD_ECKA_TAG & 0xff))){ + const unsigned char *ptr = recv+2; + + if(*ptr > *epubkey_sd_len) { + DBG("Buffer size too small. Found key length %d", *ptr); + return YKPIV_SIZE_ERROR; + } + + *epubkey_sd_len = *ptr; + ptr++; + + memcpy(epubkey_sd, ptr, *epubkey_sd_len); + ptr += *epubkey_sd_len; + + if(*ptr == SCP11_RECEIPT_TAG) { + ptr++; + if(*ptr != SCP11_RECEIPT_LEN) { + DBG("Wrong receipt length. Expected 16. Found %d\n", (*ptr)); + return YKPIV_AUTHENTICATION_ERROR; + } + ptr++; + + memcpy(receipt, ptr, SCP11_RECEIPT_LEN); + } + } + + return YKPIV_OK; +} + +static ykpiv_rc calculate_cmac(uint8_t *verification_key, uint8_t *ka_data, size_t ka_data_len, uint8_t *mac_out) { + /* Fetch the CMAC implementation */ + EVP_MAC *mac = EVP_MAC_fetch(NULL, "CMAC", NULL); + if (mac == NULL) { + DBG("Failed to fetch CMAC implementation"); + return YKPIV_AUTHENTICATION_ERROR; + } + + /* Create a context for the CMAC operation */ + EVP_MAC_CTX *mctx = EVP_MAC_CTX_new(mac); + if (mctx == NULL) { + DBG("Failed to create CMAC context"); + return YKPIV_AUTHENTICATION_ERROR; + } + + OSSL_PARAM params[3]; + size_t params_n = 0; + + char cipher_name[] = "AES-128-CBC"; + params[params_n++] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_CIPHER, (char*)cipher_name, 0); + params[params_n] = OSSL_PARAM_construct_end(); + + ykpiv_rc res = YKPIV_OK; + + /* Initialise the CMAC operation */ + if (!EVP_MAC_init(mctx, verification_key, SCP11_SESSION_KEY_LEN, params)) { + DBG("Failed to initiate CMAC function"); + res = YKPIV_KEY_ERROR; + goto cmac_free; + } + + /* Make one or more calls to process the data to be authenticated */ + if (!EVP_MAC_update(mctx, ka_data, ka_data_len)) { + DBG("Failed to set CMAC input data"); + res = YKPIV_AUTHENTICATION_ERROR; + goto cmac_free; + } + + /* Make a call to the final with a NULL buffer to get the length of the MAC */ + size_t out_len = 0; + if (!EVP_MAC_final(mctx, NULL, &out_len, 0)) { + DBG("Failed to retrieve CMAC length"); + res = YKPIV_AUTHENTICATION_ERROR; + goto cmac_free; + } + + if(out_len != SCP11_RECEIPT_LEN) { + DBG("Unexpected MAC length. Expected %d. Found %ld\n", SCP11_RECEIPT_LEN, out_len); + res = YKPIV_AUTHENTICATION_ERROR; + goto cmac_free; + } + + /* Make one call to the final to get the MAC */ + if (!EVP_MAC_final(mctx, mac_out, &out_len, out_len)) { + DBG("Failed to calculate CMAC value"); + res = YKPIV_KEY_ERROR; + goto cmac_free; + } + +cmac_free: + EVP_MAC_CTX_free(mctx); + return res; +} + +static ykpiv_rc scp11_open_secure_channel(ykpiv_state *state) { + ykpiv_rc res; + if ((res = _ykpiv_select_gp_application(state)) != YKPIV_OK) { + DBG("Failed to select management applet"); + return res; + } + + EVP_PKEY *pubkey_sd = scp11_get_sd_pubkey(state); + if (!pubkey_sd) { + DBG("Failed to get SD public key (PK.SD.ECKA)"); + return YKPIV_AUTHENTICATION_ERROR; + } + + EC_KEY *ekeypair_oce = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + if (!ekeypair_oce) { + DBG("Failed to create EC object from curve.\n"); + return YKPIV_AUTHENTICATION_ERROR; + } + if(EC_KEY_generate_key(ekeypair_oce) != 1) { + DBG("Failed to generate OCE ephemeral keypair.\n"); + return YKPIV_AUTHENTICATION_ERROR; + } + + + const EC_GROUP *ecg = EC_KEY_get0_group(ekeypair_oce); + const EC_POINT *ecp = EC_KEY_get0_public_key(ekeypair_oce); + unsigned char epubkey_oce[1024] = {0}; + size_t epubkey_oce_len = sizeof(epubkey_oce); + if ((epubkey_oce_len = EC_POINT_point2oct(ecg, ecp, POINT_CONVERSION_UNCOMPRESSED, epubkey_oce, epubkey_oce_len, NULL)) == 0) { + DBG("Failed to parse OCE ephemeral public key (ePK.OCE.ECKA).\n"); + return YKPIV_AUTHENTICATION_ERROR; + } + + +// uint8_t data1[] = {SCP11_KEY_AGREEMENT_TAG, 0x0d, 0x90, 0x02, 0x11, 0x00, 0x95, 0x1, 0x3c, 0x80, 0x1, 0x88, 0x81, 0x01, 0x10, 0x5f, 0x49, epubkey_oce_len}; +// uint8_t data_len = sizeof(data1) + epubkey_oce_len; +// uint8_t *data = malloc(data_len); +// memcpy(data, data1, sizeof(data1)); +// memcpy(data+sizeof(data1), epubkey_oce, epubkey_oce_len); + + + // Construct the control reference template (key agreement) + uint8_t ka_template[] = {SCP11_SCP_ID_TAG, 2, SCP11_SCP_ID, SCP11_SCP11B_ID, SCP11_KEY_USAGE_TAG, 1, SCP11_KEY_USAGE, SCP11_KEY_TYPE_TAG, 1, SCP11_KEY_TYPE, SCP11_KEY_LEN_TAG, 1, SCP11_SESSION_KEY_LEN}; + size_t data_len = 2 + sizeof(ka_template) + 3 + epubkey_oce_len; + uint8_t *data = malloc(data_len); + uint8_t *ptr = data; + *ptr++ = SCP11_KEY_AGREEMENT_TAG; + *ptr++ = sizeof(ka_template); + memcpy(ptr, ka_template, sizeof(ka_template)); + ptr += sizeof(ka_template); + *ptr++ = SCP11_ePK_SD_ECKA_TAG >> 8; + *ptr++ = SCP11_ePK_SD_ECKA_TAG & 0xff; + *ptr++ = epubkey_oce_len; + memcpy(ptr, epubkey_oce, epubkey_oce_len); + + uint8_t epubkey_sd[512] = {0}; + size_t epubkey_sd_len = sizeof(epubkey_sd); + uint8_t receipt[SCP11_RECEIPT_LEN] = {0}; + if((res = scp11_internal_authenticate(state, data, data_len, epubkey_sd, &epubkey_sd_len, receipt)) != YKPIV_OK) { + DBG("Failed to do SCP11 internal authentication"); + return res; + } + + uint8_t session_keys[SCP11_SESSION_KEY_LEN*6] = {0}; + if((res = scp11_derive_session_keys(ekeypair_oce, epubkey_sd, epubkey_sd_len, pubkey_sd, session_keys)) != YKPIV_OK) { + DBG("Failed to derive SCP11 session keys"); + return res; + } + + uint8_t *ka_data = malloc(data_len + epubkey_sd_len + 3); + memcpy(ka_data, data, data_len); + ka_data[data_len] = SCP11_ePK_SD_ECKA_TAG >> 8; + ka_data[data_len + 1] = SCP11_ePK_SD_ECKA_TAG & 0xff; + ka_data[data_len + 2] = epubkey_sd_len; + memcpy(ka_data + data_len + 3, epubkey_sd, epubkey_sd_len); + + + uint8_t verification_key[SCP11_SESSION_KEY_LEN] = {0}; + memcpy(verification_key, session_keys, sizeof(verification_key)); + + uint8_t mac_out[SCP11_RECEIPT_LEN] = {0}; + if((res = calculate_cmac(verification_key, ka_data, data_len + epubkey_sd_len + 3, mac_out)) != YKPIV_OK) { + DBG("Failed to calculate CMAC value"); + return res; + } + + if(memcmp(mac_out, receipt, SCP11_RECEIPT_LEN) != 0) { + DBG("Failed to verify SCP11 connection"); + return res; + } + +#if ENABLE_APPLICATION_RESELECTION + res = _ykpiv_ensure_application_selected(state); +#else + res = _ykpiv_select_application(state); +#endif + if(res != YKPIV_OK) { + DBG("Failed to select PIV application after deriving SCP11 session keys"); + return res; + } + + state->scp11_state.security_level = SCP11_KEY_USAGE; + memcpy(state->scp11_state.senc, session_keys + SCP11_SESSION_KEY_LEN, SCP11_SESSION_KEY_LEN); + memcpy(state->scp11_state.smac, session_keys + (SCP11_SESSION_KEY_LEN * 2), SCP11_SESSION_KEY_LEN); + memcpy(state->scp11_state.srmac, session_keys + (SCP11_SESSION_KEY_LEN * 3), SCP11_SESSION_KEY_LEN); + + return YKPIV_OK; +} + + ykpiv_rc _ykpiv_select_gp_application(ykpiv_state *state) { + unsigned char templ[] = {0x00, YKPIV_INS_SELECT_APPLICATION, 0x04, 0x00}; + unsigned char data[256] = {0}; + unsigned long recv_len = sizeof(data); + int sw = 0; + ykpiv_rc res; + + if((res = _ykpiv_transfer_data(state, templ, sd_aid, sizeof(sd_aid), data, &recv_len, &sw)) != YKPIV_OK) { + return res; + } + res = ykpiv_translate_sw_ex(__FUNCTION__, sw); + if(res != YKPIV_OK) { + DBG("Failed selecting application"); + return res; + } + + return res; + } + ykpiv_rc _ykpiv_select_application(ykpiv_state *state) { unsigned char templ[] = {0x00, YKPIV_INS_SELECT_APPLICATION, 0x04, 0x00}; unsigned char data[256] = {0}; @@ -532,6 +926,10 @@ ykpiv_rc ykpiv_validate(ykpiv_state *state, const char *wanted) { } ykpiv_rc ykpiv_connect(ykpiv_state *state, const char *wanted) { + return ykpiv_connect_ex(state, wanted, false); +} + +ykpiv_rc ykpiv_connect_ex(ykpiv_state *state, const char *wanted, bool scp11) { char reader_buf[2048] = {0}; size_t num_readers = sizeof(reader_buf); pcsc_long rc; @@ -540,7 +938,7 @@ ykpiv_rc ykpiv_connect(ykpiv_state *state, const char *wanted) { SCARDHANDLE card = (SCARDHANDLE)-1; if(wanted && *wanted == '@') { - wanted++; // Skip the '@' + wanted++; // Skip the '@' DBG("Connect reader '%s'.", wanted); if(SCardIsValidContext(state->context) != SCARD_S_SUCCESS) { rc = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &state->context); @@ -609,19 +1007,28 @@ ykpiv_rc ykpiv_connect(ykpiv_state *state, const char *wanted) { // at this point, card should not equal state->card, to allow _ykpiv_connect() to determine device type if (YKPIV_OK == _ykpiv_connect(state, state->context, card)) { - /* - * Select applet. This is done here instead of in _ykpiv_connect() 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 ret; + /* + * Select applet. This is done here instead of in _ykpiv_connect() 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 ret; + + if (scp11) { + if ((ret = scp11_open_secure_channel(state)) != YKPIV_OK) { + DBG("Unable to open encrypted session"); + return ret; + } + } else { #if ENABLE_APPLICATION_RESELECTION - ret = _ykpiv_ensure_application_selected(state); + ret = _ykpiv_ensure_application_selected(state); #else - ret = _ykpiv_select_application(state); + ret = _ykpiv_select_application(state); #endif - _ykpiv_end_transaction(state); - return ret; + } + _ykpiv_end_transaction(state); + return ret; + } return YKPIV_GENERIC_ERROR; @@ -1351,7 +1758,7 @@ static ykpiv_rc _ykpiv_get_version(ykpiv_state *state) { unsigned long recv_len = sizeof(data); int sw = 0; ykpiv_rc res; - + if (!state) { return YKPIV_ARGUMENT_ERROR; } @@ -1804,8 +2211,8 @@ static ykpiv_rc _ykpiv_change_pin(ykpiv_state *state, int action, const char * c if(res != YKPIV_OK) { return res; - } - res = ykpiv_translate_sw_ex(__FUNCTION__, sw); + } + res = ykpiv_translate_sw_ex(__FUNCTION__, sw); if(res != YKPIV_OK) { if((sw >> 8) == 0x63) { if (tries) *tries = sw & 0xf; @@ -1895,7 +2302,7 @@ ykpiv_rc _ykpiv_fetch_object(ykpiv_state *state, int object_id, res = ykpiv_translate_sw_ex(__FUNCTION__, sw); if(res == YKPIV_OK) { size_t outlen = 0; - size_t offs = _ykpiv_get_length(data + 1, data + *len, &outlen); + size_t offs = _ykpiv_get_length(data + 1, data + *len, &outlen); if(!offs) { return YKPIV_PARSE_ERROR; } @@ -2166,7 +2573,7 @@ ykpiv_rc ykpiv_get_metadata(ykpiv_state *state, const unsigned char key, unsigne ykpiv_rc ykpiv_auth_getchallenge(ykpiv_state *state, ykpiv_metadata *metadata, uint8_t *challenge, unsigned long *challenge_len) { ykpiv_rc res; - + if (NULL == state) return YKPIV_ARGUMENT_ERROR; if (NULL == metadata) return YKPIV_ARGUMENT_ERROR; if (NULL == challenge) return YKPIV_ARGUMENT_ERROR; diff --git a/lib/ykpiv.h b/lib/ykpiv.h index 02c024c4..907ebaa9 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -91,6 +91,7 @@ extern "C" ykpiv_rc ykpiv_done(ykpiv_state *state); ykpiv_rc ykpiv_validate(ykpiv_state *state, const char *wanted); ykpiv_rc ykpiv_connect(ykpiv_state *state, const char *wanted); + ykpiv_rc ykpiv_connect_ex(ykpiv_state *state, const char *wanted, bool scp11); ykpiv_rc ykpiv_list_readers(ykpiv_state *state, char *readers, size_t *len); ykpiv_rc ykpiv_disconnect(ykpiv_state *state); ykpiv_rc ykpiv_translate_sw(int sw); @@ -723,6 +724,18 @@ extern "C" #define YKPIV_INS_SELECT_APPLICATION 0xa4 #define YKPIV_INS_GET_RESPONSE_APDU 0xc0 +#define GP_CLA 0x80 +#define GP_INS_INITIALIZE_UPDATE 0x50 +#define GP_INS_GET_DATA 0xca +#define GP_CLA_EXTERNAL_AUTHENTICATE 0x84 +#define GP_INS_EXTERNAL_AUTHENTICATE 0x82 +#define GP_INS_INTERNAL_AUTHENTICATE 0x88 +#define GP_HIGHEST_SEC_LEVEL 0x33 +#define GP_INS_PUT_KEY 0xd8 +#define GP_VERSION_FACTORY 0xFF +#define GP_VERSION_DERIVED 0x01 +#define GP_CHAL_SIZE 8 + /* sw is status words, see NIST special publication 800-73-4, section 5.6 */ #define SW_SUCCESS 0x9000 #define SW_ERR_SECURITY_STATUS 0x6982 diff --git a/tool/cmdline.ggo b/tool/cmdline.ggo index 7fe347f5..790fd25d 100644 --- a/tool/cmdline.ggo +++ b/tool/cmdline.ggo @@ -79,3 +79,5 @@ option "sign" - "Sign data" flag off hidden option "stdin-input" - "Read sensitive values from stdin" flag off hidden option "attestation" - "Add attestation cross-signature" flag off option "new-key-algo" m "New management key algorithm to use for action set-mgm-key" values="TDES","AES128","AES192","AES256" enum optional default="TDES" + +option "scp11" - "Open Secure Channel authentication" flag off \ No newline at end of file diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index 9fc0d650..db0c1219 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -2521,7 +2521,7 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - if((rc = ykpiv_connect(state, args_info.reader_arg)) != YKPIV_OK) { + if((rc = ykpiv_connect_ex(state, args_info.reader_arg, args_info.scp11_given)) != YKPIV_OK) { fprintf(stderr, "Failed to connect to yubikey: %s.\n", ykpiv_strerror(rc)); if (rc == YKPIV_PCSC_SERVICE_ERROR) { fprintf(stderr, "Try restarting the PCSC subsystem.\n");