From c23d92ea2808fe6f97b81c4abf762c2a242ae6ab Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Fri, 31 Mar 2023 20:02:06 +0200 Subject: [PATCH] Added support for OTP YubiOTP. It generates a 44 byte string, modhex encoded, following the specification of Yubikey for OTP YubiOTP. When button is pressed, it sends the 44-byte OTP to the host machine, as if it was typed. Signed-off-by: Pol Henarejos --- pico-hsm-sdk | 2 +- src/fido/otp.c | 115 ++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 100 insertions(+), 17 deletions(-) diff --git a/pico-hsm-sdk b/pico-hsm-sdk index 38fb801..46e7d3a 160000 --- a/pico-hsm-sdk +++ b/pico-hsm-sdk @@ -1 +1 @@ -Subproject commit 38fb8018e68011af14c1320f89ad27cfc89d62ab +Subproject commit 46e7d3a181fab92fb8e394ed9b579b2b2f6a54a0 diff --git a/src/fido/otp.c b/src/fido/otp.c index 2cc2f80..2a1f5c3 100644 --- a/src/fido/otp.c +++ b/src/fido/otp.c @@ -23,6 +23,8 @@ #include "version.h" #include "asn1.h" #include "hid/ctap_hid.h" +#include "bsp/board.h" +#include "mbedtls/aes.h" #define FIXED_SIZE 16 #define KEY_SIZE 16 @@ -125,36 +127,70 @@ app_t *otp_select(app_t *a, const uint8_t *aid, uint8_t aid_len) { return NULL; } +uint8_t modhex_tab[] = {'c', 'b', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'r', 't', 'u', 'v'}; +int encode_modhex(const uint8_t *in, size_t len, uint8_t *out) { + for (int l = 0; l < len; l++) { + *out++ = modhex_tab[in[l] >> 4]; + *out++ = modhex_tab[in[l] & 0xf]; + } + return 0; +} +static bool scanned = false; +static uint8_t session_counter[2] = {0}; +extern void scan_all(); +void init_otp() { + if (scanned == false) { + scan_all(); + for (int i = 0; i < 2; i++) { + file_t *ef = search_dynamic_file(EF_OTP_SLOT1 + i); + uint8_t *data = file_get_data(ef); + otp_config_t *otp_config = (otp_config_t *)data; + if (file_has_data(ef) && !(otp_config->tkt_flags & OATH_HOTP) && !(otp_config->cfg_flags & SHORT_TICKET || otp_config->cfg_flags & STATIC_TICKET)) { + uint16_t counter = (data[otp_config_size] << 8) | data[otp_config_size + 1]; + if (++counter <= 0x7fff) { + uint8_t new_data[otp_config_size + 8]; + memcpy(new_data, data, sizeof(new_data)); + new_data[otp_config_size] = counter >> 8; + new_data[otp_config_size + 1] = counter & 0xff; + flash_write_data_to_file(ef, new_data, sizeof(new_data)); + } + } + } + scanned = true; + low_flash_available(); + } +} extern int calculate_oath(uint8_t truncate, const uint8_t *key, size_t key_len, const uint8_t *chal, size_t chal_len); int otp_button_pressed(uint8_t slot) { + init_otp(); #ifndef ENABLE_EMULATION - printf("CB PRESSED slot %d\n", slot); file_t *ef = search_dynamic_file(slot == 1 ? EF_OTP_SLOT1 : EF_OTP_SLOT2); const uint8_t *data = file_get_data(ef); otp_config_t *otp_config = (otp_config_t *)data; + if (file_has_data(ef) == false) { + return 1; + } if (otp_config->tkt_flags & OATH_HOTP) { uint8_t tmp_key[KEY_SIZE + 2]; tmp_key[0] = 0x01; memcpy(tmp_key + 2, otp_config->aes_key, KEY_SIZE); uint64_t imf = 0; - if (file_get_size(ef) == otp_config_size) { + const uint8_t *p = data + otp_config_size; + imf |= (uint64_t)*p++ << 56; + imf |= (uint64_t)*p++ << 48; + imf |= (uint64_t)*p++ << 40; + imf |= (uint64_t)*p++ << 32; + imf |= *p++ << 24; + imf |= *p++ << 16; + imf |= *p++ << 8; + imf |= *p++; + if (imf == 0) { imf = ((otp_config->uid[4] << 8) | otp_config->uid[5]) << 4; } - else { - const uint8_t *p = data + otp_config_size; - imf |= (uint64_t)*p++ << 56; - imf |= (uint64_t)*p++ << 48; - imf |= (uint64_t)*p++ << 40; - imf |= (uint64_t)*p++ << 32; - imf |= *p++ << 24; - imf |= *p++ << 16; - imf |= *p++ << 8; - imf |= *p++; - } uint8_t chal[8] = {imf >> 56, imf >> 48, imf >> 40, imf >> 32, imf >> 24, imf >> 16, imf >> 8, imf & 0xff}; res_APDU_size = 0; int ret = calculate_oath(1, tmp_key, sizeof(tmp_key), chal, sizeof(chal)); @@ -193,7 +229,55 @@ int otp_button_pressed(uint8_t slot) { } } else { + uint8_t otpk[22], *po = otpk; + bool update_counter = false; + uint16_t counter = (data[otp_config_size] << 8) | data[otp_config_size + 1], crc = 0; + uint32_t ts = board_millis() / 1000; + if (counter == 0) { + update_counter = true; + counter = 1; + } + memcpy(po, otp_config->fixed_data, 6); + po += 6; + memcpy(po, otp_config->uid, UID_SIZE); + po += UID_SIZE; + *po++ = session_counter[slot - 1]; + *po++ = counter & 0xff; + *po++ = counter >> 8; + ts >>= 3; + *po++ = ts & 0xff; + *po++ = ts >> 8; + *po++ = ts >> 16; + random_gen(NULL, po, 2); + po += 2; + crc = calculate_crc(otpk + 6, 14); + *po++ = ~crc & 0xff; + *po++ = ~crc >> 8; + mbedtls_aes_context ctx; + mbedtls_aes_init(&ctx); + mbedtls_aes_setkey_enc(&ctx, otp_config->aes_key, 128); + mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, otpk + 6, otpk + 6); + mbedtls_aes_free(&ctx); + uint8_t otp_out[44]; + encode_modhex(otpk, sizeof(otpk), otp_out); + add_keyboard_buffer((const uint8_t *)otp_out, sizeof(otp_out), true); + if (otp_config->tkt_flags & APPEND_CR) { + append_keyboard_buffer((const uint8_t *)"\r", 1); + } + if (++session_counter[slot - 1] == 0) { + if (++counter <= 0x7fff) { + update_counter = true; + } + } + if (update_counter == true) { + uint8_t new_data[otp_config_size + 8]; + memcpy(new_data, data, sizeof(new_data)); + new_data[otp_config_size] = counter >> 8; + new_data[otp_config_size + 1] = counter & 0xff; + flash_write_data_to_file(ef, new_data, sizeof(new_data)); + low_flash_available(); + } } #endif return 0; @@ -208,8 +292,6 @@ int otp_unload() { return CCID_OK; } -static bool scanned = false; -extern void scan_all(); uint16_t otp_status() { if (scanned == false) { scan_all(); @@ -253,7 +335,8 @@ int cmd_otp() { } for (int c = 0; c < otp_config_size; c++) { if (apdu.data[c] != 0) { - flash_write_data_to_file(ef, apdu.data, otp_config_size); + memset(apdu.data + otp_config_size, 0, 8); // Add 8 bytes extra + flash_write_data_to_file(ef, apdu.data, otp_config_size + 8); low_flash_available(); config_seq++; return otp_status();