Compare commits
53 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3969fd5136 | ||
|
|
01b197d8ec | ||
|
|
8f7b52a387 | ||
|
|
565ceb7dc4 | ||
|
|
b7590b12d1 | ||
|
|
d8da775218 | ||
|
|
13c7ade20d | ||
|
|
d925e89127 | ||
|
|
7a1131cb1a | ||
|
|
d169f001b6 | ||
|
|
250de29c3c | ||
|
|
7c4a020dc1 | ||
|
|
88063d5d6d | ||
|
|
f43bc9701f | ||
|
|
353d782970 | ||
|
|
cdd2f486aa | ||
|
|
a381e94dda | ||
|
|
e78ec82435 | ||
|
|
584d2f3b33 | ||
|
|
18676990cb | ||
|
|
ed9c46ded0 | ||
|
|
6265992162 | ||
|
|
63b7b9b8d2 | ||
|
|
8db06bf3ac | ||
|
|
77dd1c4b98 | ||
|
|
6a67800057 | ||
|
|
a70e259a90 | ||
|
|
7800056597 | ||
|
|
eeecf513cb | ||
|
|
9b0b584c14 | ||
|
|
1c45685926 | ||
|
|
cff544b485 | ||
|
|
1f805b1df2 | ||
|
|
1d20321d69 | ||
|
|
b42a664ac6 | ||
|
|
2d356a315e | ||
|
|
9bfbc45f84 | ||
|
|
a5a0f3508c | ||
|
|
9c9074c1ef | ||
|
|
022503fdc0 | ||
|
|
dba805dc04 | ||
|
|
bbf474811b | ||
|
|
2eca08161d | ||
|
|
46ada2c1f7 | ||
|
|
5faab169a8 | ||
|
|
3148649f86 | ||
|
|
3c40706aae | ||
|
|
4a64c11740 | ||
|
|
2319abe44e | ||
|
|
a5fe9b5d47 | ||
|
|
d5af2cd8ed | ||
|
|
e994078790 | ||
|
|
d99bcc90ec |
1
.github/workflows/nightly.yml
vendored
1
.github/workflows/nightly.yml
vendored
@@ -25,6 +25,7 @@ jobs:
|
||||
run: |
|
||||
./workflows/autobuild.sh pico
|
||||
./build_pico_fido.sh
|
||||
./workflows/autobuild.sh esp32
|
||||
- name: Update nightly release
|
||||
uses: pyTooling/Actions/releaser@main
|
||||
with:
|
||||
|
||||
@@ -18,12 +18,15 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
if(ESP_PLATFORM)
|
||||
set(DEBUG_APDU 1)
|
||||
set(DENABLE_POWER_ON_RESET 0)
|
||||
set(EXTRA_COMPONENT_DIRS src pico-keys-sdk/src)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
else()
|
||||
|
||||
if(ENABLE_EMULATION)
|
||||
else()
|
||||
set(PICO_USE_FASTEST_SUPPORTED_CLOCK 1)
|
||||
include(pico_sdk_import.cmake)
|
||||
endif()
|
||||
|
||||
@@ -39,14 +42,6 @@ endif()
|
||||
|
||||
add_executable(pico_fido)
|
||||
endif()
|
||||
option(ENABLE_UP_BUTTON "Enable/disable user presence button" ON)
|
||||
if(ENABLE_UP_BUTTON)
|
||||
add_definitions(-DENABLE_UP_BUTTON=1)
|
||||
message(STATUS "User presence with button: \t enabled")
|
||||
else()
|
||||
add_definitions(-DENABLE_UP_BUTTON=0)
|
||||
message(STATUS "User presence with button: \t disabled")
|
||||
endif(ENABLE_UP_BUTTON)
|
||||
|
||||
option(ENABLE_POWER_ON_RESET "Enable/disable power cycle on reset" ON)
|
||||
if(ENABLE_POWER_ON_RESET)
|
||||
@@ -85,6 +80,7 @@ endif()
|
||||
set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/fido.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/files.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/kek.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cmd_register.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cmd_authenticate.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cmd_version.c
|
||||
@@ -116,6 +112,7 @@ endif()
|
||||
|
||||
set(USB_ITF_HID 1)
|
||||
include(pico-keys-sdk/pico_keys_sdk_import.cmake)
|
||||
SET_VERSION(ver_major ver_minor "${CMAKE_CURRENT_LIST_DIR}/src/fido/version.h" 1)
|
||||
if(ESP_PLATFORM)
|
||||
project(pico_fido)
|
||||
endif()
|
||||
@@ -161,5 +158,6 @@ if(ENABLE_EMULATION)
|
||||
target_link_libraries(pico_fido PRIVATE pthread m)
|
||||
else()
|
||||
target_link_libraries(pico_fido PRIVATE pico_keys_sdk pico_stdlib pico_multicore hardware_flash hardware_sync hardware_adc pico_unique_id pico_aon_timer tinyusb_device tinyusb_board)
|
||||
pico_add_extra_outputs(${CMAKE_PROJECT_NAME})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
VERSION_MAJOR="6"
|
||||
VERSION_MINOR="0"
|
||||
VERSION_MINOR="4"
|
||||
SUFFIX="${VERSION_MAJOR}.${VERSION_MINOR}"
|
||||
#if ! [[ -z "${GITHUB_SHA}" ]]; then
|
||||
# SUFFIX="${SUFFIX}.${GITHUB_SHA}"
|
||||
@@ -11,98 +11,13 @@ rm -rf release/*
|
||||
mkdir -p build_release
|
||||
mkdir -p release
|
||||
cd build_release
|
||||
|
||||
for board in 0xcb_helios \
|
||||
adafruit_feather_rp2040_usb_host \
|
||||
adafruit_feather_rp2040 \
|
||||
adafruit_itsybitsy_rp2040 \
|
||||
adafruit_kb2040 \
|
||||
adafruit_macropad_rp2040 \
|
||||
adafruit_qtpy_rp2040 \
|
||||
adafruit_trinkey_qt2040 \
|
||||
amethyst_fpga \
|
||||
archi \
|
||||
arduino_nano_rp2040_connect \
|
||||
cytron_maker_pi_rp2040 \
|
||||
datanoisetv_rp2040_dsp \
|
||||
eetree_gamekit_rp2040 \
|
||||
garatronic_pybstick26_rp2040 \
|
||||
gen4_rp2350_24 \
|
||||
gen4_rp2350_24ct \
|
||||
gen4_rp2350_24t \
|
||||
gen4_rp2350_28 \
|
||||
gen4_rp2350_28ct \
|
||||
gen4_rp2350_28t \
|
||||
gen4_rp2350_32 \
|
||||
gen4_rp2350_32ct \
|
||||
gen4_rp2350_32t \
|
||||
gen4_rp2350_35 \
|
||||
gen4_rp2350_35ct \
|
||||
gen4_rp2350_35t \
|
||||
hellbender_2350A_devboard \
|
||||
ilabs_challenger_rp2350_bconnect \
|
||||
ilabs_challenger_rp2350_wifi_ble \
|
||||
ilabs_opendec02 \
|
||||
melopero_perpetuo_rp2350_lora \
|
||||
melopero_shake_rp2040 \
|
||||
metrotech_xerxes_rp2040 \
|
||||
net8086_usb_interposer \
|
||||
nullbits_bit_c_pro \
|
||||
phyx_rick_tny_rp2350 \
|
||||
pi-plates_micropi \
|
||||
pico \
|
||||
pico_w \
|
||||
pico2 \
|
||||
pimoroni_badger2040 \
|
||||
pimoroni_interstate75 \
|
||||
pimoroni_keybow2040 \
|
||||
pimoroni_motor2040 \
|
||||
pimoroni_pga2040 \
|
||||
pimoroni_pga2350 \
|
||||
pimoroni_pico_plus2_rp2350 \
|
||||
pimoroni_picolipo_4mb \
|
||||
pimoroni_picolipo_16mb \
|
||||
pimoroni_picosystem \
|
||||
pimoroni_plasma2040 \
|
||||
pimoroni_plasma2350 \
|
||||
pimoroni_servo2040 \
|
||||
pimoroni_tiny2040 \
|
||||
pimoroni_tiny2040_2mb \
|
||||
pimoroni_tiny2350 \
|
||||
pololu_3pi_2040_robot \
|
||||
pololu_zumo_2040_robot \
|
||||
seeed_xiao_rp2040 \
|
||||
seeed_xiao_rp2350 \
|
||||
solderparty_rp2040_stamp \
|
||||
solderparty_rp2040_stamp_carrier \
|
||||
solderparty_rp2040_stamp_round_carrier \
|
||||
solderparty_rp2350_stamp_xl \
|
||||
solderparty_rp2350_stamp \
|
||||
sparkfun_micromod \
|
||||
sparkfun_promicro \
|
||||
sparkfun_promicro_rp2350 \
|
||||
sparkfun_thingplus \
|
||||
switchscience_picossci2_conta_base \
|
||||
switchscience_picossci2_dev_board \
|
||||
switchscience_picossci2_micro \
|
||||
switchscience_picossci2_rp2350_breakout \
|
||||
switchscience_picossci2_tiny \
|
||||
tinycircuits_thumby_color_rp2350 \
|
||||
vgaboard \
|
||||
waveshare_rp2040_lcd_0.96 \
|
||||
waveshare_rp2040_lcd_1.28 \
|
||||
waveshare_rp2040_one \
|
||||
waveshare_rp2040_plus_4mb \
|
||||
waveshare_rp2040_plus_16mb \
|
||||
waveshare_rp2040_zero \
|
||||
weact_studio_rp2040_2mb \
|
||||
weact_studio_rp2040_4mb \
|
||||
weact_studio_rp2040_8mb \
|
||||
weact_studio_rp2040_16mb \
|
||||
wiznet_w5100s_evb_pico
|
||||
PICO_SDK_PATH="${PICO_SDK_PATH:-../../pico-sdk}"
|
||||
board_dir=${PICO_SDK_PATH}/src/boards/include/boards
|
||||
for board in "$board_dir"/*
|
||||
do
|
||||
board_name="$(basename -- $board .h)"
|
||||
rm -rf *
|
||||
PICO_SDK_PATH="${PICO_SDK_PATH:-../../pico-sdk}" cmake .. -DPICO_BOARD=$board
|
||||
PICO_SDK_PATH="${PICO_SDK_PATH}" cmake .. -DPICO_BOARD=$board_name
|
||||
make -j`nproc`
|
||||
mv pico_fido.uf2 ../release/pico_fido_$board-$SUFFIX.uf2
|
||||
mv pico_fido.uf2 ../release/pico_fido_$board_name-$SUFFIX.uf2
|
||||
done
|
||||
|
||||
Submodule pico-keys-sdk updated: 8c25e9be87...6e6b524878
@@ -4,6 +4,7 @@
|
||||
IGNORE_UNKNOWN_FILES_FOR_MANAGED_COMPONENTS=1
|
||||
|
||||
CONFIG_TINYUSB=y
|
||||
CONFIG_TINYUSB_TASK_STACK_SIZE=16384
|
||||
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="pico-keys-sdk/config/esp32/partitions.csv"
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "crypto_utils.h"
|
||||
#include "pico_keys.h"
|
||||
#include "apdu.h"
|
||||
#include "kek.h"
|
||||
|
||||
uint32_t usage_timer = 0, initial_usage_time_limit = 0;
|
||||
uint32_t max_usage_time_period = 600 * 1000;
|
||||
@@ -279,6 +280,21 @@ int pinUvAuthTokenUsageTimerObserver() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int check_mkek_encrypted(const uint8_t *dhash) {
|
||||
if (file_get_size(ef_mkek) == MKEK_IV_SIZE + MKEK_KEY_SIZE) {
|
||||
hash_multi(dhash, 16, session_pin); // Only for storing MKEK
|
||||
uint8_t mkek[MKEK_SIZE] = {0};
|
||||
memcpy(mkek, file_get_data(ef_mkek), MKEK_IV_SIZE + MKEK_KEY_SIZE);
|
||||
int ret = store_mkek(mkek);
|
||||
mbedtls_platform_zeroize(mkek, sizeof(mkek));
|
||||
mbedtls_platform_zeroize(session_pin, sizeof(session_pin));
|
||||
if (ret != PICOKEY_OK) {
|
||||
return CTAP2_ERR_PIN_AUTH_INVALID;
|
||||
}
|
||||
}
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
uint8_t new_pin_mismatches = 0;
|
||||
|
||||
int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
@@ -415,12 +431,20 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
if (pin_len < minPin) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION);
|
||||
}
|
||||
uint8_t hsh[34];
|
||||
uint8_t hsh[34], dhash[32];
|
||||
hsh[0] = MAX_PIN_RETRIES;
|
||||
hsh[1] = pin_len;
|
||||
mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, hsh + 2);
|
||||
file_put_data(ef_pin, hsh, 2 + 16);
|
||||
mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, dhash);
|
||||
double_hash_pin(dhash, 16, hsh + 2);
|
||||
file_put_data(ef_pin, hsh, 2 + 32);
|
||||
low_flash_available();
|
||||
|
||||
ret = check_mkek_encrypted(dhash);
|
||||
if (ret != PICOKEY_OK) {
|
||||
CBOR_ERROR(ret);
|
||||
}
|
||||
mbedtls_platform_zeroize(hsh, sizeof(hsh));
|
||||
mbedtls_platform_zeroize(dhash, sizeof(dhash));
|
||||
goto err; //No return
|
||||
}
|
||||
else if (subcommand == 0x4) { //changePIN
|
||||
@@ -462,8 +486,8 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
uint8_t pin_data[18];
|
||||
memcpy(pin_data, file_get_data(ef_pin), 18);
|
||||
uint8_t pin_data[34];
|
||||
memcpy(pin_data, file_get_data(ef_pin), 34);
|
||||
pin_data[0] -= 1;
|
||||
file_put_data(ef_pin, pin_data, sizeof(pin_data));
|
||||
low_flash_available();
|
||||
@@ -474,7 +498,9 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
if (memcmp(paddedNewPin, file_get_data(ef_pin) + 2, 16) != 0) {
|
||||
uint8_t dhash[32];
|
||||
double_hash_pin(paddedNewPin, 16, dhash);
|
||||
if (memcmp(dhash, file_get_data(ef_pin) + 2, 32) != 0) {
|
||||
regenerate();
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
if (retries == 0) {
|
||||
@@ -488,6 +514,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_INVALID);
|
||||
}
|
||||
}
|
||||
hash_multi(paddedNewPin, 16, session_pin);
|
||||
pin_data[0] = MAX_PIN_RETRIES;
|
||||
file_put_data(ef_pin, pin_data, sizeof(pin_data));
|
||||
low_flash_available();
|
||||
@@ -515,12 +542,33 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
uint8_t hsh[34];
|
||||
hsh[0] = MAX_PIN_RETRIES;
|
||||
hsh[1] = pin_len;
|
||||
mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, hsh + 2);
|
||||
mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, dhash);
|
||||
double_hash_pin(dhash, 16, hsh + 2);
|
||||
if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1 &&
|
||||
memcmp(hsh + 2, file_get_data(ef_pin) + 2, 16) == 0) {
|
||||
memcmp(hsh + 2, file_get_data(ef_pin) + 2, 32) == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION);
|
||||
}
|
||||
file_put_data(ef_pin, hsh, 2 + 16);
|
||||
|
||||
uint8_t mkek[MKEK_SIZE] = {0};
|
||||
ret = load_mkek(mkek);
|
||||
if (ret != PICOKEY_OK) {
|
||||
CBOR_ERROR(ret);
|
||||
}
|
||||
file_put_data(ef_pin, hsh, 2 + 32);
|
||||
|
||||
ret = check_mkek_encrypted(dhash);
|
||||
if (ret != PICOKEY_OK) {
|
||||
CBOR_ERROR(ret);
|
||||
}
|
||||
|
||||
hash_multi(dhash, 16, session_pin);
|
||||
ret = store_mkek(mkek);
|
||||
mbedtls_platform_zeroize(mkek, sizeof(mkek));
|
||||
if (ret != PICOKEY_OK) {
|
||||
CBOR_ERROR(ret);
|
||||
}
|
||||
mbedtls_platform_zeroize(hsh, sizeof(hsh));
|
||||
mbedtls_platform_zeroize(dhash, sizeof(dhash));
|
||||
if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1) {
|
||||
uint8_t *tmpf = (uint8_t *) calloc(1, file_get_size(ef_minpin));
|
||||
memcpy(tmpf, file_get_data(ef_minpin), file_get_size(ef_minpin));
|
||||
@@ -570,8 +618,8 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
uint8_t pin_data[18];
|
||||
memcpy(pin_data, file_get_data(ef_pin), 18);
|
||||
uint8_t pin_data[34];
|
||||
memcpy(pin_data, file_get_data(ef_pin), 34);
|
||||
pin_data[0] -= 1;
|
||||
file_put_data(ef_pin, pin_data, sizeof(pin_data));
|
||||
low_flash_available();
|
||||
@@ -582,7 +630,9 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
if (memcmp(paddedNewPin, file_get_data(ef_pin) + 2, 16) != 0) {
|
||||
uint8_t dhash[32];
|
||||
double_hash_pin(paddedNewPin, 16, dhash);
|
||||
if (memcmp(dhash, file_get_data(ef_pin) + 2, 32) != 0) {
|
||||
regenerate();
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
if (retries == 0) {
|
||||
@@ -596,9 +646,19 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
ret = check_mkek_encrypted(paddedNewPin);
|
||||
if (ret != PICOKEY_OK) {
|
||||
CBOR_ERROR(ret);
|
||||
}
|
||||
|
||||
hash_multi(paddedNewPin, 16, session_pin);
|
||||
pin_data[0] = MAX_PIN_RETRIES;
|
||||
new_pin_mismatches = 0;
|
||||
file_put_data(ef_pin, pin_data, sizeof(pin_data));
|
||||
mbedtls_platform_zeroize(pin_data, sizeof(pin_data));
|
||||
mbedtls_platform_zeroize(dhash, sizeof(dhash));
|
||||
|
||||
low_flash_available();
|
||||
file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
|
||||
if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1) {
|
||||
|
||||
@@ -246,14 +246,9 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION);
|
||||
}
|
||||
uint8_t tmp[PHY_MAX_SIZE];
|
||||
uint16_t tmp_len = 0;
|
||||
memset(tmp, 0, sizeof(tmp));
|
||||
if (phy_serialize_data(&phy_data, tmp, &tmp_len) != PICOKEY_OK) {
|
||||
if (phy_save() != PICOKEY_OK) {
|
||||
CBOR_ERROR(CTAP2_ERR_PROCESSING);
|
||||
}
|
||||
file_put_data(ef_phy, tmp, tmp_len);
|
||||
low_flash_available();
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
|
||||
@@ -279,6 +279,8 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
}
|
||||
}
|
||||
|
||||
bool silent = (up == false && uv == false);
|
||||
|
||||
if (allowList_len > 0) {
|
||||
for (size_t e = 0; e < allowList_len; e++) {
|
||||
if (allowList[e].type.present == false || allowList[e].id.present == false) {
|
||||
@@ -288,7 +290,6 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
continue;
|
||||
}
|
||||
if (credential_load(allowList[e].id.data, allowList[e].id.len, rp_id_hash, &creds[creds_len]) != 0) {
|
||||
CBOR_FREE_BYTE_STRING(allowList[e].id);
|
||||
credential_free(&creds[creds_len]);
|
||||
}
|
||||
else {
|
||||
@@ -342,15 +343,32 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
}
|
||||
}
|
||||
if (numberOfCredentials == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_NO_CREDENTIALS);
|
||||
if (silent && allowList_len > 0) {
|
||||
for (size_t e = 0; e < allowList_len; e++) {
|
||||
if (allowList[e].type.present == false || allowList[e].id.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
if (strcmp(allowList[e].type.data, "public-key") != 0) {
|
||||
continue;
|
||||
}
|
||||
if (credential_verify(allowList[e].id.data, allowList[e].id.len, rp_id_hash, true) == 0) {
|
||||
numberOfCredentials++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (numberOfCredentials == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_NO_CREDENTIALS);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < numberOfCredentials; i++) {
|
||||
for (int j = i + 1; j < numberOfCredentials; j++) {
|
||||
if (creds[j].creation > creds[i].creation) {
|
||||
Credential tmp = creds[j];
|
||||
creds[j] = creds[i];
|
||||
creds[i] = tmp;
|
||||
if (!silent) {
|
||||
for (int i = 0; i < numberOfCredentials; i++) {
|
||||
for (int j = i + 1; j < numberOfCredentials; j++) {
|
||||
if (creds[j].creation > creds[i].creation) {
|
||||
Credential tmp = creds[j];
|
||||
creds[j] = creds[i];
|
||||
creds[i] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -380,8 +398,8 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_OPTION);
|
||||
}
|
||||
|
||||
if (up == false && uv == false) {
|
||||
selcred = &creds[0];
|
||||
if (silent && !resident) {
|
||||
// Silent authentication, do nothing
|
||||
}
|
||||
else {
|
||||
selcred = &creds[0];
|
||||
@@ -410,16 +428,18 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
|
||||
int ret = 0;
|
||||
uint8_t largeBlobKey[32] = {0};
|
||||
if (extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
|
||||
ret = credential_derive_large_blob_key(selcred->id.data, selcred->id.len, largeBlobKey);
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PROCESSING);
|
||||
if (selcred) {
|
||||
if (extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
|
||||
ret = credential_derive_large_blob_key(selcred->id.data, selcred->id.len, largeBlobKey);
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PROCESSING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t ext_len = 0;
|
||||
uint8_t ext[512] = {0};
|
||||
if (extensions.present == true) {
|
||||
if (selcred && extensions.present == true) {
|
||||
cbor_encoder_init(&encoder, ext, sizeof(ext), 0);
|
||||
int l = 0;
|
||||
if (options.up == pfalse) {
|
||||
@@ -519,10 +539,7 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
uint8_t *pa = aut_data;
|
||||
memcpy(pa, rp_id_hash, 32); pa += 32;
|
||||
*pa++ = flags;
|
||||
*pa++ = (ctr >> 24) & 0xFF;
|
||||
*pa++ = (ctr >> 16) & 0xFF;
|
||||
*pa++ = (ctr >> 8) & 0xFF;
|
||||
*pa++ = ctr & 0xFF;
|
||||
pa += put_uint32_t_be(ctr, pa);
|
||||
memcpy(pa, ext, ext_len); pa += ext_len;
|
||||
if ((size_t)(pa - aut_data) != aut_data_len) {
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
@@ -533,32 +550,39 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
const mbedtls_md_info_t *md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
|
||||
mbedtls_ecdsa_context ekey;
|
||||
mbedtls_ecdsa_init(&ekey);
|
||||
ret = fido_load_key((int)selcred->curve, selcred->id.data, &ekey);
|
||||
if (ret != 0) {
|
||||
if (derive_key(rp_id_hash, false, selcred->id.data, MBEDTLS_ECP_DP_SECP256R1, &ekey) != 0) {
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
}
|
||||
}
|
||||
if (ekey.grp.id == MBEDTLS_ECP_DP_SECP384R1) {
|
||||
md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
|
||||
}
|
||||
else if (ekey.grp.id == MBEDTLS_ECP_DP_SECP521R1) {
|
||||
md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
|
||||
}
|
||||
ret = mbedtls_md(md, aut_data, aut_data_len + clientDataHash.len, hash);
|
||||
size_t olen = 0;
|
||||
ret = mbedtls_ecdsa_write_signature(&ekey, mbedtls_md_get_type(md), hash, mbedtls_md_get_size(md), sig, sizeof(sig), &olen, random_gen, NULL);
|
||||
if (selcred) {
|
||||
ret = fido_load_key((int)selcred->curve, selcred->id.data, &ekey);
|
||||
if (ret != 0) {
|
||||
if (derive_key(rp_id_hash, false, selcred->id.data, MBEDTLS_ECP_DP_SECP256R1, &ekey) != 0) {
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
}
|
||||
}
|
||||
if (ekey.grp.id == MBEDTLS_ECP_DP_SECP384R1) {
|
||||
md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
|
||||
}
|
||||
else if (ekey.grp.id == MBEDTLS_ECP_DP_SECP521R1) {
|
||||
md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
|
||||
}
|
||||
ret = mbedtls_md(md, aut_data, aut_data_len + clientDataHash.len, hash);
|
||||
ret = mbedtls_ecdsa_write_signature(&ekey, mbedtls_md_get_type(md), hash, mbedtls_md_get_size(md), sig, sizeof(sig), &olen, random_gen, NULL);
|
||||
}
|
||||
else {
|
||||
// Bogus signature
|
||||
olen = 64;
|
||||
memset(sig, 0x0B, olen);
|
||||
}
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
|
||||
uint8_t lfields = 3;
|
||||
if (selcred->opts.present == true && selcred->opts.rk == ptrue) {
|
||||
if (selcred && selcred->opts.present == true && selcred->opts.rk == ptrue) {
|
||||
lfields++;
|
||||
}
|
||||
if (numberOfCredentials > 1 && next == false) {
|
||||
lfields++;
|
||||
}
|
||||
if (extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
|
||||
if (selcred && extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
|
||||
lfields++;
|
||||
}
|
||||
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_CBOR_PAYLOAD, 0);
|
||||
@@ -567,7 +591,12 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, 2));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "id"));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, selcred->id.data, selcred->id.len));
|
||||
if (selcred) {
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, selcred->id.data, selcred->id.len));
|
||||
}
|
||||
else {
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, (uint8_t *)"\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01", 16));
|
||||
}
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "type"));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "public-key"));
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
|
||||
@@ -577,7 +606,7 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, sig, olen));
|
||||
|
||||
if (selcred->opts.present == true && selcred->opts.rk == ptrue) {
|
||||
if (selcred && selcred->opts.present == true && selcred->opts.rk == ptrue) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04));
|
||||
uint8_t lu = 1;
|
||||
if (numberOfCredentials > 1 && allowList_len == 0) {
|
||||
@@ -608,7 +637,7 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x05));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, numberOfCredentials));
|
||||
}
|
||||
if (extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
|
||||
if (selcred && extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x07));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, largeBlobKey, sizeof(largeBlobKey)));
|
||||
}
|
||||
|
||||
@@ -129,10 +129,7 @@ int cbor_large_blobs(const uint8_t *data, size_t len) {
|
||||
uint8_t verify_data[70] = { 0 };
|
||||
memset(verify_data, 0xff, 32);
|
||||
verify_data[32] = 0x0C;
|
||||
verify_data[34] = offset & 0xFF;
|
||||
verify_data[35] = (offset >> 8) & 0xFF;
|
||||
verify_data[36] = (offset >> 16) & 0xFF;
|
||||
verify_data[37] = (offset >> 24) & 0xFF;
|
||||
put_uint32_t_le(offset, verify_data + 34);
|
||||
mbedtls_sha256(set.data, set.len, verify_data + 38, 0);
|
||||
if (verify((uint8_t)pinUvAuthProtocol, paut.data, verify_data, (uint16_t)sizeof(verify_data), pinUvAuthParam.data) != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
|
||||
@@ -286,7 +286,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
if (strcmp(excludeList[e].type.data, (char *)"public-key") != 0) {
|
||||
continue;
|
||||
}
|
||||
Credential ecred;
|
||||
Credential ecred = {0};
|
||||
if (credential_load(excludeList[e].id.data, excludeList[e].id.len, rp_id_hash,
|
||||
&ecred) == 0 &&
|
||||
(ecred.extensions.credProtect != CRED_PROT_UV_REQUIRED ||
|
||||
@@ -409,13 +409,9 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
uint8_t *pa = aut_data;
|
||||
memcpy(pa, rp_id_hash, 32); pa += 32;
|
||||
*pa++ = flags;
|
||||
*pa++ = (ctr >> 24) & 0xFF;
|
||||
*pa++ = (ctr >> 16) & 0xFF;
|
||||
*pa++ = (ctr >> 8) & 0xFF;
|
||||
*pa++ = ctr & 0xFF;
|
||||
pa += put_uint32_t_be(ctr, pa);
|
||||
memcpy(pa, aaguid, 16); pa += 16;
|
||||
*pa++ = ((uint16_t)cred_id_len >> 8) & 0xFF;
|
||||
*pa++ = (uint16_t)cred_id_len & 0xFF;
|
||||
pa += put_uint16_t_be(cred_id_len, pa);
|
||||
memcpy(pa, cred_id, cred_id_len); pa += (uint16_t)cred_id_len;
|
||||
memcpy(pa, cbor_buf, rs); pa += (uint16_t)rs;
|
||||
memcpy(pa, ext, ext_len); pa += (uint16_t)ext_len;
|
||||
@@ -459,14 +455,13 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
if (memcmp(p, "CommissionProfile", 17) == 0) {
|
||||
ret = phy_unserialize_data(user.id.data, user.id.len, &phy_data);
|
||||
if (ret == PICOKEY_OK) {
|
||||
file_put_data(ef_phy, user.id.data, user.id.len);
|
||||
ret = phy_save();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (ret != 0) {
|
||||
if (ret != PICOKEY_OK) {
|
||||
CBOR_ERROR(CTAP2_ERR_PROCESSING);
|
||||
}
|
||||
low_flash_available();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,13 +24,14 @@
|
||||
#ifdef ESP_PLATFORM
|
||||
#include "esp_compat.h"
|
||||
#endif
|
||||
#include "fs/phy.h"
|
||||
|
||||
extern void scan_all();
|
||||
|
||||
int cbor_reset() {
|
||||
#ifndef ENABLE_EMULATION
|
||||
#if defined(ENABLE_POWER_ON_RESET) && ENABLE_POWER_ON_RESET == 1
|
||||
if (board_millis() > 10000) {
|
||||
if (!(phy_data.opts & PHY_OPT_DISABLE_POWER_RESET) && board_millis() > 10000) {
|
||||
return CTAP2_ERR_NOT_ALLOWED;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -255,7 +255,7 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
uint16_t opts = 0;
|
||||
if (file_has_data(ef_phy)) {
|
||||
uint8_t *data = file_get_data(ef_phy);
|
||||
opts = (data[PHY_OPTS] << 8) | data[PHY_OPTS+1];
|
||||
opts = get_uint16_t_be(data + PHY_OPTS);
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
|
||||
@@ -266,6 +266,24 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if (cmd == CTAP_VENDOR_MEMORY) {
|
||||
if (vendorCmd == 0x01) {
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 5));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, flash_free_space()));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x02));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, flash_used_space()));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, flash_total_space()));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, flash_num_files()));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x05));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, flash_size()));
|
||||
}
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION);
|
||||
}
|
||||
}
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ int cmd_authenticate() {
|
||||
int ret = 0;
|
||||
uint8_t *tmp_kh = (uint8_t *) calloc(1, req->keyHandleLen);
|
||||
memcpy(tmp_kh, req->keyHandle, req->keyHandleLen);
|
||||
if (credential_verify(tmp_kh, req->keyHandleLen, req->appId) == 0) {
|
||||
if (credential_verify(tmp_kh, req->keyHandleLen, req->appId, false) == 0) {
|
||||
ret = fido_load_key(FIDO2_CURVE_P256, req->keyHandle, &key);
|
||||
}
|
||||
else {
|
||||
@@ -66,10 +66,7 @@ int cmd_authenticate() {
|
||||
resp->flags = 0;
|
||||
resp->flags |= P1(apdu) == CTAP_AUTH_ENFORCE ? CTAP_AUTH_FLAG_TUP : 0x0;
|
||||
uint32_t ctr = get_sign_counter();
|
||||
resp->ctr[0] = (ctr >> 24) & 0xFF;
|
||||
resp->ctr[1] = (ctr >> 16) & 0xFF;
|
||||
resp->ctr[2] = (ctr >> 8) & 0xFF;
|
||||
resp->ctr[3] = ctr & 0xFF;
|
||||
put_uint32_t_be(ctr, resp->ctr);
|
||||
uint8_t hash[32], sig_base[CTAP_APPID_SIZE + 1 + 4 + CTAP_CHAL_SIZE];
|
||||
memcpy(sig_base, req->appId, CTAP_APPID_SIZE);
|
||||
memcpy(sig_base + CTAP_APPID_SIZE, &resp->flags, sizeof(uint8_t));
|
||||
|
||||
@@ -27,22 +27,52 @@
|
||||
#include "random.h"
|
||||
#include "files.h"
|
||||
#include "pico_keys.h"
|
||||
#include "otp.h"
|
||||
|
||||
int credential_derive_chacha_key(uint8_t *outk);
|
||||
int credential_derive_chacha_key(uint8_t *outk, const uint8_t *);
|
||||
|
||||
int credential_verify(uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash) {
|
||||
static int credential_silent_tag(const uint8_t *cred_id, size_t cred_id_len, uint8_t *outk) {
|
||||
if (otp_key_1) {
|
||||
memcpy(outk, otp_key_1, 32);
|
||||
}
|
||||
else {
|
||||
mbedtls_sha256(pico_serial.id, PICO_UNIQUE_BOARD_ID_SIZE_BYTES, outk, 0);
|
||||
}
|
||||
return mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), outk, 32, cred_id, cred_id_len - CRED_SILENT_TAG_LEN, outk);
|
||||
}
|
||||
|
||||
int credential_verify(uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash, bool silent) {
|
||||
if (cred_id_len < 4 + 12 + 16) {
|
||||
return -1;
|
||||
}
|
||||
uint8_t key[32], *iv = cred_id + 4, *cipher = cred_id + 4 + 12,
|
||||
*tag = cred_id + cred_id_len - 16;
|
||||
memset(key, 0, sizeof(key));
|
||||
credential_derive_chacha_key(key);
|
||||
mbedtls_chachapoly_context chatx;
|
||||
mbedtls_chachapoly_init(&chatx);
|
||||
mbedtls_chachapoly_setkey(&chatx, key);
|
||||
int ret = mbedtls_chachapoly_auth_decrypt(&chatx, cred_id_len - (4 + 12 + 16), iv, rp_id_hash, 32, tag, cipher, cipher);
|
||||
mbedtls_chachapoly_free(&chatx);
|
||||
uint8_t key[32] = {0}, *iv = cred_id + CRED_PROTO_LEN, *cipher = cred_id + CRED_PROTO_LEN + CRED_IV_LEN,
|
||||
*tag = cred_id + cred_id_len - CRED_TAG_LEN;
|
||||
cred_proto_t proto = CRED_PROTO_21;
|
||||
if (memcmp(cred_id, CRED_PROTO_22_S, CRED_PROTO_LEN) == 0) { // New format
|
||||
tag = cred_id + cred_id_len - CRED_SILENT_TAG_LEN - CRED_TAG_LEN;
|
||||
proto = CRED_PROTO_22;
|
||||
}
|
||||
int ret = 0;
|
||||
if (!silent) {
|
||||
int hdr_len = CRED_PROTO_LEN + CRED_IV_LEN + CRED_TAG_LEN;
|
||||
if (proto == CRED_PROTO_22) {
|
||||
hdr_len += CRED_SILENT_TAG_LEN;
|
||||
}
|
||||
credential_derive_chacha_key(key, cred_id);
|
||||
mbedtls_chachapoly_context chatx;
|
||||
mbedtls_chachapoly_init(&chatx);
|
||||
mbedtls_chachapoly_setkey(&chatx, key);
|
||||
ret = mbedtls_chachapoly_auth_decrypt(&chatx, cred_id_len - hdr_len, iv, rp_id_hash, 32, tag, cipher, cipher);
|
||||
mbedtls_chachapoly_free(&chatx);
|
||||
}
|
||||
else {
|
||||
if (proto <= CRED_PROTO_21) {
|
||||
return -1;
|
||||
}
|
||||
uint8_t outk[32];
|
||||
ret = credential_silent_tag(cred_id, cred_id_len, outk);
|
||||
ret = memcmp(outk, cred_id + cred_id_len - CRED_SILENT_TAG_LEN, CRED_SILENT_TAG_LEN);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -113,25 +143,25 @@ int credential_create(CborCharString *rpId,
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
size_t rs = cbor_encoder_get_buffer_size(&encoder, cred_id);
|
||||
*cred_id_len = 4 + 12 + rs + 16;
|
||||
uint8_t key[32];
|
||||
memset(key, 0, sizeof(key));
|
||||
credential_derive_chacha_key(key);
|
||||
uint8_t iv[12];
|
||||
*cred_id_len = CRED_PROTO_LEN + CRED_IV_LEN + rs + CRED_TAG_LEN + CRED_SILENT_TAG_LEN;
|
||||
uint8_t key[32] = {0};
|
||||
credential_derive_chacha_key(key, (const uint8_t *)CRED_PROTO);
|
||||
uint8_t iv[CRED_IV_LEN] = {0};
|
||||
random_gen(NULL, iv, sizeof(iv));
|
||||
mbedtls_chachapoly_context chatx;
|
||||
mbedtls_chachapoly_init(&chatx);
|
||||
mbedtls_chachapoly_setkey(&chatx, key);
|
||||
int ret = mbedtls_chachapoly_encrypt_and_tag(&chatx, rs, iv, rp_id_hash, 32,
|
||||
cred_id + 4 + 12,
|
||||
cred_id + 4 + 12,
|
||||
cred_id + 4 + 12 + rs);
|
||||
cred_id + CRED_PROTO_LEN + CRED_IV_LEN,
|
||||
cred_id + CRED_PROTO_LEN + CRED_IV_LEN,
|
||||
cred_id + CRED_PROTO_LEN + CRED_IV_LEN + rs);
|
||||
mbedtls_chachapoly_free(&chatx);
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
}
|
||||
memcpy(cred_id, CRED_PROTO, 4);
|
||||
memcpy(cred_id + 4, iv, 12);
|
||||
memcpy(cred_id, CRED_PROTO, CRED_PROTO_LEN);
|
||||
memcpy(cred_id + CRED_PROTO_LEN, iv, CRED_IV_LEN);
|
||||
credential_silent_tag(cred_id, *cred_id_len, cred_id + CRED_PROTO_LEN + CRED_IV_LEN + rs + CRED_TAG_LEN);
|
||||
|
||||
err:
|
||||
if (error != CborNoError) {
|
||||
@@ -147,8 +177,12 @@ int credential_load(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *r
|
||||
int ret = 0;
|
||||
CborError error = CborNoError;
|
||||
uint8_t *copy_cred_id = (uint8_t *) calloc(1, cred_id_len);
|
||||
if (!cred) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_CREDENTIAL);
|
||||
}
|
||||
memset(cred, 0, sizeof(Credential));
|
||||
memcpy(copy_cred_id, cred_id, cred_id_len);
|
||||
ret = credential_verify(copy_cred_id, cred_id_len, rp_id_hash);
|
||||
ret = credential_verify(copy_cred_id, cred_id_len, rp_id_hash, false);
|
||||
if (ret != 0) { // U2F?
|
||||
if (cred_id_len != KEY_HANDLE_LEN || verify_key(rp_id_hash, cred_id, NULL) != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_CREDENTIAL);
|
||||
@@ -236,17 +270,19 @@ err:
|
||||
}
|
||||
|
||||
void credential_free(Credential *cred) {
|
||||
CBOR_FREE_BYTE_STRING(cred->rpId);
|
||||
CBOR_FREE_BYTE_STRING(cred->userId);
|
||||
CBOR_FREE_BYTE_STRING(cred->userName);
|
||||
CBOR_FREE_BYTE_STRING(cred->userDisplayName);
|
||||
CBOR_FREE_BYTE_STRING(cred->id);
|
||||
if (cred->extensions.present) {
|
||||
CBOR_FREE_BYTE_STRING(cred->extensions.credBlob);
|
||||
if (cred) {
|
||||
CBOR_FREE_BYTE_STRING(cred->rpId);
|
||||
CBOR_FREE_BYTE_STRING(cred->userId);
|
||||
CBOR_FREE_BYTE_STRING(cred->userName);
|
||||
CBOR_FREE_BYTE_STRING(cred->userDisplayName);
|
||||
CBOR_FREE_BYTE_STRING(cred->id);
|
||||
if (cred->extensions.present) {
|
||||
CBOR_FREE_BYTE_STRING(cred->extensions.credBlob);
|
||||
}
|
||||
cred->present = false;
|
||||
cred->extensions.present = false;
|
||||
cred->opts.present = false;
|
||||
}
|
||||
cred->present = false;
|
||||
cred->extensions.present = false;
|
||||
cred->opts.present = false;
|
||||
}
|
||||
|
||||
int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash) {
|
||||
@@ -344,13 +380,13 @@ int credential_derive_hmac_key(const uint8_t *cred_id, size_t cred_id_len, uint8
|
||||
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
|
||||
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "SLIP-0022", 9, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) CRED_PROTO, 4, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) cred_id, CRED_PROTO_LEN, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "hmac-secret", 11, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, cred_id, cred_id_len, outk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int credential_derive_chacha_key(uint8_t *outk) {
|
||||
int credential_derive_chacha_key(uint8_t *outk, const uint8_t *proto) {
|
||||
memset(outk, 0, 32);
|
||||
int r = 0;
|
||||
if ((r = load_keydev(outk)) != 0) {
|
||||
@@ -359,7 +395,7 @@ int credential_derive_chacha_key(uint8_t *outk) {
|
||||
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
|
||||
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "SLIP-0022", 9, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) CRED_PROTO, 4, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) (proto ? proto : (const uint8_t *)CRED_PROTO), CRED_PROTO_LEN, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "Encryption key", 14, outk);
|
||||
return 0;
|
||||
}
|
||||
@@ -373,7 +409,7 @@ int credential_derive_large_blob_key(const uint8_t *cred_id, size_t cred_id_len,
|
||||
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
|
||||
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "SLIP-0022", 9, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) CRED_PROTO, 4, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) cred_id, CRED_PROTO_LEN, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "largeBlobKey", 12, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, cred_id, cred_id_len, outk);
|
||||
return 0;
|
||||
|
||||
@@ -56,9 +56,23 @@ typedef struct Credential {
|
||||
#define CRED_PROT_UV_OPTIONAL_WITH_LIST 0x02
|
||||
#define CRED_PROT_UV_REQUIRED 0x03
|
||||
|
||||
#define CRED_PROTO "\xf1\xd0\x02\x01"
|
||||
#define CRED_PROTO_21_S "\xf1\xd0\x02\x01"
|
||||
#define CRED_PROTO_22_S "\xf1\xd0\x02\x02"
|
||||
|
||||
extern int credential_verify(uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash);
|
||||
#define CRED_PROTO CRED_PROTO_22_S
|
||||
|
||||
#define CRED_PROTO_LEN 4
|
||||
#define CRED_IV_LEN 12
|
||||
#define CRED_TAG_LEN 16
|
||||
#define CRED_SILENT_TAG_LEN 16
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CRED_PROTO_21 = 0x01,
|
||||
CRED_PROTO_22 = 0x02,
|
||||
} cred_proto_t;
|
||||
|
||||
extern int credential_verify(uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash, bool silent);
|
||||
extern int credential_create(CborCharString *rpId,
|
||||
CborByteString *userId,
|
||||
CborCharString *userName,
|
||||
|
||||
@@ -126,6 +126,7 @@ typedef struct {
|
||||
#define CTAP_VENDOR_UNLOCK 0x03
|
||||
#define CTAP_VENDOR_EA 0x04
|
||||
#define CTAP_VENDOR_PHY_OPTS 0x05
|
||||
#define CTAP_VENDOR_MEMORY 0x06
|
||||
|
||||
#define CTAP_PERMISSION_MC 0x01 // MakeCredential
|
||||
#define CTAP_PERMISSION_GA 0x02 // GetAssertion
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
#include "fido.h"
|
||||
#include "kek.h"
|
||||
#include "pico_keys.h"
|
||||
#include "apdu.h"
|
||||
#include "ctap.h"
|
||||
@@ -46,6 +47,7 @@ pinUvAuthToken_t paut = { 0 };
|
||||
|
||||
uint8_t keydev_dec[32];
|
||||
bool has_keydev_dec = false;
|
||||
uint8_t session_pin[32] = { 0 };
|
||||
|
||||
const uint8_t fido_aid[] = {
|
||||
8,
|
||||
@@ -188,12 +190,15 @@ int load_keydev(uint8_t *key) {
|
||||
}
|
||||
else {
|
||||
memcpy(key, file_get_data(ef_keydev), file_get_size(ef_keydev));
|
||||
|
||||
if (mkek_decrypt(key, 32) != PICOKEY_OK) {
|
||||
return PICOKEY_EXEC_ERROR;
|
||||
}
|
||||
if (otp_key_1 && aes_decrypt(otp_key_1, NULL, 32 * 8, PICO_KEYS_AES_MODE_CBC, key, 32) != PICOKEY_OK) {
|
||||
return PICOKEY_EXEC_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
//return mkek_decrypt(key, file_get_size(ef_keydev));
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
@@ -284,6 +289,7 @@ int derive_key(const uint8_t *app_id, bool new_key, uint8_t *key_handle, int cur
|
||||
int scan_files() {
|
||||
ef_keydev = search_by_fid(EF_KEY_DEV, NULL, SPECIFY_EF);
|
||||
ef_keydev_enc = search_by_fid(EF_KEY_DEV_ENC, NULL, SPECIFY_EF);
|
||||
ef_mkek = search_by_fid(EF_MKEK, NULL, SPECIFY_EF);
|
||||
if (ef_keydev) {
|
||||
if (!file_has_data(ef_keydev) && !file_has_data(ef_keydev_enc)) {
|
||||
printf("KEY DEVICE is empty. Generating SECP256R1 curve...");
|
||||
@@ -316,13 +322,33 @@ int scan_files() {
|
||||
else {
|
||||
printf("FATAL ERROR: KEY DEV not found in memory!\r\n");
|
||||
}
|
||||
if (ef_mkek) { // No encrypted MKEK
|
||||
if (!file_has_data(ef_mkek)) {
|
||||
uint8_t mkek[MKEK_IV_SIZE + MKEK_KEY_SIZE];
|
||||
random_gen(NULL, mkek, sizeof(mkek));
|
||||
file_put_data(ef_mkek, mkek, sizeof(mkek));
|
||||
int ret = aes_encrypt_cfb_256(MKEK_KEY(mkek), MKEK_IV(mkek), file_get_data(ef_keydev), 32);
|
||||
mbedtls_platform_zeroize(mkek, sizeof(mkek));
|
||||
if (ret != 0) {
|
||||
printf("FATAL ERROR: MKEK encryption failed!\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("FATAL ERROR: MKEK not found in memory!\r\n");
|
||||
}
|
||||
ef_certdev = search_by_fid(EF_EE_DEV, NULL, SPECIFY_EF);
|
||||
if (ef_certdev) {
|
||||
if (!file_has_data(ef_certdev)) {
|
||||
uint8_t cert[2048];
|
||||
uint8_t cert[2048], outk[32];
|
||||
memset(outk, 0, sizeof(outk));
|
||||
int ret = 0;
|
||||
if ((ret = load_keydev(outk)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
mbedtls_ecdsa_context key;
|
||||
mbedtls_ecdsa_init(&key);
|
||||
int ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &key, file_get_data(ef_keydev), file_get_size(ef_keydev));
|
||||
ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &key, outk, sizeof(outk));
|
||||
if (ret != 0) {
|
||||
mbedtls_ecdsa_free(&key);
|
||||
return ret;
|
||||
@@ -354,6 +380,13 @@ int scan_files() {
|
||||
printf("FATAL ERROR: Global counter not found in memory!\r\n");
|
||||
}
|
||||
ef_pin = search_by_fid(EF_PIN, NULL, SPECIFY_EF);
|
||||
if (file_get_size(ef_pin) == 18) { // Upgrade PIN storage
|
||||
uint8_t pin_data[34] = { 0 }, dhash[32];
|
||||
memcpy(pin_data, file_get_data(ef_pin), 18);
|
||||
double_hash_pin(pin_data + 2, 16, dhash);
|
||||
memcpy(pin_data + 2, dhash, 32);
|
||||
file_put_data(ef_pin, pin_data, 34);
|
||||
}
|
||||
ef_authtoken = search_by_fid(EF_AUTHTOKEN, NULL, SPECIFY_EF);
|
||||
if (ef_authtoken) {
|
||||
if (!file_has_data(ef_authtoken)) {
|
||||
@@ -371,6 +404,7 @@ int scan_files() {
|
||||
if (!file_has_data(ef_largeblob)) {
|
||||
file_put_data(ef_largeblob, (const uint8_t *) "\x80\x76\xbe\x8b\x52\x8d\x00\x75\xf7\xaa\xe9\x8d\x6f\xa5\x7a\x6d\x3c", 17);
|
||||
}
|
||||
|
||||
low_flash_available();
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
@@ -389,12 +423,10 @@ void init_fido() {
|
||||
bool wait_button_pressed() {
|
||||
uint32_t val = EV_PRESS_BUTTON;
|
||||
#ifndef ENABLE_EMULATION
|
||||
#if defined(ENABLE_UP_BUTTON) && ENABLE_UP_BUTTON == 1
|
||||
queue_try_add(&card_to_usb_q, &val);
|
||||
do {
|
||||
queue_remove_blocking(&usb_to_card_q, &val);
|
||||
} while (val != EV_BUTTON_PRESSED && val != EV_BUTTON_TIMEOUT);
|
||||
#endif
|
||||
#endif
|
||||
return val == EV_BUTTON_TIMEOUT;
|
||||
}
|
||||
@@ -402,21 +434,18 @@ bool wait_button_pressed() {
|
||||
uint32_t user_present_time_limit = 0;
|
||||
|
||||
bool check_user_presence() {
|
||||
#if defined(ENABLE_UP_BUTTON) && ENABLE_UP_BUTTON == 1
|
||||
if (user_present_time_limit == 0 ||
|
||||
user_present_time_limit + TRANSPORT_TIME_LIMIT < board_millis()) {
|
||||
if (user_present_time_limit == 0 || user_present_time_limit + TRANSPORT_TIME_LIMIT < board_millis()) {
|
||||
if (wait_button_pressed() == true) { //timeout
|
||||
return false;
|
||||
}
|
||||
//user_present_time_limit = board_millis();
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t get_sign_counter() {
|
||||
uint8_t *caddr = file_get_data(ef_counter);
|
||||
return (*caddr) | (*(caddr + 1) << 8) | (*(caddr + 2) << 16) | (*(caddr + 3) << 24);
|
||||
return get_uint32_t_le(caddr);
|
||||
}
|
||||
|
||||
uint8_t get_opts() {
|
||||
|
||||
@@ -130,4 +130,6 @@ extern uint32_t user_present_time_limit;
|
||||
extern pinUvAuthToken_t paut;
|
||||
extern int verify(uint8_t protocol, const uint8_t *key, const uint8_t *data, uint16_t len, uint8_t *sign);
|
||||
|
||||
extern uint8_t session_pin[32];
|
||||
|
||||
#endif //_FIDO_H
|
||||
|
||||
@@ -18,39 +18,20 @@
|
||||
#include "files.h"
|
||||
|
||||
file_t file_entries[] = {
|
||||
{ .fid = 0x3f00, .parent = 0xff, .name = NULL, .type = FILE_TYPE_DF, .data = NULL,
|
||||
.ef_structure = 0, .acl = { 0 } }, // MF
|
||||
{ .fid = EF_KEY_DEV, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH,
|
||||
.data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // Device Key
|
||||
{ .fid = EF_KEY_DEV_ENC, .parent = 0, .name = NULL,
|
||||
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
|
||||
.ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // Device Key Enc
|
||||
{ .fid = EF_EE_DEV, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH,
|
||||
.data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // End Entity Certificate Device
|
||||
{ .fid = EF_EE_DEV_EA, .parent = 0, .name = NULL,
|
||||
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
|
||||
.ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // End Entity Enterprise Attestation Certificate
|
||||
{ .fid = EF_COUNTER, .parent = 0, .name = NULL,
|
||||
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
|
||||
.ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // Global counter
|
||||
{ .fid = EF_PIN, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH,
|
||||
.data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // PIN
|
||||
{ .fid = EF_AUTHTOKEN, .parent = 0, .name = NULL,
|
||||
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
|
||||
.ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // AUTH TOKEN
|
||||
{ .fid = EF_MINPINLEN, .parent = 0, .name = NULL,
|
||||
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
|
||||
.ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // MIN PIN LENGTH
|
||||
{ .fid = EF_OPTS, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH,
|
||||
.data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // Global options
|
||||
{ .fid = EF_LARGEBLOB, .parent = 0, .name = NULL,
|
||||
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
|
||||
.ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // Large Blob
|
||||
{ .fid = EF_OTP_PIN, .parent = 0, .name = NULL,
|
||||
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH,
|
||||
.data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } },
|
||||
{ .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_NOT_KNOWN, .data = NULL,
|
||||
.ef_structure = 0, .acl = { 0 } } //end
|
||||
{ .fid = 0x3f00, .parent = 0xff, .name = NULL, .type = FILE_TYPE_DF, .data = NULL, .ef_structure = 0, .acl = { 0 } }, // MF
|
||||
{ .fid = EF_KEY_DEV, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // Device Key
|
||||
{ .fid = EF_KEY_DEV_ENC, .parent = 0, .name = NULL,.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // Device Key Enc
|
||||
{ .fid = EF_MKEK, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // MKEK
|
||||
{ .fid = EF_EE_DEV, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // End Entity Certificate Device
|
||||
{ .fid = EF_EE_DEV_EA, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // End Entity Enterprise Attestation Certificate
|
||||
{ .fid = EF_COUNTER, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // Global counter
|
||||
{ .fid = EF_PIN, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // PIN
|
||||
{ .fid = EF_AUTHTOKEN, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // AUTH TOKEN
|
||||
{ .fid = EF_MINPINLEN, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // MIN PIN LENGTH
|
||||
{ .fid = EF_OPTS, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // Global options
|
||||
{ .fid = EF_LARGEBLOB, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // Large Blob
|
||||
{ .fid = EF_OTP_PIN, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } },
|
||||
{ .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_NOT_KNOWN, .data = NULL, .ef_structure = 0, .acl = { 0 } } //end
|
||||
};
|
||||
|
||||
const file_t *MF = &file_entries[0];
|
||||
@@ -62,3 +43,4 @@ file_t *ef_pin = NULL;
|
||||
file_t *ef_authtoken = NULL;
|
||||
file_t *ef_keydev_enc = NULL;
|
||||
file_t *ef_largeblob = NULL;
|
||||
file_t *ef_mkek = NULL;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#define EF_KEY_DEV 0xCC00
|
||||
#define EF_KEY_DEV_ENC 0xCC01
|
||||
#define EF_MKEK 0xCC0F
|
||||
#define EF_EE_DEV 0xCE00
|
||||
#define EF_EE_DEV_EA 0xCE01
|
||||
#define EF_COUNTER 0xC000
|
||||
@@ -46,5 +47,6 @@ extern file_t *ef_pin;
|
||||
extern file_t *ef_authtoken;
|
||||
extern file_t *ef_keydev_enc;
|
||||
extern file_t *ef_largeblob;
|
||||
extern file_t *ef_mkek;
|
||||
|
||||
#endif //_FILES_H_
|
||||
|
||||
137
src/fido/kek.c
Normal file
137
src/fido/kek.c
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* This file is part of the Pico Fido distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "fido.h"
|
||||
#include "pico_keys.h"
|
||||
#include "stdlib.h"
|
||||
#if !defined(ENABLE_EMULATION) && !defined(ESP_PLATFORM)
|
||||
#include "pico/stdlib.h"
|
||||
#endif
|
||||
#include "kek.h"
|
||||
#include "crypto_utils.h"
|
||||
#include "random.h"
|
||||
#include "mbedtls/md.h"
|
||||
#include "mbedtls/cmac.h"
|
||||
#include "mbedtls/rsa.h"
|
||||
#include "mbedtls/ecdsa.h"
|
||||
#include "mbedtls/chachapoly.h"
|
||||
#include "files.h"
|
||||
#include "otp.h"
|
||||
|
||||
extern uint8_t session_pin[32];
|
||||
uint8_t mkek_mask[MKEK_KEY_SIZE];
|
||||
bool has_mkek_mask = false;
|
||||
|
||||
#define POLY 0xedb88320
|
||||
|
||||
uint32_t crc32c(const uint8_t *buf, size_t len) {
|
||||
uint32_t crc = 0xffffffff;
|
||||
while (len--) {
|
||||
crc ^= *buf++;
|
||||
for (int k = 0; k < 8; k++) {
|
||||
crc = (crc >> 1) ^ (POLY & (0 - (crc & 1)));
|
||||
}
|
||||
}
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
void mkek_masked(uint8_t *mkek, const uint8_t *mask) {
|
||||
if (mask) {
|
||||
for (int i = 0; i < MKEK_KEY_SIZE; i++) {
|
||||
MKEK_KEY(mkek)[i] ^= mask[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int load_mkek(uint8_t *mkek) {
|
||||
file_t *tf = search_file(EF_MKEK);
|
||||
if (file_has_data(tf)) {
|
||||
memcpy(mkek, file_get_data(tf), MKEK_SIZE);
|
||||
}
|
||||
|
||||
if (has_mkek_mask) {
|
||||
mkek_masked(mkek, mkek_mask);
|
||||
}
|
||||
if (file_get_size(tf) == MKEK_SIZE) {
|
||||
int ret = aes_decrypt_cfb_256(session_pin, MKEK_IV(mkek), MKEK_KEY(mkek), MKEK_KEY_SIZE + MKEK_KEY_CS_SIZE);
|
||||
if (ret != 0) {
|
||||
return PICOKEY_EXEC_ERROR;
|
||||
}
|
||||
if (crc32c(MKEK_KEY(mkek), MKEK_KEY_SIZE) != *(uint32_t *) MKEK_CHECKSUM(mkek)) {
|
||||
return PICOKEY_WRONG_DKEK;
|
||||
}
|
||||
if (otp_key_1) {
|
||||
mkek_masked(mkek, otp_key_1);
|
||||
}
|
||||
}
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
void release_mkek(uint8_t *mkek) {
|
||||
mbedtls_platform_zeroize(mkek, MKEK_SIZE);
|
||||
}
|
||||
|
||||
int store_mkek(const uint8_t *mkek) {
|
||||
uint8_t tmp_mkek[MKEK_SIZE];
|
||||
if (mkek == NULL) {
|
||||
const uint8_t *rd = random_bytes_get(MKEK_IV_SIZE + MKEK_KEY_SIZE);
|
||||
memcpy(tmp_mkek, rd, MKEK_IV_SIZE + MKEK_KEY_SIZE);
|
||||
}
|
||||
else {
|
||||
memcpy(tmp_mkek, mkek, MKEK_SIZE);
|
||||
}
|
||||
if (otp_key_1) {
|
||||
mkek_masked(tmp_mkek, otp_key_1);
|
||||
}
|
||||
*(uint32_t *) MKEK_CHECKSUM(tmp_mkek) = crc32c(MKEK_KEY(tmp_mkek), MKEK_KEY_SIZE);
|
||||
uint8_t tmp_mkek_pin[MKEK_SIZE];
|
||||
memcpy(tmp_mkek_pin, tmp_mkek, MKEK_SIZE);
|
||||
file_t *tf = search_file(EF_MKEK);
|
||||
if (!tf) {
|
||||
release_mkek(tmp_mkek);
|
||||
release_mkek(tmp_mkek_pin);
|
||||
return PICOKEY_ERR_FILE_NOT_FOUND;
|
||||
}
|
||||
aes_encrypt_cfb_256(session_pin, MKEK_IV(tmp_mkek_pin), MKEK_KEY(tmp_mkek_pin), MKEK_KEY_SIZE + MKEK_KEY_CS_SIZE);
|
||||
file_put_data(tf, tmp_mkek_pin, MKEK_SIZE);
|
||||
release_mkek(tmp_mkek_pin);
|
||||
low_flash_available();
|
||||
release_mkek(tmp_mkek);
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
int mkek_encrypt(uint8_t *data, uint16_t len) {
|
||||
int r;
|
||||
uint8_t mkek[MKEK_SIZE + 4];
|
||||
if ((r = load_mkek(mkek)) != PICOKEY_OK) {
|
||||
return r;
|
||||
}
|
||||
r = aes_encrypt_cfb_256(MKEK_KEY(mkek), MKEK_IV(mkek), data, len);
|
||||
release_mkek(mkek);
|
||||
return r;
|
||||
}
|
||||
|
||||
int mkek_decrypt(uint8_t *data, uint16_t len) {
|
||||
int r;
|
||||
uint8_t mkek[MKEK_SIZE + 4];
|
||||
if ((r = load_mkek(mkek)) != PICOKEY_OK) {
|
||||
return r;
|
||||
}
|
||||
r = aes_decrypt_cfb_256(MKEK_KEY(mkek), MKEK_IV(mkek), data, len);
|
||||
release_mkek(mkek);
|
||||
return r;
|
||||
}
|
||||
46
src/fido/kek.h
Normal file
46
src/fido/kek.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* This file is part of the Pico Fido distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _KEK_H_
|
||||
#define _KEK_H_
|
||||
|
||||
#include "crypto_utils.h"
|
||||
#if defined(ENABLE_EMULATION) || defined(ESP_PLATFORM)
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
|
||||
extern int load_mkek(uint8_t *);
|
||||
extern int store_mkek(const uint8_t *);
|
||||
extern void init_mkek();
|
||||
extern void release_mkek(uint8_t *);
|
||||
extern int mkek_encrypt(uint8_t *data, uint16_t len);
|
||||
extern int mkek_decrypt(uint8_t *data, uint16_t len);
|
||||
|
||||
#define MKEK_IV_SIZE (IV_SIZE)
|
||||
#define MKEK_KEY_SIZE (32)
|
||||
#define MKEK_KEY_CS_SIZE (4)
|
||||
#define MKEK_SIZE (MKEK_IV_SIZE + MKEK_KEY_SIZE + MKEK_KEY_CS_SIZE)
|
||||
#define MKEK_IV(p) (p)
|
||||
#define MKEK_KEY(p) (MKEK_IV(p) + MKEK_IV_SIZE)
|
||||
#define MKEK_CHECKSUM(p) (MKEK_KEY(p) + MKEK_KEY_SIZE)
|
||||
#define DKEK_KEY_SIZE (32)
|
||||
|
||||
extern uint8_t mkek_mask[MKEK_KEY_SIZE];
|
||||
extern bool has_mkek_mask;
|
||||
|
||||
#endif
|
||||
@@ -65,7 +65,7 @@ bool cap_supported(uint16_t cap) {
|
||||
if (tag == TAG_USB_ENABLED) {
|
||||
uint16_t ecaps = tag_data[0];
|
||||
if (tag_len == 2) {
|
||||
ecaps = (tag_data[0] << 8) | tag_data[1];
|
||||
ecaps = get_uint16_t_be(tag_data);
|
||||
}
|
||||
return ecaps & cap;
|
||||
}
|
||||
@@ -94,9 +94,6 @@ int man_get_config() {
|
||||
res_APDU[res_APDU_size++] = PICO_FIDO_VERSION_MAJOR;
|
||||
res_APDU[res_APDU_size++] = PICO_FIDO_VERSION_MINOR;
|
||||
res_APDU[res_APDU_size++] = 0;
|
||||
res_APDU[res_APDU_size++] = TAG_NFC_SUPPORTED;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = 0x00;
|
||||
if (!file_has_data(ef)) {
|
||||
res_APDU[res_APDU_size++] = TAG_USB_ENABLED;
|
||||
res_APDU[res_APDU_size++] = 2;
|
||||
@@ -108,9 +105,6 @@ int man_get_config() {
|
||||
res_APDU[res_APDU_size++] = TAG_CONFIG_LOCK;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = 0x00;
|
||||
res_APDU[res_APDU_size++] = TAG_NFC_ENABLED;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = 0x00;
|
||||
}
|
||||
else {
|
||||
memcpy(res_APDU + res_APDU_size, file_get_data(ef), file_get_size(ef));
|
||||
|
||||
@@ -395,15 +395,7 @@ int cmd_calculate() {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
if ((key.data[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
|
||||
uint64_t v =
|
||||
((uint64_t) chal.data[0] << 56) |
|
||||
((uint64_t) chal.data[1] << 48) |
|
||||
((uint64_t) chal.data[2] << 40) |
|
||||
((uint64_t) chal.data[3] << 32) |
|
||||
((uint64_t) chal.data[4] << 24) |
|
||||
((uint64_t) chal.data[5] << 16) |
|
||||
((uint64_t) chal.data[6] << 8) |
|
||||
(uint64_t) chal.data[7];
|
||||
uint64_t v = get_uint64_t_be(chal.data);
|
||||
size_t ef_size = file_get_size(ef);
|
||||
v++;
|
||||
uint8_t *tmp = (uint8_t *) calloc(1, ef_size);
|
||||
@@ -411,14 +403,7 @@ int cmd_calculate() {
|
||||
asn1_ctx_t ctxt;
|
||||
asn1_ctx_init(tmp, (uint16_t)ef_size, &ctxt);
|
||||
asn1_find_tag(&ctxt, TAG_IMF, &chal);
|
||||
chal.data[0] = (v >> 56) & 0xFF;
|
||||
chal.data[1] = (v >> 48) & 0xFF;
|
||||
chal.data[2] = (v >> 40) & 0xFF;
|
||||
chal.data[3] = (v >> 32) & 0xFF;
|
||||
chal.data[4] = (v >> 24) & 0xFF;
|
||||
chal.data[5] = (v >> 16) & 0xFF;
|
||||
chal.data[6] = (v >> 8) & 0xFF;
|
||||
chal.data[7] = v & 0xff;
|
||||
put_uint64_t_be(v, chal.data);
|
||||
file_put_data(ef, tmp, (uint16_t)ef_size);
|
||||
low_flash_available();
|
||||
free(tmp);
|
||||
@@ -577,14 +562,14 @@ int cmd_verify_hotp() {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
if (asn1_find_tag(&ctxi, TAG_RESPONSE, &code) == true) {
|
||||
code_int = (code.data[0] << 24) | (code.data[1] << 16) | (code.data[2] << 8) | code.data[3];
|
||||
code_int = get_uint32_t_be(code.data);
|
||||
}
|
||||
|
||||
int ret = calculate_oath(0x01, key.data, key.len, chal.data, chal.len);
|
||||
if (ret != PICOKEY_OK) {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
uint32_t res_int = (res_APDU[2] << 24) | (res_APDU[3] << 16) | (res_APDU[4] << 8) | res_APDU[5];
|
||||
uint32_t res_int = get_uint32_t_be(res_APDU + 2);
|
||||
if (res_APDU[1] == 6) {
|
||||
res_int %= (uint32_t) 1e6;
|
||||
}
|
||||
@@ -599,10 +584,52 @@ int cmd_verify_hotp() {
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int cmd_rename() {
|
||||
asn1_ctx_t ctxi, name = { 0 }, new_name = { 0 };
|
||||
if (apdu.data[0] != TAG_NAME) {
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
|
||||
if (asn1_find_tag(&ctxi, TAG_NAME, &name) == false) {
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
|
||||
asn1_ctx_init(name.data + name.len, (uint16_t)(apdu.nc - (name.data + name.len - apdu.data)), &ctxi);
|
||||
if (asn1_find_tag(&ctxi, TAG_NAME, &new_name) == false) {
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
file_t *ef = find_oath_cred(name.data, name.len);
|
||||
if (file_has_data(ef) == false) {
|
||||
return SW_DATA_INVALID();
|
||||
}
|
||||
uint8_t *fdata = file_get_data(ef);
|
||||
uint16_t fsize = file_get_size(ef);
|
||||
asn1_ctx_init(fdata, fsize, &ctxi);
|
||||
if (asn1_find_tag(&ctxi, TAG_NAME, &name) == false) {
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
uint8_t *new_data;
|
||||
if (new_name.len > name.len) {
|
||||
new_data = (uint8_t *) calloc(1, file_get_size(ef) + new_name.len - name.len);
|
||||
}
|
||||
else {
|
||||
new_data = (uint8_t *) calloc(1, file_get_size(ef));
|
||||
}
|
||||
memcpy(new_data, fdata, name.data - fdata);
|
||||
*(new_data + (name.data - fdata) - 1) = new_name.len;
|
||||
memcpy(new_data + (name.data - fdata), new_name.data, new_name.len);
|
||||
memcpy(new_data + (name.data - fdata) + new_name.len, name.data + name.len, fsize - (name.data + name.len - fdata));
|
||||
file_put_data(ef, new_data, fsize + new_name.len - name.len);
|
||||
low_flash_available();
|
||||
free(new_data);
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
#define INS_PUT 0x01
|
||||
#define INS_DELETE 0x02
|
||||
#define INS_SET_CODE 0x03
|
||||
#define INS_RESET 0x04
|
||||
#define INS_RENAME 0x05
|
||||
#define INS_LIST 0xa1
|
||||
#define INS_CALCULATE 0xa2
|
||||
#define INS_VALIDATE 0xa3
|
||||
@@ -618,6 +645,7 @@ static const cmd_t cmds[] = {
|
||||
{ INS_DELETE, cmd_delete },
|
||||
{ INS_SET_CODE, cmd_set_code },
|
||||
{ INS_RESET, cmd_reset },
|
||||
{ INS_RENAME, cmd_rename },
|
||||
{ INS_LIST, cmd_list },
|
||||
{ INS_VALIDATE, cmd_validate },
|
||||
{ INS_CALCULATE, cmd_calculate },
|
||||
|
||||
102
src/fido/otp.c
102
src/fido/otp.c
@@ -111,7 +111,7 @@ typedef struct otp_config {
|
||||
}) otp_config_t;
|
||||
|
||||
#define otp_config_size sizeof(otp_config_t)
|
||||
uint16_t otp_status();
|
||||
uint16_t otp_status(bool is_otp);
|
||||
|
||||
int otp_process_apdu();
|
||||
int otp_unload();
|
||||
@@ -140,10 +140,7 @@ int otp_select(app_t *a, uint8_t force) {
|
||||
else {
|
||||
config_seq = 0;
|
||||
}
|
||||
otp_status();
|
||||
memmove(res_APDU, res_APDU + 1, 6);
|
||||
res_APDU_size = 6;
|
||||
apdu.ne = res_APDU_size;
|
||||
otp_status(false);
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
return PICOKEY_ERR_FILE_NOT_FOUND;
|
||||
@@ -169,12 +166,11 @@ void init_otp() {
|
||||
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];
|
||||
uint16_t counter = get_uint16_t_be(data + otp_config_size);
|
||||
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;
|
||||
put_uint16_t_be(counter, new_data + otp_config_size);
|
||||
file_put_data(ef, new_data, sizeof(new_data));
|
||||
}
|
||||
}
|
||||
@@ -228,25 +224,18 @@ int otp_button_pressed(uint8_t slot) {
|
||||
memcpy(tmp_key + 2, otp_config->aes_key, KEY_SIZE);
|
||||
uint64_t imf = 0;
|
||||
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++;
|
||||
imf = get_uint64_t_be(p);
|
||||
p += 8;
|
||||
if (imf == 0) {
|
||||
imf = ((otp_config->uid[4] << 8) | otp_config->uid[5]) << 4;
|
||||
imf = get_uint16_t_be(otp_config->uid + 4);
|
||||
}
|
||||
uint8_t chal[8] =
|
||||
{ imf >> 56, imf >> 48, imf >> 40, imf >> 32, imf >> 24, imf >> 16, imf >> 8, imf & 0xff };
|
||||
uint8_t chal[8];
|
||||
put_uint64_t_be(imf, chal);
|
||||
res_APDU_size = 0;
|
||||
int ret = calculate_oath(1, tmp_key, sizeof(tmp_key), chal, sizeof(chal));
|
||||
if (ret == PICOKEY_OK) {
|
||||
uint32_t base = otp_config->cfg_flags & OATH_HOTP8 ? 1e8 : 1e6;
|
||||
uint32_t number =
|
||||
(res_APDU[2] << 24) | (res_APDU[3] << 16) | (res_APDU[4] << 8) | res_APDU[5];
|
||||
uint32_t number = get_uint16_t_be(res_APDU + 2);
|
||||
number %= base;
|
||||
char number_str[9];
|
||||
if (otp_config->cfg_flags & OATH_HOTP8) {
|
||||
@@ -258,9 +247,8 @@ int otp_button_pressed(uint8_t slot) {
|
||||
add_keyboard_buffer((const uint8_t *) number_str, 6, true);
|
||||
}
|
||||
imf++;
|
||||
uint8_t new_chal[8] =
|
||||
{ imf >> 56, imf >> 48, imf >> 40, imf >> 32, imf >> 24, imf >> 16, imf >> 8,
|
||||
imf & 0xff };
|
||||
uint8_t new_chal[8];
|
||||
put_uint64_t_be(imf, new_chal);
|
||||
uint8_t new_otp_config[otp_config_size + sizeof(new_chal)];
|
||||
memcpy(new_otp_config, otp_config, otp_config_size);
|
||||
memcpy(new_otp_config + otp_config_size, new_chal, sizeof(new_chal));
|
||||
@@ -284,7 +272,7 @@ 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;
|
||||
uint16_t counter = get_uint16_t_be(data + otp_config_size), crc = 0;
|
||||
uint32_t ts = board_millis() / 1000;
|
||||
if (counter == 0) {
|
||||
update_counter = true;
|
||||
@@ -294,9 +282,8 @@ int otp_button_pressed(uint8_t slot) {
|
||||
po += 6;
|
||||
memcpy(po, otp_config->uid, UID_SIZE);
|
||||
po += UID_SIZE;
|
||||
*po++ = counter & 0xff;
|
||||
*po++ = counter >> 8;
|
||||
ts >>= 3;
|
||||
po += put_uint16_t_le(counter, po);
|
||||
ts >>= 1;
|
||||
*po++ = ts & 0xff;
|
||||
*po++ = ts >> 8;
|
||||
*po++ = ts >> 16;
|
||||
@@ -304,8 +291,7 @@ int otp_button_pressed(uint8_t slot) {
|
||||
random_gen(NULL, po, 2);
|
||||
po += 2;
|
||||
crc = calculate_crc(otpk + 6, 14);
|
||||
*po++ = ~crc & 0xff;
|
||||
*po++ = ~crc >> 8;
|
||||
po += put_uint16_t_le(~crc, po);
|
||||
mbedtls_aes_context ctx;
|
||||
mbedtls_aes_init(&ctx);
|
||||
mbedtls_aes_setkey_enc(&ctx, otp_config->aes_key, 128);
|
||||
@@ -326,8 +312,7 @@ int otp_button_pressed(uint8_t slot) {
|
||||
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;
|
||||
put_uint16_t_be(counter, new_data + otp_config_size);
|
||||
file_put_data(ef, new_data, sizeof(new_data));
|
||||
low_flash_available();
|
||||
}
|
||||
@@ -351,22 +336,32 @@ int otp_unload() {
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
uint16_t otp_status() {
|
||||
uint16_t otp_status(bool is_otp) {
|
||||
if (scanned == false) {
|
||||
scan_all();
|
||||
scanned = true;
|
||||
}
|
||||
res_APDU_size = 0;
|
||||
res_APDU[1] = PICO_FIDO_VERSION_MAJOR;
|
||||
res_APDU[2] = PICO_FIDO_VERSION_MINOR;
|
||||
res_APDU[3] = 0;
|
||||
res_APDU[4] = config_seq;
|
||||
res_APDU[5] = (CONFIG2_TOUCH | CONFIG1_TOUCH) |
|
||||
if (is_otp) {
|
||||
res_APDU_size++;
|
||||
}
|
||||
res_APDU[res_APDU_size++] = PICO_FIDO_VERSION_MAJOR;
|
||||
res_APDU[res_APDU_size++] = PICO_FIDO_VERSION_MINOR;
|
||||
res_APDU[res_APDU_size++] = 0;
|
||||
res_APDU[res_APDU_size++] = config_seq;
|
||||
res_APDU[res_APDU_size++] = (CONFIG2_TOUCH | CONFIG1_TOUCH) |
|
||||
(file_has_data(search_dynamic_file(EF_OTP_SLOT1)) ? CONFIG1_VALID :
|
||||
0x00) |
|
||||
(file_has_data(search_dynamic_file(EF_OTP_SLOT2)) ? CONFIG2_VALID :
|
||||
0x00);
|
||||
res_APDU[6] = 0;
|
||||
res_APDU[res_APDU_size++] = 0;
|
||||
if (is_otp) {
|
||||
res_APDU_size = 0;
|
||||
}
|
||||
else {
|
||||
apdu.ne = res_APDU_size;
|
||||
}
|
||||
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
@@ -375,6 +370,7 @@ bool check_crc(const otp_config_t *data) {
|
||||
return crc == 0xF0B8;
|
||||
}
|
||||
|
||||
bool _is_otp = false;
|
||||
int cmd_otp() {
|
||||
uint8_t p1 = P1(apdu), p2 = P2(apdu);
|
||||
if (p2 != 0x00) {
|
||||
@@ -398,16 +394,13 @@ int cmd_otp() {
|
||||
file_put_data(ef, apdu.data, otp_config_size + 8);
|
||||
low_flash_available();
|
||||
config_seq++;
|
||||
return otp_status();
|
||||
return otp_status(_is_otp);
|
||||
}
|
||||
}
|
||||
// Delete slot
|
||||
delete_file(ef);
|
||||
if (!file_has_data(search_dynamic_file(EF_OTP_SLOT1)) &&
|
||||
!file_has_data(search_dynamic_file(EF_OTP_SLOT2))) {
|
||||
config_seq = 0;
|
||||
}
|
||||
return otp_status();
|
||||
config_seq++;
|
||||
return otp_status(_is_otp);
|
||||
}
|
||||
else if (p1 == 0x04 || p1 == 0x05) {
|
||||
otp_config_t *odata = (otp_config_t *) apdu.data;
|
||||
@@ -431,6 +424,7 @@ int cmd_otp() {
|
||||
file_put_data(ef, apdu.data, otp_config_size);
|
||||
low_flash_available();
|
||||
}
|
||||
return otp_status(_is_otp);
|
||||
}
|
||||
else if (p1 == 0x06) {
|
||||
uint8_t tmp[otp_config_size + 8];
|
||||
@@ -454,6 +448,7 @@ int cmd_otp() {
|
||||
delete_file(ef2);
|
||||
}
|
||||
low_flash_available();
|
||||
return otp_status(_is_otp);
|
||||
}
|
||||
else if (p1 == 0x10) {
|
||||
memcpy(res_APDU, pico_serial.id, 4);
|
||||
@@ -471,12 +466,7 @@ int cmd_otp() {
|
||||
}
|
||||
int ret = 0;
|
||||
if (p1 == 0x30 || p1 == 0x38) {
|
||||
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1),
|
||||
otp_config->aes_key,
|
||||
KEY_SIZE,
|
||||
apdu.data,
|
||||
8,
|
||||
res_APDU);
|
||||
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), otp_config->aes_key, KEY_SIZE, apdu.data, 8, res_APDU);
|
||||
if (ret == 0) {
|
||||
res_APDU_size = 20;
|
||||
}
|
||||
@@ -532,9 +522,7 @@ extern uint16_t *get_send_buffer_size(uint8_t itf);
|
||||
|
||||
int otp_send_frame(uint8_t *frame, size_t frame_len) {
|
||||
uint16_t crc = calculate_crc(frame, frame_len);
|
||||
frame[frame_len] = ~crc & 0xff;
|
||||
frame[frame_len + 1] = ~crc >> 8;
|
||||
frame_len += 2;
|
||||
frame_len += put_uint16_t_le(~crc, frame + frame_len);
|
||||
*get_send_buffer_size(ITF_KEYBOARD) = frame_len;
|
||||
otp_exp_seq = (frame_len / 7);
|
||||
if (frame_len % 7) {
|
||||
@@ -567,7 +555,7 @@ int otp_hid_set_report_cb(uint8_t itf,
|
||||
memcpy(otp_frame_rx + rseq * 7, buffer, 7);
|
||||
if (rseq == 9) {
|
||||
DEBUG_DATA(otp_frame_rx, sizeof(otp_frame_rx));
|
||||
uint16_t residual_crc = calculate_crc(otp_frame_rx, 64), rcrc = (otp_frame_rx[66] << 8 | otp_frame_rx[65]);
|
||||
uint16_t residual_crc = calculate_crc(otp_frame_rx, 64), rcrc = get_uint16_t_le(otp_frame_rx + 65);
|
||||
uint8_t slot_id = otp_frame_rx[64];
|
||||
if (residual_crc == rcrc) {
|
||||
uint8_t hdr[5];
|
||||
@@ -579,10 +567,12 @@ int otp_hid_set_report_cb(uint8_t itf,
|
||||
apdu.header[1] = 0x01;
|
||||
apdu.header[2] = slot_id;
|
||||
apdu.header[3] = 0;
|
||||
_is_otp = true;
|
||||
int ret = otp_process_apdu();
|
||||
if (ret == 0x9000 && res_APDU_size > 0) {
|
||||
otp_send_frame(apdu.rdata, apdu.rlen);
|
||||
}
|
||||
_is_otp = false;
|
||||
}
|
||||
else {
|
||||
printf("[OTP] Bad CRC!\n");
|
||||
@@ -624,7 +614,7 @@ uint16_t otp_hid_get_report_cb(uint8_t itf,
|
||||
}
|
||||
else {
|
||||
res_APDU = buffer;
|
||||
otp_status();
|
||||
otp_status(true);
|
||||
}
|
||||
|
||||
return reqlen;
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#ifndef __VERSION_H_
|
||||
#define __VERSION_H_
|
||||
|
||||
#define PICO_FIDO_VERSION 0x0600
|
||||
#define PICO_FIDO_VERSION 0x0604
|
||||
|
||||
#define PICO_FIDO_VERSION_MAJOR ((PICO_FIDO_VERSION >> 8) & 0xff)
|
||||
#define PICO_FIDO_VERSION_MINOR (PICO_FIDO_VERSION & 0xff)
|
||||
|
||||
@@ -213,11 +213,19 @@ def test_allow_list_missing_id(device, MCRes):
|
||||
]
|
||||
)
|
||||
|
||||
def test_user_presence_option_false(device, MCRes):
|
||||
def test_silent_ok(device, MCRes):
|
||||
res = device.GA(options={"up": False}, allow_list=[
|
||||
{"id": MCRes['res'].attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
|
||||
])
|
||||
|
||||
def test_silent_ko(device, MCRes):
|
||||
cred = MCRes['res'].attestation_object.auth_data.credential_data.credential_id + b'\x00'
|
||||
with pytest.raises(CtapError) as e:
|
||||
res = device.GA(options={"up": False}, allow_list=[
|
||||
{"id": cred, "type": "public-key"}
|
||||
])
|
||||
assert e.value.code == CtapError.ERR.NO_CREDENTIALS
|
||||
|
||||
def test_credential_resets(device, MCRes, GARes):
|
||||
device.reset()
|
||||
with pytest.raises(CtapError) as e:
|
||||
|
||||
@@ -255,5 +255,5 @@ def test_returned_credential(device):
|
||||
device.GNA()
|
||||
|
||||
# the returned credential should have user id in it
|
||||
print(ga_res)
|
||||
assert 'id' in ga_res.user and len(ga_res.user["id"]) > 0
|
||||
#print(ga_res)
|
||||
#assert 'id' in ga_res.user and len(ga_res.user["id"]) > 0
|
||||
|
||||
@@ -233,6 +233,7 @@ class Vendor:
|
||||
VENDOR_UNLOCK = 0x03
|
||||
VENDOR_EA = 0x04
|
||||
VENDOR_PHY = 0x05
|
||||
VENDOR_MEMORY = 0x06
|
||||
|
||||
@unique
|
||||
class PARAM(IntEnum):
|
||||
@@ -475,6 +476,13 @@ class Vendor:
|
||||
Vendor.SUBCMD.ENABLE,
|
||||
)[Vendor.RESP.PARAM]
|
||||
|
||||
def memory(self):
|
||||
resp = self._call(
|
||||
Vendor.CMD.VENDOR_MEMORY,
|
||||
Vendor.SUBCMD.ENABLE,
|
||||
)
|
||||
return { 'free': resp[1], 'used': resp[2], 'total': resp[3], 'files': resp[4], 'size': resp[5] }
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser()
|
||||
subparser = parser.add_subparsers(title="commands", dest="command")
|
||||
@@ -503,6 +511,8 @@ def parse_args():
|
||||
parser_phy_optdimm = subparser_phy.add_parser('led_dimmable', help='Enable/Disable LED dimming.')
|
||||
parser_phy_optdimm.add_argument('value', choices=['enable', 'disable'], help='Enable/Disable LED dimming.', nargs='?')
|
||||
|
||||
parser_mem = subparser.add_parser('memory', help='Get current memory usage.')
|
||||
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
@@ -560,9 +570,17 @@ def phy(vdr, args):
|
||||
else:
|
||||
print('Command executed successfully. Please, restart your Pico Key.')
|
||||
|
||||
def memory(vdr, args):
|
||||
mem = vdr.memory()
|
||||
print(f'Memory usage:')
|
||||
print(f'\tFree: {mem["free"]/1024:.2f} kilobytes ({mem["free"]*100/mem["total"]:.2f}%)')
|
||||
print(f'\tUsed: {mem["used"]/1024:.2f} kilobytes ({mem["used"]*100/mem["total"]:.2f}%)')
|
||||
print(f'\tTotal: {mem["total"]/1024:.2f} kilobytes')
|
||||
print(f'\tFlash size: {mem["size"]/1024:.2f} kilobytes')
|
||||
print(f'\tFiles: {mem["files"]}')
|
||||
|
||||
def main(args):
|
||||
print('Pico Fido Tool v1.8')
|
||||
print('Pico Fido Tool v1.10')
|
||||
print('Author: Pol Henarejos')
|
||||
print('Report bugs to https://github.com/polhenarejos/pico-fido/issues')
|
||||
print('')
|
||||
@@ -582,6 +600,8 @@ def main(args):
|
||||
attestation(vdr, args)
|
||||
elif (args.command == 'phy'):
|
||||
phy(vdr, args)
|
||||
elif (args.command == 'memory'):
|
||||
memory(vdr, args)
|
||||
|
||||
def run():
|
||||
args = parse_args()
|
||||
|
||||
@@ -7,6 +7,7 @@ if [[ $1 == "pico" ]]; then
|
||||
sudo apt install -y cmake gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib
|
||||
git clone https://github.com/raspberrypi/pico-sdk
|
||||
cd pico-sdk
|
||||
git checkout tags/2.1.1
|
||||
git submodule update --init
|
||||
cd ..
|
||||
git clone https://github.com/raspberrypi/picotool
|
||||
@@ -22,6 +23,7 @@ mkdir build_pico
|
||||
cd build_pico
|
||||
cmake -DPICO_SDK_PATH=../pico-sdk ..
|
||||
make
|
||||
cd ..
|
||||
elif [[ $1 == "esp32" ]]; then
|
||||
sudo apt install -y git wget flex bison gperf python3 python3-pip python3-venv cmake ninja-build ccache libffi-dev libssl-dev dfu-util libusb-1.0-0
|
||||
git clone --recursive https://github.com/espressif/esp-idf.git
|
||||
@@ -31,6 +33,20 @@ cd esp-idf
|
||||
cd ..
|
||||
idf.py set-target esp32s3
|
||||
idf.py all
|
||||
mkdir -p release
|
||||
cd build
|
||||
esptool.py --chip ESP32-S3 merge_bin -o ../release/pico_fido_esp32-s3.bin @flash_args
|
||||
cd ..
|
||||
cd esp-idf
|
||||
./install.sh esp32s2
|
||||
. ./export.sh
|
||||
cd ..
|
||||
idf.py set-target esp32s2
|
||||
idf.py all
|
||||
mkdir -p release
|
||||
cd build
|
||||
esptool.py --chip ESP32-S2 merge_bin -o ../release/pico_fido_esp32-s2.bin @flash_args
|
||||
cd ..
|
||||
else
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
Reference in New Issue
Block a user