diff --git a/.gitignore b/.gitignore index bf8ad5c..310e9ed 100644 --- a/.gitignore +++ b/.gitignore @@ -75,12 +75,15 @@ tool/cmdline.h tool/cmdline.o tool/yubico-piv-tool tool/yubico-piv-tool.1 +tool/yubico-piv-tool.exe tool/yubico-piv-tool.o tool/.libs/ tool/libpiv_cmd.la tool/libpiv_cmd_la-cmdline.lo tool/libpiv_cmd_la-cmdline.o tool/libpiv_util.la +tool/openssl-compat.lo +tool/openssl-compat.o tool/util.lo tool/util.o tool/tests/cert_9a.pem diff --git a/configure.ac b/configure.ac index d6b6de8..d833163 100644 --- a/configure.ac +++ b/configure.ac @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -AC_INIT([yubico-piv-tool], [1.5.1]) +AC_INIT([yubico-piv-tool], [1.5.2]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) diff --git a/lib/internal.c b/lib/internal.c index 2846045..e5ba0f2 100644 --- a/lib/internal.c +++ b/lib/internal.c @@ -1,17 +1,28 @@ - -#ifdef _WINDOWS +#ifdef _WIN32 #include +#include +#ifdef _MSC_VER +#define strcasecmp _stricmp +#endif +#else +#include +#include +#endif + +/* the _WINDOWS define really means Windows native crypto-api/CNG */ +#ifdef _WINDOWS #include #include #else #include #include #include -#include #endif +#include #include #include +#include #include "internal.h" @@ -19,6 +30,8 @@ ** Definitions */ +/* crypt defines */ + #ifdef _WINDOWS #define STATUS_SUCCESS ((NTSTATUS)0x00000000L) @@ -90,6 +103,24 @@ struct des_key { #endif +/* config defines */ + +#ifdef _WIN32 +#define _CONFIG_REGKEY "Software\\Yubico\\yubikeypiv" +#else +#define _CONFIG_FILE "/etc/yubico/yubikeypiv.conf" +#endif + +#define _ENV_PREFIX "YUBIKEY_PIV_" + +char *_strip_ws(char *sz); +setting_bool_t _get_bool_config(const char *sz_setting); +setting_bool_t _get_bool_env(const char *sz_setting); + +/* log */ + +const char szLOG_SOURCE[] = "YubiKey PIV Library"; + /* ** Methods */ @@ -271,14 +302,6 @@ des_rc des_encrypt(des_key* key, const unsigned char* in, const size_t inlen, un // reset key usage by encrypting a fake padded block CryptEncrypt(key->hKey, 0, TRUE, 0, buf, (DWORD*)&buflen, (DWORD)buflen); - //if (CALG_3DES == key->alg) { - // // truncate the final pad block - // *outlen = inlen - 8; - //} - //else { - // *outlen = inlen; - //} - #else /* openssl returns void */ @@ -443,3 +466,232 @@ pkcs5_rc pkcs5_pbkdf2_sha1(const unsigned char* password, const size_t cb_passwo return rc; } + +/* settings */ + +char *_strip_ws(char *sz) { + char *psz_head = sz; + char *psz_tail = sz + strlen(sz) - 1; + + /* strip leading whitespace */ + while (isspace(*psz_head)) { + psz_head++; + } + + /* strip trailing whitespace */ + while ((psz_tail >= psz_head) && isspace(*psz_tail)) { + *psz_tail-- = '\0'; + } + + return psz_head; +} + +setting_bool_t _get_bool_config(const char *sz_setting) { + setting_bool_t setting = { false, SETTING_SOURCE_DEFAULT }; + +#ifdef _WIN32 + HKEY hKey = 0; + DWORD dwErr = 0; + DWORD dwValue = 0; + DWORD dwType = 0; + DWORD cbValue = sizeof(dwValue); + + /* MINGW doesn't define RRF_SUBKEY_WOW6464KEY for RegGetValue, so read the traditional way */ + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, _CONFIG_REGKEY, 0, KEY_READ | KEY_WOW64_64KEY, &hKey) == 0) { + if (RegQueryValueExA(hKey, sz_setting, NULL, &dwType, (LPBYTE)&dwValue, &cbValue) == 0) { + setting.value = ((dwType == REG_DWORD) && (dwValue == 1)); + setting.source = SETTING_SOURCE_ADMIN; + } + RegCloseKey(hKey); + hKey = 0; + } + +#else + /* read from config file*/ + char sz_line[256]; + char *psz_name = 0; + char *psz_value = 0; + char sz_name[256] = { 0 }; + char sz_value[256] = { 0 }; + size_t i = 0; + FILE *pf = 0; + + if ((pf = fopen(_CONFIG_FILE, "r"))) { + while (!feof(pf)) { + if (fgets(sz_line, sizeof(sz_line), pf)) { + if (*sz_line == '#') continue; + if (*sz_line == '\r') continue; + if (*sz_line == '\n') continue; + + if (sscanf(sz_line, "%255[^=]=%255s", sz_name, sz_value) == 2); + + /* strip leading/trailing whitespace */ + psz_name = _strip_ws(sz_name); + + if (!strcasecmp(psz_name, sz_setting)) { + psz_value = _strip_ws(sz_value); + + setting.source = SETTING_SOURCE_ADMIN; + setting.value = (!strcmp(psz_value, "1") || !strcasecmp(psz_value, "true")); + break; + } + } + } + fclose(pf); + } + +#endif + + return setting; +} + +setting_bool_t _get_bool_env(const char *sz_setting) { + setting_bool_t setting = { false, SETTING_SOURCE_DEFAULT }; + char *psz_value = NULL; + char sz_name[256] = { 0 }; + + snprintf(sz_name, sizeof(sz_name) - 1, "%s%s", _ENV_PREFIX, sz_setting); + + /* MINGW does not implement getenv_s, only _wgetenv_s */ +#ifdef _MSC_VER + size_t cb_value = 0; + char sz_value[100] = { 0 }; + + if ((getenv_s(&cb_value, sz_value, sizeof(sz_value) - 1, sz_name) == 0) && (cb_value > 0)) { + psz_value = sz_value; + } + +#else + psz_value = getenv(sz_name); + +#endif + + if (psz_value) { + setting.source = SETTING_SOURCE_USER; + setting.value = (!strcmp(psz_value, "1") || !strcasecmp(psz_value, "true")); + } + + return setting; +} + +setting_bool_t setting_get_bool(const char *sz_setting, bool def) { + setting_bool_t setting = { def, SETTING_SOURCE_DEFAULT }; + + setting = _get_bool_config(sz_setting); + + if (setting.source == SETTING_SOURCE_DEFAULT) { + setting = _get_bool_env(sz_setting); + } + + if (setting.source == SETTING_SOURCE_DEFAULT) { + setting.value = def; + } + + return setting; +} + +/* logging */ + +void yc_log_event(uint32_t id, yc_log_level_t level, const char * sz_format, ...) { + char rgsz_message[4096]; + va_list vl; + +#ifdef _WIN32 + HANDLE hLog = NULL; + LPCSTR sz_message = rgsz_message; + DWORD dw_type = EVENTLOG_SUCCESS; +#else + int priority = LOG_INFO; +#endif + + va_start(vl, sz_format); + +#ifdef _WIN32 + + switch (level) { + case YC_LOG_LEVEL_ERROR: + dw_type = EVENTLOG_ERROR_TYPE; + break; + case YC_LOG_LEVEL_WARN: + dw_type = EVENTLOG_WARNING_TYPE; + break; + case YC_LOG_LEVEL_INFO: + dw_type = EVENTLOG_INFORMATION_TYPE; + break; + case YC_LOG_LEVEL_VERBOSE: + dw_type = EVENTLOG_INFORMATION_TYPE; + break; + default: + case YC_LOG_LEVEL_DEBUG: + dw_type = EVENTLOG_SUCCESS; + break; + } + + if (!(hLog = RegisterEventSourceA(NULL, szLOG_SOURCE))) { + goto Cleanup; + } + + /* format message */ + + if (FAILED(StringCbVPrintfA( + rgsz_message, + sizeof(rgsz_message), + sz_format, + vl))) { + goto Cleanup; + }; + + // write to the local event log + + ReportEventA( + hLog, + dw_type, + 0, + (DWORD)id, + NULL, + 1, + 0, + (LPCSTR *)&sz_message, + NULL); + +#else + + switch (level) { + case YC_LOG_LEVEL_ERROR: + priority = LOG_ERR; + break; + case YC_LOG_LEVEL_WARN: + priority = LOG_WARNING; + break; + case YC_LOG_LEVEL_INFO: + priority = LOG_NOTICE; + break; + case YC_LOG_LEVEL_VERBOSE: + priority = LOG_INFO; + break; + default: + case YC_LOG_LEVEL_DEBUG: + priority = LOG_DEBUG; + break; + } + + if (vsnprintf(rgsz_message, sizeof(rgsz_message), sz_format, vl) < 0) { + goto Cleanup; + } + + openlog(szLOG_SOURCE, LOG_PID | LOG_NDELAY, LOG_USER); + syslog(priority, rgsz_message); + closelog(); + +#endif + +Cleanup: + + va_end(vl); +#ifdef _WIN32 + if (hLog) { + DeregisterEventSource(hLog); + } +#endif + +} diff --git a/lib/internal.h b/lib/internal.h index 20968de..bdc1e68 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -146,6 +146,12 @@ typedef enum { PRNG_GENERAL_ERROR = -1 } prng_rc; +typedef struct _ykpiv_version_t { + uint8_t major; + uint8_t minor; + uint8_t patch; +} ykpiv_version_t; + struct ykpiv_state { SCARDCONTEXT context; SCARDHANDLE card; @@ -153,6 +159,8 @@ struct ykpiv_state { char *pin; ykpiv_allocator allocator; bool isNEO; + ykpiv_version_t ver; + uint32_t serial; }; union u_APDU { @@ -191,6 +199,37 @@ void* _ykpiv_realloc(ykpiv_state *state, void *address, size_t size); void _ykpiv_free(ykpiv_state *state, void *data); ykpiv_rc _ykpiv_save_object(ykpiv_state *state, int object_id, unsigned char *indata, size_t len); ykpiv_rc _ykpiv_fetch_object(ykpiv_state *state, int object_id, unsigned char *data, unsigned long *len); +ykpiv_rc _send_data(ykpiv_state *state, APDU *apdu, unsigned char *data, uint32_t *recv_len, int *sw); +ykpiv_rc _ykpiv_get_version(ykpiv_state *state, ykpiv_version_t *p_version); +ykpiv_rc _ykpiv_util_get_serial(ykpiv_state *state, uint32_t *p_serial, bool f_force); + +/* authentication functions not ready for public api */ +ykpiv_rc ykpiv_auth_getchallenge(ykpiv_state *state, uint8_t *challenge, const size_t challenge_len); +ykpiv_rc ykpiv_auth_verifyresponse(ykpiv_state *state, uint8_t *response, const size_t response_len); +ykpiv_rc ykpiv_auth_deauthenticate(ykpiv_state *state); + +typedef enum _setting_source_t { + SETTING_SOURCE_USER, + SETTING_SOURCE_ADMIN, + SETTING_SOURCE_DEFAULT +} setting_source_t; + +typedef struct _setting_bool_t { + bool value; + setting_source_t source; +} setting_bool_t; + +setting_bool_t setting_get_bool(const char *sz_setting, bool f_default); + +typedef enum _yc_log_level_t { + YC_LOG_LEVEL_ERROR, + YC_LOG_LEVEL_WARN, + YC_LOG_LEVEL_INFO, + YC_LOG_LEVEL_VERBOSE, + YC_LOG_LEVEL_DEBUG +} yc_log_level_t; + +void yc_log_event(uint32_t id, yc_log_level_t level, const char *sz_format, ...); #ifdef __cplusplus } diff --git a/lib/util.c b/lib/util.c index 9b83eb7..38bcc40 100644 --- a/lib/util.c +++ b/lib/util.c @@ -699,7 +699,6 @@ ykpiv_rc ykpiv_util_generate_key(ykpiv_state *state, uint8_t slot, uint8_t algor ykpiv_rc res = YKPIV_OK; unsigned char in_data[11]; unsigned char *in_ptr = in_data; - char version[7]; unsigned char data[1024]; unsigned char templ[] = { 0, YKPIV_INS_GENERATE_ASYMMETRIC, 0, 0 }; unsigned long recv_len = sizeof(data); @@ -711,21 +710,47 @@ ykpiv_rc ykpiv_util_generate_key(ykpiv_state *state, uint8_t slot, uint8_t algor uint8_t *ptr_point = NULL; size_t cb_point = 0; + setting_bool_t setting_roca = { 0 }; + const char sz_setting_roca[] = "Enable_Unsafe_Keygen_ROCA"; + const char sz_roca_format[] = "YubiKey serial number %u is affected by vulnerability " + "CVE-2017-15361 (ROCA) and should be replaced. On-chip key generation %s " + "See YSA-2017-01 " + "for additional information on device replacement and mitigation assistance.\n"; + const char sz_roca_allow_user[] = "was permitted by an end-user configuration setting, but is not recommended."; + const char sz_roca_allow_admin[] = "was permitted by an administrator configuration setting, but is not recommended."; + const char sz_roca_block_user[] = "was blocked due to an end-user configuration setting."; + const char sz_roca_block_admin[] = "was blocked due to an administrator configuration setting."; + const char sz_roca_default[] = "was permitted by default, but is not recommended. " + "The default behavior will change in a future Yubico release."; + + if (!state) return YKPIV_ARGUMENT_ERROR; + if (ykpiv_util_devicemodel(state) == DEVTYPE_YK4 && (algorithm == YKPIV_ALGO_RSA1024 || algorithm == YKPIV_ALGO_RSA2048)) { - if ((res = ykpiv_get_version(state, version, sizeof(version))) == YKPIV_OK) { - int major, minor, build; -#pragma warning(push) -#pragma warning(suppress: 4996) // Suppress _CRT_SECURE_NO_WARNINGS complaint (about sscanf_s) - int match = sscanf(version, "%d.%d.%d", &major, &minor, &build); -#pragma warning(pop) - if (match == 3 && major == 4 && (minor < 3 || (minor == 3 && build < 5))) { - fprintf(stderr, "On-chip RSA key generation on this YubiKey has been blocked.\n"); - fprintf(stderr, "Please see https://yubi.co/ysa201701/ for details.\n"); + if ((state->ver.major == 4) && (state->ver.minor < 3 || ((state->ver.minor == 3) && (state->ver.patch < 5)))) { + const char *psz_msg = NULL; + setting_roca = setting_get_bool(sz_setting_roca, true); + + switch (setting_roca.source) { + case SETTING_SOURCE_ADMIN: + psz_msg = setting_roca.value ? sz_roca_allow_admin : sz_roca_block_admin; + break; + + case SETTING_SOURCE_USER: + psz_msg = setting_roca.value ? sz_roca_allow_user : sz_roca_block_user; + break; + + default: + case SETTING_SOURCE_DEFAULT: + psz_msg = sz_roca_default; + break; + } + + fprintf(stderr, sz_roca_format, state->serial, psz_msg); + yc_log_event(1, setting_roca.value ? YC_LOG_LEVEL_WARN : YC_LOG_LEVEL_ERROR, sz_roca_format, state->serial, psz_msg); + + if (!setting_roca.value) { return YKPIV_NOT_SUPPORTED; } - } else { - fprintf(stderr, "Failed to get device version.\n"); - return YKPIV_GENERIC_ERROR; } } @@ -1302,6 +1327,82 @@ uint32_t ykpiv_util_slot_object(uint8_t slot) { return object_id; } +/* caller must make sure that this is wrapped in a transaction for synchronized operation */ +ykpiv_rc _ykpiv_util_get_serial(ykpiv_state *state, uint32_t *p_serial, bool f_force) { + ykpiv_rc res = YKPIV_OK; + APDU apdu; + const uint8_t templ[] = { 0, YKPIV_INS_SELECT_APPLICATION, 0x04, 0 }; + const uint8_t yk_applet[] = { 0xa0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01, 0x01 }; + unsigned char data[0xff]; + uint32_t recv_len = sizeof(data); + int sw; + uint8_t *p_temp = NULL; + + if (!state) { + return YKPIV_ARGUMENT_ERROR; + } + + if (!f_force && (state->serial != 0)) { + if (p_serial) *p_serial = state->serial; + return YKPIV_OK; + } + + /* this function does not use ykpiv_transfer_data because it requires two apdus and selects a different app */ + + memset(apdu.raw, 0, sizeof(apdu)); + apdu.st.ins = YKPIV_INS_SELECT_APPLICATION; + apdu.st.p1 = 0x04; + apdu.st.lc = sizeof(yk_applet); + memcpy(apdu.st.data, yk_applet, sizeof(yk_applet)); + + if ((res = _send_data(state, &apdu, data, &recv_len, &sw)) < YKPIV_OK) { + if (state->verbose) { + fprintf(stderr, "Failed communicating with card: '%s'\n", ykpiv_strerror(res)); + } + goto Cleanup; + } + else if (sw != SW_SUCCESS) { + if (state->verbose) { + fprintf(stderr, "Failed selecting yk application: %04x\n", sw); + } + res = YKPIV_GENERIC_ERROR; + goto Cleanup; + } + + recv_len = sizeof(data); + memset(apdu.raw, 0, sizeof(apdu)); + apdu.st.ins = 0x01; + apdu.st.p1 = 0x10; + apdu.st.lc = 0x00; + + if ((res = _send_data(state, &apdu, data, &recv_len, &sw)) < YKPIV_OK) { + if (state->verbose) { + fprintf(stderr, "Failed communicating with card: '%s'\n", ykpiv_strerror(res)); + } + goto Cleanup; + } + else if (sw != SW_SUCCESS) { + if (state->verbose) { + fprintf(stderr, "Failed retrieving serial number: %04x\n", sw); + } + res = YKPIV_GENERIC_ERROR; + goto Cleanup; + } + + p_temp = (uint8_t*)(&state->serial); + + *p_temp++ = data[3]; + *p_temp++ = data[2]; + *p_temp++ = data[1]; + *p_temp++ = data[0]; + + if (p_serial) *p_serial = state->serial; + +Cleanup: + + return res; +} + static ykpiv_rc _read_certificate(ykpiv_state *state, uint8_t slot, uint8_t *buf, size_t *buf_len) { ykpiv_rc res = YKPIV_OK; uint8_t *ptr = NULL; diff --git a/lib/ykpiv.c b/lib/ykpiv.c index e9d5a92..3dd1bb5 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -80,14 +80,10 @@ static ykpiv_rc _cache_pin(ykpiv_state *state, const char *pin, size_t len); -static ykpiv_rc _send_data(ykpiv_state *state, APDU *apdu, - unsigned char *data, uint32_t *recv_len, int *sw); - unsigned const char aid[] = { 0xa0, 0x00, 0x00, 0x03, 0x08 }; - static void* _default_alloc(void *data, size_t cb) { (void)data; return calloc(cb, 1); @@ -241,7 +237,9 @@ ykpiv_rc _ykpiv_select_application(ykpiv_state *state) { unsigned char data[0xff]; uint32_t recv_len = sizeof(data); int sw; - ykpiv_rc res; + ykpiv_rc res = YKPIV_OK; + + _ykpiv_util_get_serial(state, NULL, false); memset(apdu.raw, 0, sizeof(apdu)); apdu.st.ins = YKPIV_INS_SELECT_APPLICATION; @@ -254,14 +252,15 @@ ykpiv_rc _ykpiv_select_application(ykpiv_state *state) { fprintf(stderr, "Failed communicating with card: '%s'\n", ykpiv_strerror(res)); } return res; - } else if(sw == SW_SUCCESS) { - return YKPIV_OK; - } else { + } + else if(sw != SW_SUCCESS) { if(state->verbose) { fprintf(stderr, "Failed selecting application: %04x\n", sw); } return YKPIV_GENERIC_ERROR; } + + _ykpiv_get_version(state, NULL); } ykpiv_rc _ykpiv_ensure_application_selected(ykpiv_state *state) { @@ -574,7 +573,7 @@ Cleanup: return res; } -static ykpiv_rc _send_data(ykpiv_state *state, APDU *apdu, +ykpiv_rc _send_data(ykpiv_state *state, APDU *apdu, unsigned char *data, uint32_t *recv_len, int *sw) { long rc; unsigned int send_len = (unsigned int)apdu->st.lc + 5; @@ -944,32 +943,63 @@ Cleanup: return res; } -ykpiv_rc ykpiv_get_version(ykpiv_state *state, char *version, size_t len) { +ykpiv_rc _ykpiv_get_version(ykpiv_state *state, ykpiv_version_t *p_version) { APDU apdu; unsigned char data[261]; uint32_t recv_len = sizeof(data); int sw; ykpiv_rc res; - if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; - if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; + if (!state) { + return YKPIV_ARGUMENT_ERROR; + } + + /* get version from state if already from device */ + + if (state->ver.major || state->ver.minor || state->ver.patch) { + if (p_version) { + memcpy(p_version, &(state->ver), sizeof(ykpiv_version_t)); + } + return YKPIV_OK; + } + + /* get version from device */ memset(apdu.raw, 0, sizeof(apdu)); apdu.st.ins = YKPIV_INS_GET_VERSION; if((res = _send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) { - goto Cleanup; + return res; } else if(sw == SW_SUCCESS) { - int result = snprintf(version, len, "%d.%d.%d", data[0], data[1], data[2]); + state->ver.major = data[0]; + state->ver.minor = data[1]; + state->ver.patch = data[2]; + if (p_version) { + memcpy(p_version, &(state->ver), sizeof(ykpiv_version_t)); + } + } else { + res = YKPIV_GENERIC_ERROR; + } + + return res; +} + +ykpiv_rc ykpiv_get_version(ykpiv_state *state, char *version, size_t len) { + ykpiv_rc res; + int result = 0; + ykpiv_version_t ver = {0}; + + if ((res = _ykpiv_begin_transaction(state)) < YKPIV_OK) return YKPIV_PCSC_ERROR; + if ((res = _ykpiv_ensure_application_selected(state)) < YKPIV_OK) goto Cleanup; + + if ((res = _ykpiv_get_version(state, &ver)) >= YKPIV_OK) { + result = snprintf(version, len, "%d.%d.%d", ver.major, ver.minor, ver.patch); if(result < 0) { res = YKPIV_SIZE_ERROR; } - goto Cleanup; - } else { - res = YKPIV_GENERIC_ERROR; - goto Cleanup; } Cleanup: + _ykpiv_end_transaction(state); return res; } @@ -1050,7 +1080,7 @@ 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 (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; if (force_select) { if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; } @@ -1454,15 +1484,18 @@ ykpiv_rc ykpiv_attest(ykpiv_state *state, const unsigned char key, unsigned char bool ret = false; unsigned char templ[] = {0, YKPIV_INS_ATTEST, key, 0}; int sw; + unsigned long ul_data_len; if (state == NULL || data == NULL || data_len == NULL) { return YKPIV_ARGUMENT_ERROR; } + ul_data_len = *data_len; + if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR; if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup; - if ((res = ykpiv_transfer_data(state, templ, NULL, 0, data, data_len, &sw)) != YKPIV_OK) { + if ((res = ykpiv_transfer_data(state, templ, NULL, 0, data, &ul_data_len, &sw)) != YKPIV_OK) { goto Cleanup; } if (SW_SUCCESS != sw) { @@ -1477,6 +1510,8 @@ ykpiv_rc ykpiv_attest(ykpiv_state *state, const unsigned char key, unsigned char goto Cleanup; } + *data_len = (size_t)ul_data_len; + Cleanup: _ykpiv_end_transaction(state); return res; @@ -1562,3 +1597,12 @@ Cleanup: _ykpiv_end_transaction(state); return res; } + +ykpiv_rc ykpiv_auth_deauthenticate(ykpiv_state *state) { + ykpiv_rc res = YKPIV_OK; + + if ((res = _ykpiv_begin_transaction(state)) < YKPIV_OK) return res; + _ykpiv_util_get_serial(state, NULL, true); + _ykpiv_end_transaction(state); + return res; +}