Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d4ed55b5a5 | ||
|
|
cfb0b8f3f2 | ||
|
|
eca8656bd9 | ||
|
|
5b5a9fc0fe | ||
|
|
59ec9b75fc | ||
|
|
8b2be54ede | ||
|
|
483073ebb8 | ||
|
|
12250a1c31 | ||
|
|
3f8aed1ecf | ||
|
|
707af84292 | ||
|
|
717e7135d5 | ||
|
|
fc909fa93d | ||
|
|
03f29f5be6 | ||
|
|
267e66eaee | ||
|
|
22317d4322 | ||
|
|
376b49db95 | ||
|
|
9756b451bb | ||
|
|
dcdf605a5e | ||
|
|
6d9208f434 | ||
|
|
9a493b9972 | ||
|
|
ef993d0f7b | ||
|
|
5360d62062 | ||
|
|
6d123e2a0f | ||
|
|
379f136699 | ||
|
|
f3decd8649 | ||
|
|
4f33d999e3 | ||
|
|
46661ee808 | ||
|
|
b1fdb9b1d1 | ||
|
|
b3b2e98ec1 | ||
|
|
cdf96e3564 | ||
|
|
4fe29750f2 | ||
|
|
87bdea7e28 | ||
|
|
cecb5da3a0 | ||
|
|
49cf031037 | ||
|
|
3a2ab27567 | ||
|
|
601e1bcda7 | ||
|
|
dc6f25caf3 | ||
|
|
20345ebd10 | ||
|
|
6674a0bb1e | ||
|
|
c45c70d95d | ||
|
|
061b5e919e | ||
|
|
0dc547dbe5 | ||
|
|
c383f6c446 | ||
|
|
c3d6d4c657 | ||
|
|
e387033266 | ||
|
|
97336bf8d4 | ||
|
|
6ebaa05523 | ||
|
|
00f30ba00c | ||
|
|
363ad1c9e2 | ||
|
|
94806f9bf0 | ||
|
|
03b35cfe88 | ||
|
|
bc9bbaf292 | ||
|
|
e5ca759dea | ||
|
|
f780b2a0b4 | ||
|
|
b9f1adf211 | ||
|
|
880a1b003d | ||
|
|
545860ccbc |
10
.github/workflows/codeql.yml
vendored
10
.github/workflows/codeql.yml
vendored
@@ -13,10 +13,10 @@ name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
branches: [ "main", "development" ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ "main" ]
|
||||
branches: [ "main", "development" ]
|
||||
schedule:
|
||||
- cron: '23 5 * * 4'
|
||||
|
||||
@@ -48,11 +48,11 @@ jobs:
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
|
||||
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
# - name: Autobuild
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
|
||||
- run: |
|
||||
|
||||
36
.github/workflows/test.yml
vendored
Normal file
36
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "Emulation and test"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main", "development" ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ "main", "development" ]
|
||||
schedule:
|
||||
- cron: '23 5 * * 4'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository and submodules
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build in container
|
||||
run: ./tests/build-in-docker.sh
|
||||
- name: Start emulation and test
|
||||
run: ./tests/run-test-in-docker.sh
|
||||
@@ -17,36 +17,66 @@
|
||||
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
if(ENABLE_EMULATION)
|
||||
else()
|
||||
include(pico_sdk_import.cmake)
|
||||
endif()
|
||||
|
||||
project(pico_fido C CXX ASM)
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
if(ENABLE_EMULATION)
|
||||
else()
|
||||
pico_sdk_init()
|
||||
endif()
|
||||
|
||||
add_executable(pico_fido)
|
||||
|
||||
option(ENABLE_UP_BUTTON "Enable/disable user presence button" ON)
|
||||
if(ENABLE_UP_BUTTON)
|
||||
add_definitions(-DENABLE_UP_BUTTON=1)
|
||||
message("Enabling user presence with button")
|
||||
message(STATUS "User presence with button: \t enabled")
|
||||
else()
|
||||
add_definitions(-DENABLE_UP_BUTTON=0)
|
||||
message("Disabling user presence with button")
|
||||
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)
|
||||
add_definitions(-DENABLE_POWER_ON_RESET=1)
|
||||
message("Enabling power cycle on reset")
|
||||
message(STATUS "Power cycle on reset: \t enabled")
|
||||
else()
|
||||
add_definitions(-DENABLE_POWER_ON_RESET=0)
|
||||
message("Disabling power cycle on reset")
|
||||
message(STATUS "Power cycle on reset: \t disabled")
|
||||
endif(ENABLE_POWER_ON_RESET)
|
||||
|
||||
target_sources(pico_fido PUBLIC
|
||||
option(ENABLE_OATH_APP "Enable/disable OATH application" ON)
|
||||
if(ENABLE_OATH_APP)
|
||||
add_definitions(-DENABLE_OATH_APP=1)
|
||||
message(STATUS "OATH Application: \t\t enabled")
|
||||
else()
|
||||
add_definitions(-DENABLE_OATH_APP=0)
|
||||
message(STATUS "OATH Application: \t\t disabled")
|
||||
endif(ENABLE_OATH_APP)
|
||||
|
||||
option(ENABLE_OTP_APP "Enable/disable OTP application" ON)
|
||||
if(ENABLE_OTP_APP)
|
||||
add_definitions(-DENABLE_OTP_APP=1)
|
||||
message(STATUS "OTP Application: \t\t enabled")
|
||||
else()
|
||||
add_definitions(-DENABLE_OTP_APP=0)
|
||||
message(STATUS "OTP Application: \t\t disabled")
|
||||
endif(ENABLE_OTP_APP)
|
||||
|
||||
if(ENABLE_OTP_APP OR ENABLE_OATH_APP)
|
||||
set(USB_ITF_CCID 1)
|
||||
else()
|
||||
set(USB_ITF_CCID 0)
|
||||
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/cmd_register.c
|
||||
@@ -66,13 +96,27 @@ target_sources(pico_fido PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_vendor.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_large_blobs.c
|
||||
)
|
||||
set(HSM_DRIVER "hid")
|
||||
if (${ENABLE_OATH_APP})
|
||||
set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/oath.c
|
||||
)
|
||||
endif()
|
||||
if (${ENABLE_OTP_APP})
|
||||
set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/otp.c
|
||||
)
|
||||
endif()
|
||||
|
||||
set(USB_ITF_HID 1)
|
||||
include(pico-hsm-sdk/pico_hsm_sdk_import.cmake)
|
||||
|
||||
target_include_directories(pico_fido PUBLIC
|
||||
set(INCLUDES ${INCLUDES}
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido
|
||||
)
|
||||
|
||||
target_sources(pico_fido PUBLIC ${SOURCES})
|
||||
target_include_directories(pico_fido PUBLIC ${INCLUDES})
|
||||
|
||||
target_compile_options(pico_fido PUBLIC
|
||||
-Wall
|
||||
-Werror
|
||||
@@ -84,6 +128,23 @@ if (${COMPILER_COLON} GREATER_EQUAL 0)
|
||||
)
|
||||
endif()
|
||||
|
||||
pico_add_extra_outputs(pico_fido)
|
||||
if(ENABLE_EMULATION)
|
||||
|
||||
target_compile_options(pico_fido PUBLIC
|
||||
-fdata-sections
|
||||
-ffunction-sections
|
||||
)
|
||||
if(APPLE)
|
||||
target_link_options(pico_fido PUBLIC
|
||||
-Wl,-dead_strip
|
||||
)
|
||||
else()
|
||||
target_link_options(pico_fido PUBLIC
|
||||
-Wl,--gc-sections
|
||||
)
|
||||
target_link_libraries(pico_fido PRIVATE m)
|
||||
endif (APPLE)
|
||||
else()
|
||||
pico_add_extra_outputs(pico_fido)
|
||||
target_link_libraries(pico_fido PRIVATE pico_hsm_sdk pico_stdlib pico_multicore hardware_flash hardware_sync hardware_adc pico_unique_id hardware_rtc tinyusb_device tinyusb_board)
|
||||
endif()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
VERSION_MAJOR="2"
|
||||
VERSION_MINOR="10"
|
||||
VERSION_MAJOR="3"
|
||||
VERSION_MINOR="0"
|
||||
|
||||
rm -rf release/*
|
||||
cd build_release
|
||||
@@ -40,6 +40,8 @@ for board in adafruit_feather_rp2040 \
|
||||
sparkfun_thingplus \
|
||||
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 \
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
VERSION_MAJOR="3" #Version of Pico CCID Core
|
||||
VERSION_MINOR="4"
|
||||
VERSION_MAJOR="4" #Version of Pico CCID Core
|
||||
VERSION_MINOR="0"
|
||||
|
||||
echo "----------------------------"
|
||||
echo "VID/PID patcher for Pico FIDO"
|
||||
|
||||
Submodule pico-hsm-sdk updated: fa54da973c...b12e66a057
@@ -15,13 +15,12 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#ifndef ENABLE_EMULATION
|
||||
#include "pico/stdlib.h"
|
||||
#include "ctap2_cbor.h"
|
||||
#endif
|
||||
#include "hid/ctap_hid.h"
|
||||
#include "ctap.h"
|
||||
#include "ctap_hid.h"
|
||||
#include "fido.h"
|
||||
#include "hsm.h"
|
||||
#include "usb.h"
|
||||
#include "apdu.h"
|
||||
|
||||
@@ -39,45 +38,60 @@ int cbor_config(const uint8_t *data, size_t len);
|
||||
int cbor_vendor(const uint8_t *data, size_t len);
|
||||
int cbor_large_blobs(const uint8_t *data, size_t len);
|
||||
|
||||
const uint8_t aaguid[16] = {0x89, 0xFB, 0x94, 0xB7, 0x06, 0xC9, 0x36, 0x73, 0x9B, 0x7E, 0x30, 0x52, 0x6D, 0x96, 0x81, 0x45}; // First 16 bytes of SHA256("Pico FIDO2")
|
||||
const uint8_t aaguid[16] =
|
||||
{ 0x89, 0xFB, 0x94, 0xB7, 0x06, 0xC9, 0x36, 0x73, 0x9B, 0x7E, 0x30, 0x52, 0x6D, 0x96, 0x81, 0x45 }; // First 16 bytes of SHA256("Pico FIDO2")
|
||||
|
||||
static const uint8_t *cbor_data = NULL;
|
||||
static size_t cbor_len = 0;
|
||||
static uint8_t cmd = 0;
|
||||
const uint8_t *cbor_data = NULL;
|
||||
size_t cbor_len = 0;
|
||||
uint8_t cmd = 0;
|
||||
|
||||
int cbor_parse(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
if (len == 0)
|
||||
if (len == 0 && cmd == CTAPHID_CBOR) {
|
||||
return CTAP1_ERR_INVALID_LEN;
|
||||
DEBUG_DATA(data+1,len-1);
|
||||
driver_prepare_response();
|
||||
}
|
||||
if (len > 0) {
|
||||
DEBUG_DATA(data + 1, len - 1);
|
||||
}
|
||||
driver_prepare_response_hid();
|
||||
if (cmd == CTAPHID_CBOR) {
|
||||
if (data[0] == CTAP_MAKE_CREDENTIAL)
|
||||
if (data[0] == CTAP_MAKE_CREDENTIAL) {
|
||||
return cbor_make_credential(data + 1, len - 1);
|
||||
if (data[0] == CTAP_GET_INFO)
|
||||
}
|
||||
if (data[0] == CTAP_GET_INFO) {
|
||||
return cbor_get_info();
|
||||
else if (data[0] == CTAP_RESET)
|
||||
}
|
||||
else if (data[0] == CTAP_RESET) {
|
||||
return cbor_reset();
|
||||
else if (data[0] == CTAP_CLIENT_PIN)
|
||||
}
|
||||
else if (data[0] == CTAP_CLIENT_PIN) {
|
||||
return cbor_client_pin(data + 1, len - 1);
|
||||
else if (data[0] == CTAP_GET_ASSERTION)
|
||||
}
|
||||
else if (data[0] == CTAP_GET_ASSERTION) {
|
||||
return cbor_get_assertion(data + 1, len - 1, false);
|
||||
else if (data[0] == CTAP_GET_NEXT_ASSERTION)
|
||||
}
|
||||
else if (data[0] == CTAP_GET_NEXT_ASSERTION) {
|
||||
return cbor_get_next_assertion(data + 1, len - 1);
|
||||
else if (data[0] == CTAP_SELECTION)
|
||||
}
|
||||
else if (data[0] == CTAP_SELECTION) {
|
||||
return cbor_selection();
|
||||
else if (data[0] == CTAP_CREDENTIAL_MGMT || data[0] == 0x41)
|
||||
}
|
||||
else if (data[0] == CTAP_CREDENTIAL_MGMT || data[0] == 0x41) {
|
||||
return cbor_cred_mgmt(data + 1, len - 1);
|
||||
else if (data[0] == CTAP_CONFIG)
|
||||
}
|
||||
else if (data[0] == CTAP_CONFIG) {
|
||||
return cbor_config(data + 1, len - 1);
|
||||
else if (data[0] == CTAP_LARGE_BLOBS)
|
||||
}
|
||||
else if (data[0] == CTAP_LARGE_BLOBS) {
|
||||
return cbor_large_blobs(data + 1, len - 1);
|
||||
}
|
||||
}
|
||||
else if (cmd == CTAP_VENDOR_CBOR) {
|
||||
return cbor_vendor(data, len);
|
||||
}
|
||||
return CTAP2_ERR_INVALID_CBOR;
|
||||
return CTAP1_ERR_INVALID_CMD;
|
||||
}
|
||||
|
||||
#ifndef ENABLE_EMULATION
|
||||
void cbor_thread() {
|
||||
|
||||
card_init_core1();
|
||||
@@ -87,18 +101,20 @@ void cbor_thread() {
|
||||
|
||||
if (m == EV_EXIT) {
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
apdu.sw = cbor_parse(cmd, cbor_data, cbor_len);
|
||||
if (apdu.sw == 0)
|
||||
if (apdu.sw == 0) {
|
||||
DEBUG_DATA(res_APDU + 1, res_APDU_size);
|
||||
}
|
||||
|
||||
finished_data_size = res_APDU_size + 1;
|
||||
|
||||
finished_data_size = res_APDU_size+1;
|
||||
uint32_t flag = EV_EXEC_FINISHED;
|
||||
queue_add_blocking(&card_to_usb_q, &flag);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int cbor_process(uint8_t last_cmd, const uint8_t *data, size_t len) {
|
||||
cbor_data = data;
|
||||
|
||||
@@ -23,7 +23,10 @@
|
||||
#include "cbor.h"
|
||||
#include "ctap.h"
|
||||
#include "ctap2_cbor.h"
|
||||
#ifndef ENABLE_EMULATION
|
||||
#include "bsp/board.h"
|
||||
#endif
|
||||
#include "hid/ctap_hid.h"
|
||||
#include "fido.h"
|
||||
#include "files.h"
|
||||
#include "random.h"
|
||||
@@ -32,7 +35,7 @@
|
||||
#include "apdu.h"
|
||||
|
||||
uint32_t usage_timer = 0, initial_usage_time_limit = 0;
|
||||
uint32_t max_usage_time_period = 600*1000;
|
||||
uint32_t max_usage_time_period = 600 * 1000;
|
||||
bool needs_power_cycle = false;
|
||||
static mbedtls_ecdh_context hkey;
|
||||
static bool hkey_init = false;
|
||||
@@ -47,18 +50,21 @@ int beginUsingPinUvAuthToken(bool userIsPresent) {
|
||||
}
|
||||
|
||||
void clearUserPresentFlag() {
|
||||
if (paut.in_use == true)
|
||||
if (paut.in_use == true) {
|
||||
paut.user_present = false;
|
||||
}
|
||||
}
|
||||
|
||||
void clearUserVerifiedFlag() {
|
||||
if (paut.in_use == true)
|
||||
if (paut.in_use == true) {
|
||||
paut.user_verified = false;
|
||||
}
|
||||
}
|
||||
|
||||
void clearPinUvAuthTokenPermissionsExceptLbw() {
|
||||
if (paut.in_use == true)
|
||||
if (paut.in_use == true) {
|
||||
paut.permissions = CTAP_PERMISSION_LBW;
|
||||
}
|
||||
}
|
||||
|
||||
void stopUsingPinUvAuthToken() {
|
||||
@@ -73,28 +79,36 @@ void stopUsingPinUvAuthToken() {
|
||||
}
|
||||
|
||||
bool getUserPresentFlagValue() {
|
||||
if (paut.in_use != true)
|
||||
if (paut.in_use != true) {
|
||||
paut.user_present = false;
|
||||
}
|
||||
return paut.user_present;
|
||||
}
|
||||
|
||||
bool getUserVerifiedFlagValue() {
|
||||
if (paut.in_use != true)
|
||||
bool getUserVerifiedFlagValue() {
|
||||
if (paut.in_use != true) {
|
||||
paut.user_verified = false;
|
||||
}
|
||||
return paut.user_verified;
|
||||
}
|
||||
}
|
||||
|
||||
int regenerate() {
|
||||
if (hkey_init == true)
|
||||
if (hkey_init == true) {
|
||||
mbedtls_ecdh_free(&hkey);
|
||||
}
|
||||
|
||||
mbedtls_ecdh_init(&hkey);
|
||||
hkey_init = true;
|
||||
mbedtls_ecdh_setup(&hkey, MBEDTLS_ECP_DP_SECP256R1);
|
||||
int ret = mbedtls_ecdh_gen_public(&hkey.ctx.mbed_ecdh.grp, &hkey.ctx.mbed_ecdh.d, &hkey.ctx.mbed_ecdh.Q, random_gen, NULL);
|
||||
int ret = mbedtls_ecdh_gen_public(&hkey.ctx.mbed_ecdh.grp,
|
||||
&hkey.ctx.mbed_ecdh.d,
|
||||
&hkey.ctx.mbed_ecdh.Q,
|
||||
random_gen,
|
||||
NULL);
|
||||
mbedtls_mpi_lset(&hkey.ctx.mbed_ecdh.Qp.Z, 1);
|
||||
if (ret != 0)
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -102,17 +116,38 @@ int kdf(uint8_t protocol, const mbedtls_mpi *z, uint8_t *sharedSecret) {
|
||||
int ret = 0;
|
||||
uint8_t buf[32];
|
||||
ret = mbedtls_mpi_write_binary(z, buf, sizeof(buf));
|
||||
if (ret != 0)
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
if (protocol == 1) {
|
||||
return mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), buf, sizeof(buf), sharedSecret);
|
||||
return mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
|
||||
buf,
|
||||
sizeof(buf),
|
||||
sharedSecret);
|
||||
}
|
||||
else if (protocol == 2) {
|
||||
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
|
||||
ret = mbedtls_hkdf(md_info, NULL, 0, buf, sizeof(buf), (uint8_t *)"CTAP2 HMAC key", 14, sharedSecret, 32);
|
||||
if (ret != 0)
|
||||
ret = mbedtls_hkdf(md_info,
|
||||
NULL,
|
||||
0,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
(uint8_t *) "CTAP2 HMAC key",
|
||||
14,
|
||||
sharedSecret,
|
||||
32);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
return mbedtls_hkdf(md_info, NULL, 0, buf, sizeof(buf), (uint8_t *)"CTAP2 AES key", 13, sharedSecret+32, 32);
|
||||
}
|
||||
return mbedtls_hkdf(md_info,
|
||||
NULL,
|
||||
0,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
(uint8_t *) "CTAP2 AES key",
|
||||
13,
|
||||
sharedSecret + 32,
|
||||
32);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -120,7 +155,12 @@ int kdf(uint8_t protocol, const mbedtls_mpi *z, uint8_t *sharedSecret) {
|
||||
int ecdh(uint8_t protocol, const mbedtls_ecp_point *Q, uint8_t *sharedSecret) {
|
||||
mbedtls_mpi z;
|
||||
mbedtls_mpi_init(&z);
|
||||
int ret = mbedtls_ecdh_compute_shared(&hkey.ctx.mbed_ecdh.grp, &z, Q, &hkey.ctx.mbed_ecdh.d, random_gen, NULL);
|
||||
int ret = mbedtls_ecdh_compute_shared(&hkey.ctx.mbed_ecdh.grp,
|
||||
&z,
|
||||
Q,
|
||||
&hkey.ctx.mbed_ecdh.d,
|
||||
random_gen,
|
||||
NULL);
|
||||
ret = kdf(protocol, &z, sharedSecret);
|
||||
mbedtls_mpi_free(&z);
|
||||
return ret;
|
||||
@@ -141,12 +181,12 @@ int resetPinUvAuthToken() {
|
||||
int encrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, size_t in_len, uint8_t *out) {
|
||||
if (protocol == 1) {
|
||||
memcpy(out, in, in_len);
|
||||
return aes_encrypt(key, NULL, 32*8, HSM_AES_MODE_CBC, out, in_len);
|
||||
return aes_encrypt(key, NULL, 32 * 8, HSM_AES_MODE_CBC, out, in_len);
|
||||
}
|
||||
else if (protocol == 2) {
|
||||
random_gen(NULL, out, IV_SIZE);
|
||||
memcpy(out + IV_SIZE, in, in_len);
|
||||
return aes_encrypt(key+32, out, 32*8, HSM_AES_MODE_CBC, out+IV_SIZE, in_len);
|
||||
return aes_encrypt(key + 32, out, 32 * 8, HSM_AES_MODE_CBC, out + IV_SIZE, in_len);
|
||||
}
|
||||
|
||||
return -1;
|
||||
@@ -155,29 +195,36 @@ int encrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, size_t in_l
|
||||
int decrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, size_t in_len, uint8_t *out) {
|
||||
if (protocol == 1) {
|
||||
memcpy(out, in, in_len);
|
||||
return aes_decrypt(key, NULL, 32*8, HSM_AES_MODE_CBC, out, in_len);
|
||||
return aes_decrypt(key, NULL, 32 * 8, HSM_AES_MODE_CBC, out, in_len);
|
||||
}
|
||||
else if (protocol == 2) {
|
||||
memcpy(out, in+IV_SIZE, in_len);
|
||||
return aes_decrypt(key+32, in, 32*8, HSM_AES_MODE_CBC, out, in_len-IV_SIZE);
|
||||
memcpy(out, in + IV_SIZE, in_len);
|
||||
return aes_decrypt(key + 32, in, 32 * 8, HSM_AES_MODE_CBC, out, in_len - IV_SIZE);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int authenticate(uint8_t protocol, const uint8_t *key, const uint8_t *data, size_t len, uint8_t *sign) {
|
||||
int authenticate(uint8_t protocol,
|
||||
const uint8_t *key,
|
||||
const uint8_t *data,
|
||||
size_t len,
|
||||
uint8_t *sign) {
|
||||
uint8_t hmac[32];
|
||||
int ret = mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), key, 32, data, len, hmac);
|
||||
if (ret != 0)
|
||||
int ret =
|
||||
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), key, 32, data, len, hmac);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
if (protocol == 1) {
|
||||
memcpy(sign, hmac, 16);
|
||||
}
|
||||
else if (protocol == 2) {
|
||||
memcpy(sign, hmac, 32);
|
||||
}
|
||||
else
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -185,13 +232,17 @@ int verify(uint8_t protocol, const uint8_t *key, const uint8_t *data, size_t len
|
||||
uint8_t hmac[32];
|
||||
//if (paut.in_use == false)
|
||||
// return -2;
|
||||
int ret = mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), key, 32, data, len, hmac);
|
||||
if (ret != 0)
|
||||
int ret =
|
||||
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), key, 32, data, len, hmac);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
if (protocol == 1)
|
||||
}
|
||||
if (protocol == 1) {
|
||||
return memcmp(sign, hmac, 16);
|
||||
else if (protocol == 2)
|
||||
}
|
||||
else if (protocol == 2) {
|
||||
return memcmp(sign, hmac, 32);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -205,13 +256,17 @@ int getPublicKey() {
|
||||
}
|
||||
|
||||
int pinUvAuthTokenUsageTimerObserver() {
|
||||
if (usage_timer == 0)
|
||||
if (usage_timer == 0) {
|
||||
return -1;
|
||||
if (usage_timer+max_usage_time_period > board_millis()) {
|
||||
if (user_present_time_limit == 0 || user_present_time_limit+TRANSPORT_TIME_LIMIT < board_millis())
|
||||
}
|
||||
if (usage_timer + max_usage_time_period > board_millis()) {
|
||||
if (user_present_time_limit == 0 ||
|
||||
user_present_time_limit + TRANSPORT_TIME_LIMIT < board_millis()) {
|
||||
clearUserPresentFlag();
|
||||
}
|
||||
if (paut.in_use == true) {
|
||||
if (initial_usage_time_limit == 0 || initial_usage_time_limit+TRANSPORT_TIME_LIMIT < board_millis()) {
|
||||
if (initial_usage_time_limit == 0 ||
|
||||
initial_usage_time_limit + TRANSPORT_TIME_LIMIT < board_millis()) {
|
||||
stopUsingPinUvAuthToken();
|
||||
return 1;
|
||||
}
|
||||
@@ -231,20 +286,24 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
CborEncoder encoder, mapEncoder;
|
||||
CborValue map;
|
||||
CborError error = CborNoError;
|
||||
CborByteString pinUvAuthParam = {0}, newPinEnc = {0}, pinHashEnc = {0}, kax = {0}, kay = {0};
|
||||
CborCharString rpId = {0};
|
||||
CborByteString pinUvAuthParam = { 0 }, newPinEnc = { 0 }, pinHashEnc = { 0 }, kax = { 0 },
|
||||
kay = { 0 };
|
||||
CborCharString rpId = { 0 };
|
||||
CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map));
|
||||
uint64_t val_c = 1;
|
||||
if (hkey_init == false)
|
||||
if (hkey_init == false) {
|
||||
initialize();
|
||||
}
|
||||
CBOR_PARSE_MAP_START(map, 1)
|
||||
{
|
||||
uint64_t val_u = 0;
|
||||
CBOR_FIELD_GET_UINT(val_u, 1);
|
||||
if (val_c <= 2 && val_c != val_u)
|
||||
if (val_c <= 2 && val_c != val_u) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (val_u < val_c)
|
||||
}
|
||||
if (val_u < val_c) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
|
||||
}
|
||||
val_c = val_u + 1;
|
||||
if (val_u == 0x01) {
|
||||
CBOR_FIELD_GET_UINT(pinUvAuthProtocol, 1);
|
||||
@@ -254,7 +313,8 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
}
|
||||
else if (val_u == 0x03) {
|
||||
int64_t key = 0;
|
||||
CBOR_PARSE_MAP_START(_f1, 2) {
|
||||
CBOR_PARSE_MAP_START(_f1, 2)
|
||||
{
|
||||
CBOR_FIELD_GET_INT(key, 2);
|
||||
if (key == 1) {
|
||||
CBOR_FIELD_GET_INT(kty, 2);
|
||||
@@ -271,8 +331,9 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
else if (key == -3) {
|
||||
CBOR_FIELD_GET_BYTES(kay, 2);
|
||||
}
|
||||
else
|
||||
else {
|
||||
CBOR_ADVANCE(2);
|
||||
}
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f1, 2);
|
||||
}
|
||||
@@ -295,12 +356,13 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
CBOR_PARSE_MAP_END(map, 1);
|
||||
|
||||
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
|
||||
if (subcommand == 0x0)
|
||||
if (subcommand == 0x0) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
else if (subcommand == 0x1) { //getPINRetries
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, needs_power_cycle ? 2 : 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, (uint64_t)*file_get_data(ef_pin)));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, (uint64_t) *file_get_data(ef_pin)));
|
||||
if (needs_power_cycle) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04));
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, true));
|
||||
@@ -328,20 +390,28 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, pkey, 32));
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
|
||||
}
|
||||
else if (pinUvAuthProtocol == 0)
|
||||
else if (pinUvAuthProtocol == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
else
|
||||
}
|
||||
else {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
}
|
||||
else if (subcommand == 0x3) { //setPIN
|
||||
if (kax.present == false || kay.present == false || pinUvAuthProtocol == 0 || newPinEnc.present == false || pinUvAuthParam.present == false || alg == 0)
|
||||
if (kax.present == false || kay.present == false || pinUvAuthProtocol == 0 ||
|
||||
newPinEnc.present == false || pinUvAuthParam.present == false || alg == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2)
|
||||
}
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
if (file_has_data(ef_pin))
|
||||
}
|
||||
if (file_has_data(ef_pin)) {
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
if ((pinUvAuthProtocol == 1 && newPinEnc.len != 64) || (pinUvAuthProtocol == 2 && newPinEnc.len != 64+IV_SIZE))
|
||||
}
|
||||
if ((pinUvAuthProtocol == 1 && newPinEnc.len != 64) ||
|
||||
(pinUvAuthProtocol == 2 && newPinEnc.len != 64 + IV_SIZE)) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
if (mbedtls_mpi_read_binary(&hkey.ctx.mbed_ecdh.Qp.X, kax.data, kax.len) != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
@@ -354,45 +424,60 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
if (verify(pinUvAuthProtocol, sharedSecret, newPinEnc.data, newPinEnc.len, pinUvAuthParam.data) != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (verify(pinUvAuthProtocol, sharedSecret, newPinEnc.data, newPinEnc.len,
|
||||
pinUvAuthParam.data) != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
uint8_t paddedNewPin[64];
|
||||
ret = decrypt(pinUvAuthProtocol, sharedSecret, newPinEnc.data, newPinEnc.len, paddedNewPin);
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
if (ret != 0)
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (paddedNewPin[63] != 0)
|
||||
}
|
||||
if (paddedNewPin[63] != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION);
|
||||
}
|
||||
uint8_t pin_len = 0;
|
||||
while (paddedNewPin[pin_len] != 0 && pin_len < sizeof(paddedNewPin))
|
||||
while (paddedNewPin[pin_len] != 0 && pin_len < sizeof(paddedNewPin)) {
|
||||
pin_len++;
|
||||
}
|
||||
uint8_t minPin = 4;
|
||||
file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
|
||||
if (file_has_data(ef_minpin))
|
||||
if (file_has_data(ef_minpin)) {
|
||||
minPin = *file_get_data(ef_minpin);
|
||||
if (pin_len < minPin)
|
||||
}
|
||||
if (pin_len < minPin) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION);
|
||||
}
|
||||
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);
|
||||
flash_write_data_to_file(ef_pin, hsh, 2+16);
|
||||
flash_write_data_to_file(ef_pin, hsh, 2 + 16);
|
||||
low_flash_available();
|
||||
goto err; //No return
|
||||
}
|
||||
else if (subcommand == 0x4) { //changePIN
|
||||
if (kax.present == false || kay.present == false || pinUvAuthProtocol == 0 || newPinEnc.present == false || pinUvAuthParam.present == false || alg == 0 || pinHashEnc.present == false)
|
||||
if (kax.present == false || kay.present == false || pinUvAuthProtocol == 0 ||
|
||||
newPinEnc.present == false || pinUvAuthParam.present == false || alg == 0 ||
|
||||
pinHashEnc.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2)
|
||||
}
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
if (!file_has_data(ef_pin))
|
||||
}
|
||||
if (!file_has_data(ef_pin)) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_NOT_SET);
|
||||
if (*file_get_data(ef_pin) == 0)
|
||||
}
|
||||
if (*file_get_data(ef_pin) == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_BLOCKED);
|
||||
if ((pinUvAuthProtocol == 1 && (newPinEnc.len != 64 || pinHashEnc.len != 16)) || (pinUvAuthProtocol == 2 && (newPinEnc.len != 64+IV_SIZE || pinHashEnc.len != 16+IV_SIZE)))
|
||||
}
|
||||
if ((pinUvAuthProtocol == 1 && (newPinEnc.len != 64 || pinHashEnc.len != 16)) ||
|
||||
(pinUvAuthProtocol == 2 &&
|
||||
(newPinEnc.len != 64 + IV_SIZE || pinHashEnc.len != 16 + IV_SIZE))) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
if (mbedtls_mpi_read_binary(&hkey.ctx.mbed_ecdh.Qp.X, kax.data, kax.len) != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
@@ -408,7 +493,8 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
uint8_t tmp[80 + 32];
|
||||
memcpy(tmp, newPinEnc.data, newPinEnc.len);
|
||||
memcpy(tmp + newPinEnc.len, pinHashEnc.data, pinHashEnc.len);
|
||||
if (verify(pinUvAuthProtocol, sharedSecret, tmp, newPinEnc.len+pinHashEnc.len, pinUvAuthParam.data) != 0) {
|
||||
if (verify(pinUvAuthProtocol, sharedSecret, tmp, newPinEnc.len + pinHashEnc.len,
|
||||
pinUvAuthParam.data) != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
@@ -419,23 +505,25 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
low_flash_available();
|
||||
uint8_t retries = pin_data[0];
|
||||
uint8_t paddedNewPin[64];
|
||||
ret = decrypt(pinUvAuthProtocol, sharedSecret, pinHashEnc.data, pinHashEnc.len, paddedNewPin);
|
||||
ret =
|
||||
decrypt(pinUvAuthProtocol, sharedSecret, pinHashEnc.data, pinHashEnc.len, paddedNewPin);
|
||||
if (ret != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
if (memcmp(paddedNewPin, file_get_data(ef_pin)+2, 16) != 0) {
|
||||
if (memcmp(paddedNewPin, file_get_data(ef_pin) + 2, 16) != 0) {
|
||||
regenerate();
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
if (retries == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_BLOCKED);
|
||||
}
|
||||
if (++new_pin_mismatches >= 3) {
|
||||
if (++new_pin_mismatches >= 3) {
|
||||
needs_power_cycle = true;
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_BLOCKED);
|
||||
}
|
||||
else
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_INVALID);
|
||||
}
|
||||
}
|
||||
pin_data[0] = MAX_PIN_RETRIES;
|
||||
flash_write_data_to_file(ef_pin, pin_data, sizeof(pin_data));
|
||||
@@ -446,26 +534,32 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
if (paddedNewPin[63] != 0)
|
||||
if (paddedNewPin[63] != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
uint8_t pin_len = 0;
|
||||
while (paddedNewPin[pin_len] != 0 && pin_len < sizeof(paddedNewPin))
|
||||
while (paddedNewPin[pin_len] != 0 && pin_len < sizeof(paddedNewPin)) {
|
||||
pin_len++;
|
||||
}
|
||||
uint8_t minPin = 4;
|
||||
file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
|
||||
if (file_has_data(ef_minpin))
|
||||
if (file_has_data(ef_minpin)) {
|
||||
minPin = *file_get_data(ef_minpin);
|
||||
if (pin_len < minPin)
|
||||
}
|
||||
if (pin_len < minPin) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION);
|
||||
}
|
||||
uint8_t hsh[33];
|
||||
hsh[0] = MAX_PIN_RETRIES;
|
||||
hsh[1] = pin_len;
|
||||
mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, 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)
|
||||
if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1 &&
|
||||
memcmp(hsh + 2, file_get_data(ef_pin) + 2, 16) == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION);
|
||||
flash_write_data_to_file(ef_pin, hsh, 2+16);
|
||||
}
|
||||
flash_write_data_to_file(ef_pin, hsh, 2 + 16);
|
||||
if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1) {
|
||||
uint8_t *tmp = (uint8_t *)calloc(1, file_get_size(ef_minpin));
|
||||
uint8_t *tmp = (uint8_t *) calloc(1, file_get_size(ef_minpin));
|
||||
memcpy(tmp, file_get_data(ef_minpin), file_get_size(ef_minpin));
|
||||
tmp[1] = 0;
|
||||
flash_write_data_to_file(ef_minpin, tmp, file_get_size(ef_minpin));
|
||||
@@ -476,23 +570,31 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
goto err; // No return
|
||||
}
|
||||
else if (subcommand == 0x9 || subcommand == 0x5) { //getPinUvAuthTokenUsingPinWithPermissions
|
||||
if (kax.present == false || kay.present == false || pinUvAuthProtocol == 0 || alg == 0 || pinHashEnc.present == false)
|
||||
if (kax.present == false || kay.present == false || pinUvAuthProtocol == 0 || alg == 0 ||
|
||||
pinHashEnc.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2)
|
||||
}
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
if (subcommand == 0x5 && (permissions != 0 || rpId.present == true))
|
||||
}
|
||||
if (subcommand == 0x5 && (permissions != 0 || rpId.present == true)) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
if (subcommand == 0x9) {
|
||||
if (permissions == 0)
|
||||
if (permissions == 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
if ((permissions & CTAP_PERMISSION_BE)) // Not supported yet
|
||||
}
|
||||
if ((permissions & CTAP_PERMISSION_BE)) { // Not supported yet
|
||||
CBOR_ERROR(CTAP2_ERR_UNAUTHORIZED_PERMISSION);
|
||||
}
|
||||
|
||||
}
|
||||
if (!file_has_data(ef_pin))
|
||||
if (!file_has_data(ef_pin)) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_NOT_SET);
|
||||
if (*file_get_data(ef_pin) == 0)
|
||||
}
|
||||
if (*file_get_data(ef_pin) == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_BLOCKED);
|
||||
}
|
||||
if (mbedtls_mpi_read_binary(&hkey.ctx.mbed_ecdh.Qp.X, kax.data, kax.len) != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
@@ -511,51 +613,57 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
flash_write_data_to_file(ef_pin, pin_data, sizeof(pin_data));
|
||||
low_flash_available();
|
||||
uint8_t retries = pin_data[0];
|
||||
uint8_t paddedNewPin[64], poff = (pinUvAuthProtocol-1)*IV_SIZE;
|
||||
ret = decrypt(pinUvAuthProtocol, sharedSecret, pinHashEnc.data, pinHashEnc.len, paddedNewPin);
|
||||
uint8_t paddedNewPin[64], poff = (pinUvAuthProtocol - 1) * IV_SIZE;
|
||||
ret =
|
||||
decrypt(pinUvAuthProtocol, sharedSecret, pinHashEnc.data, pinHashEnc.len, paddedNewPin);
|
||||
if (ret != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
if (memcmp(paddedNewPin, file_get_data(ef_pin)+2, 16) != 0) {
|
||||
if (memcmp(paddedNewPin, file_get_data(ef_pin) + 2, 16) != 0) {
|
||||
regenerate();
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
if (retries == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_BLOCKED);
|
||||
}
|
||||
if (++new_pin_mismatches >= 3) {
|
||||
if (++new_pin_mismatches >= 3) {
|
||||
needs_power_cycle = true;
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_BLOCKED);
|
||||
}
|
||||
else
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_INVALID);
|
||||
}
|
||||
}
|
||||
pin_data[0] = MAX_PIN_RETRIES;
|
||||
new_pin_mismatches = 0;
|
||||
flash_write_data_to_file(ef_pin, pin_data, sizeof(pin_data));
|
||||
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)
|
||||
if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_INVALID);
|
||||
}
|
||||
resetPinUvAuthToken();
|
||||
beginUsingPinUvAuthToken(false);
|
||||
if (subcommand == 0x05)
|
||||
if (subcommand == 0x05) {
|
||||
permissions = CTAP_PERMISSION_MC | CTAP_PERMISSION_GA;
|
||||
}
|
||||
paut.permissions = permissions;
|
||||
if (rpId.present == true) {
|
||||
mbedtls_sha256((uint8_t *)rpId.data, rpId.len, paut.rp_id_hash, 0);
|
||||
mbedtls_sha256((uint8_t *) rpId.data, rpId.len, paut.rp_id_hash, 0);
|
||||
paut.has_rp_id = true;
|
||||
}
|
||||
else
|
||||
else {
|
||||
paut.has_rp_id = false;
|
||||
}
|
||||
uint8_t pinUvAuthToken_enc[32 + IV_SIZE];
|
||||
encrypt(pinUvAuthProtocol, sharedSecret, paut.data, 32, pinUvAuthToken_enc);
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x02));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, pinUvAuthToken_enc, 32+poff));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, pinUvAuthToken_enc, 32 + poff));
|
||||
}
|
||||
else
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION);
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
resp_size = cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1);
|
||||
err:
|
||||
@@ -566,8 +674,9 @@ err:
|
||||
CBOR_FREE_BYTE_STRING(kay);
|
||||
CBOR_FREE_BYTE_STRING(rpId);
|
||||
if (error != CborNoError) {
|
||||
if (error == CborErrorImproperValue)
|
||||
if (error == CborErrorImproperValue) {
|
||||
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
res_APDU_size = resp_size;
|
||||
|
||||
@@ -15,11 +15,10 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "ctap2_cbor.h"
|
||||
#include "fido.h"
|
||||
#include "ctap.h"
|
||||
#include "bsp/board.h"
|
||||
#include "hid/ctap_hid.h"
|
||||
#include "files.h"
|
||||
#include "apdu.h"
|
||||
#include "credential.h"
|
||||
@@ -37,8 +36,8 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
CborValue map;
|
||||
CborError error = CborNoError;
|
||||
uint64_t subcommand = 0, pinUvAuthProtocol = 0, vendorCommandId = 0, newMinPinLength = 0;
|
||||
CborByteString pinUvAuthParam = {0}, vendorAutCt = {0};
|
||||
CborCharString minPinLengthRPIDs[32] = {0};
|
||||
CborByteString pinUvAuthParam = { 0 }, vendorAutCt = { 0 };
|
||||
CborCharString minPinLengthRPIDs[32] = { 0 };
|
||||
size_t resp_size = 0, raw_subpara_len = 0, minPinLengthRPIDs_len = 0;
|
||||
CborEncoder encoder, mapEncoder;
|
||||
uint8_t *raw_subpara = NULL;
|
||||
@@ -46,21 +45,25 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
|
||||
CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map));
|
||||
uint64_t val_c = 1;
|
||||
CBOR_PARSE_MAP_START(map, 1) {
|
||||
CBOR_PARSE_MAP_START(map, 1)
|
||||
{
|
||||
uint64_t val_u = 0;
|
||||
CBOR_FIELD_GET_UINT(val_u, 1);
|
||||
if (val_c <= 1 && val_c != val_u)
|
||||
if (val_c <= 1 && val_c != val_u) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (val_u < val_c)
|
||||
}
|
||||
if (val_u < val_c) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
|
||||
}
|
||||
val_c = val_u + 1;
|
||||
if (val_u == 0x01) {
|
||||
CBOR_FIELD_GET_UINT(subcommand, 1);
|
||||
}
|
||||
else if (val_u == 0x02) {
|
||||
uint64_t subpara = 0;
|
||||
raw_subpara = (uint8_t *)cbor_value_get_next_byte(&_f1);
|
||||
CBOR_PARSE_MAP_START(_f1, 2) {
|
||||
raw_subpara = (uint8_t *) cbor_value_get_next_byte(&_f1);
|
||||
CBOR_PARSE_MAP_START(_f1, 2)
|
||||
{
|
||||
if (subcommand == 0xff) {
|
||||
CBOR_FIELD_GET_UINT(subpara, 2);
|
||||
if (subpara == 0x01) {
|
||||
@@ -76,11 +79,13 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
CBOR_FIELD_GET_UINT(newMinPinLength, 2);
|
||||
}
|
||||
else if (subpara == 0x02) {
|
||||
CBOR_PARSE_ARRAY_START(_f2, 3) {
|
||||
CBOR_PARSE_ARRAY_START(_f2, 3)
|
||||
{
|
||||
CBOR_FIELD_GET_TEXT(minPinLengthRPIDs[minPinLengthRPIDs_len], 3);
|
||||
minPinLengthRPIDs_len++;
|
||||
if (minPinLengthRPIDs_len >= 32)
|
||||
if (minPinLengthRPIDs_len >= 32) {
|
||||
CBOR_ERROR(CTAP2_ERR_KEY_STORE_FULL);
|
||||
}
|
||||
}
|
||||
CBOR_PARSE_ARRAY_END(_f2, 3);
|
||||
}
|
||||
@@ -103,40 +108,52 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
|
||||
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
|
||||
|
||||
if (pinUvAuthParam.present == false)
|
||||
if (pinUvAuthParam.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_PUAT_REQUIRED);
|
||||
if (pinUvAuthProtocol == 0)
|
||||
}
|
||||
if (pinUvAuthProtocol == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
|
||||
uint8_t *verify_payload = (uint8_t *)calloc(1, 32 + 1 + 1 + raw_subpara_len);
|
||||
uint8_t *verify_payload = (uint8_t *) calloc(1, 32 + 1 + 1 + raw_subpara_len);
|
||||
memset(verify_payload, 0xff, 32);
|
||||
verify_payload[32] = 0x0d;
|
||||
verify_payload[33] = subcommand;
|
||||
memcpy(verify_payload + 34, raw_subpara, raw_subpara_len);
|
||||
error = verify(pinUvAuthProtocol, paut.data, verify_payload, 32 + 1 + 1 + raw_subpara_len, pinUvAuthParam.data);
|
||||
error = verify(pinUvAuthProtocol,
|
||||
paut.data,
|
||||
verify_payload,
|
||||
32 + 1 + 1 + raw_subpara_len,
|
||||
pinUvAuthParam.data);
|
||||
free(verify_payload);
|
||||
if (error != CborNoError)
|
||||
if (error != CborNoError) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
|
||||
if (!(paut.permissions & CTAP_PERMISSION_ACFG))
|
||||
if (!(paut.permissions & CTAP_PERMISSION_ACFG)) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
|
||||
if (subcommand == 0xff) {
|
||||
if (vendorCommandId == CTAP_CONFIG_AUT_DISABLE) {
|
||||
if (!file_has_data(ef_keydev_enc))
|
||||
if (!file_has_data(ef_keydev_enc)) {
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
if (has_keydev_dec == false)
|
||||
}
|
||||
if (has_keydev_dec == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
flash_write_data_to_file(ef_keydev, keydev_dec, sizeof(keydev_dec));
|
||||
mbedtls_platform_zeroize(keydev_dec, sizeof(keydev_dec));
|
||||
flash_write_data_to_file(ef_keydev_enc, NULL, 0); // Set ef to 0 bytes
|
||||
low_flash_available();
|
||||
}
|
||||
else if (vendorCommandId == CTAP_CONFIG_AUT_ENABLE) {
|
||||
if (!file_has_data(ef_keydev))
|
||||
if (!file_has_data(ef_keydev)) {
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
if (mse.init == false)
|
||||
}
|
||||
if (mse.init == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
}
|
||||
|
||||
mbedtls_chachapoly_context chatx;
|
||||
int ret = mse_decrypt_ct(vendorAutCt.data, vendorAutCt.len);
|
||||
@@ -144,13 +161,20 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
uint8_t key_dev_enc[12+32+16];
|
||||
uint8_t key_dev_enc[12 + 32 + 16];
|
||||
random_gen(NULL, key_dev_enc, 12);
|
||||
mbedtls_chachapoly_init(&chatx);
|
||||
mbedtls_chachapoly_setkey(&chatx, vendorAutCt.data);
|
||||
ret = mbedtls_chachapoly_encrypt_and_tag(&chatx, file_get_size(ef_keydev), key_dev_enc, NULL, 0, file_get_data(ef_keydev), key_dev_enc + 12, key_dev_enc + 12 + file_get_size(ef_keydev));
|
||||
ret = mbedtls_chachapoly_encrypt_and_tag(&chatx,
|
||||
file_get_size(ef_keydev),
|
||||
key_dev_enc,
|
||||
NULL,
|
||||
0,
|
||||
file_get_data(ef_keydev),
|
||||
key_dev_enc + 12,
|
||||
key_dev_enc + 12 + file_get_size(ef_keydev));
|
||||
mbedtls_chachapoly_free(&chatx);
|
||||
if (ret != 0){
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
@@ -168,21 +192,29 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
else if (subcommand == 0x03) {
|
||||
uint8_t currentMinPinLen = 4;
|
||||
file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
|
||||
if (file_has_data(ef_minpin))
|
||||
if (file_has_data(ef_minpin)) {
|
||||
currentMinPinLen = *file_get_data(ef_minpin);
|
||||
if (newMinPinLength == 0)
|
||||
}
|
||||
if (newMinPinLength == 0) {
|
||||
newMinPinLength = currentMinPinLen;
|
||||
else if (newMinPinLength > 0 && newMinPinLength < currentMinPinLen)
|
||||
}
|
||||
else if (newMinPinLength > 0 && newMinPinLength < currentMinPinLen) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION);
|
||||
if (forceChangePin == ptrue && !file_has_data(ef_pin))
|
||||
}
|
||||
if (forceChangePin == ptrue && !file_has_data(ef_pin)) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_NOT_SET);
|
||||
if (file_has_data(ef_pin) && file_get_data(ef_pin)[1] < newMinPinLength)
|
||||
}
|
||||
if (file_has_data(ef_pin) && file_get_data(ef_pin)[1] < newMinPinLength) {
|
||||
forceChangePin = ptrue;
|
||||
uint8_t *data = (uint8_t *)calloc(1, 2 + minPinLengthRPIDs_len * 32);
|
||||
}
|
||||
uint8_t *data = (uint8_t *) calloc(1, 2 + minPinLengthRPIDs_len * 32);
|
||||
data[0] = newMinPinLength;
|
||||
data[1] = forceChangePin == ptrue ? 1 : 0;
|
||||
for (int m = 0; m < minPinLengthRPIDs_len; m++) {
|
||||
mbedtls_sha256((uint8_t *)minPinLengthRPIDs[m].data, minPinLengthRPIDs[m].len, data + 2 + m*32, 0);
|
||||
mbedtls_sha256((uint8_t *) minPinLengthRPIDs[m].data,
|
||||
minPinLengthRPIDs[m].len,
|
||||
data + 2 + m * 32,
|
||||
0);
|
||||
}
|
||||
flash_write_data_to_file(ef_minpin, data, 2 + minPinLengthRPIDs_len * 32);
|
||||
low_flash_available();
|
||||
@@ -192,12 +224,13 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
set_opts(get_opts() | FIDO2_OPT_EA);
|
||||
goto err;
|
||||
}
|
||||
else
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION);
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
resp_size = cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1);
|
||||
|
||||
err:
|
||||
err:
|
||||
CBOR_FREE_BYTE_STRING(pinUvAuthParam);
|
||||
CBOR_FREE_BYTE_STRING(vendorAutCt);
|
||||
for (int i = 0; i < minPinLengthRPIDs_len; i++) {
|
||||
@@ -205,8 +238,9 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
}
|
||||
|
||||
if (error != CborNoError) {
|
||||
if (error == CborErrorImproperValue)
|
||||
if (error == CborErrorImproperValue) {
|
||||
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
res_APDU_size = resp_size;
|
||||
|
||||
@@ -15,10 +15,9 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ctap2_cbor.h"
|
||||
#include "fido.h"
|
||||
#include "ctap.h"
|
||||
#include "bsp/board.h"
|
||||
#include "hid/ctap_hid.h"
|
||||
#include "cbor_make_credential.h"
|
||||
#include "files.h"
|
||||
#include "apdu.h"
|
||||
@@ -29,16 +28,16 @@ uint8_t rp_counter = 1;
|
||||
uint8_t rp_total = 0;
|
||||
uint8_t cred_counter = 1;
|
||||
uint8_t cred_total = 0;
|
||||
CborByteString rpIdHashx = {0};
|
||||
CborByteString rpIdHashx = { 0 };
|
||||
|
||||
int cbor_cred_mgmt(const uint8_t *data, size_t len) {
|
||||
CborParser parser;
|
||||
CborValue map;
|
||||
CborError error = CborNoError;
|
||||
uint64_t subcommand = 0, pinUvAuthProtocol = 0;
|
||||
CborByteString pinUvAuthParam = {0}, rpIdHash = {0};
|
||||
PublicKeyCredentialDescriptor credentialId = {0};
|
||||
PublicKeyCredentialUserEntity user = {0};
|
||||
CborByteString pinUvAuthParam = { 0 }, rpIdHash = { 0 };
|
||||
PublicKeyCredentialDescriptor credentialId = { 0 };
|
||||
PublicKeyCredentialUserEntity user = { 0 };
|
||||
size_t resp_size = 0;
|
||||
CborEncoder encoder, mapEncoder, mapEncoder2;
|
||||
uint8_t *raw_subpara = NULL;
|
||||
@@ -47,34 +46,41 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
|
||||
|
||||
CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map));
|
||||
uint64_t val_c = 1;
|
||||
CBOR_PARSE_MAP_START(map, 1) {
|
||||
CBOR_PARSE_MAP_START(map, 1)
|
||||
{
|
||||
uint64_t val_u = 0;
|
||||
CBOR_FIELD_GET_UINT(val_u, 1);
|
||||
if (val_c <= 1 && val_c != val_u)
|
||||
if (val_c <= 1 && val_c != val_u) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (val_u < val_c)
|
||||
}
|
||||
if (val_u < val_c) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
|
||||
}
|
||||
val_c = val_u + 1;
|
||||
if (val_u == 0x01) {
|
||||
CBOR_FIELD_GET_UINT(subcommand, 1);
|
||||
}
|
||||
else if (val_u == 0x02) {
|
||||
uint64_t subpara = 0;
|
||||
raw_subpara = (uint8_t *)cbor_value_get_next_byte(&_f1);
|
||||
CBOR_PARSE_MAP_START(_f1, 2) {
|
||||
raw_subpara = (uint8_t *) cbor_value_get_next_byte(&_f1);
|
||||
CBOR_PARSE_MAP_START(_f1, 2)
|
||||
{
|
||||
CBOR_FIELD_GET_UINT(subpara, 2);
|
||||
if (subpara == 0x01) {
|
||||
CBOR_FIELD_GET_BYTES(rpIdHash, 2);
|
||||
}
|
||||
else if (subpara == 0x02) {
|
||||
|
||||
CBOR_PARSE_MAP_START(_f2, 3) {
|
||||
CBOR_PARSE_MAP_START(_f2, 3)
|
||||
{
|
||||
CBOR_FIELD_GET_KEY_TEXT(3);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BYTES(3, "id", credentialId.id);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_TEXT(3, "type", credentialId.type);
|
||||
if (strcmp(_fd3, "transports") == 0) {
|
||||
CBOR_PARSE_ARRAY_START(_f3, 4) {
|
||||
CBOR_FIELD_GET_TEXT(credentialId.transports[credentialId.transports_len], 4);
|
||||
CBOR_PARSE_ARRAY_START(_f3, 4)
|
||||
{
|
||||
CBOR_FIELD_GET_TEXT(credentialId.transports[credentialId.
|
||||
transports_len], 4);
|
||||
credentialId.transports_len++;
|
||||
}
|
||||
CBOR_PARSE_ARRAY_END(_f3, 4);
|
||||
@@ -83,7 +89,8 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
|
||||
CBOR_PARSE_MAP_END(_f2, 3);
|
||||
}
|
||||
else if (subpara == 0x03) {
|
||||
CBOR_PARSE_MAP_START(_f1, 3) {
|
||||
CBOR_PARSE_MAP_START(_f1, 3)
|
||||
{
|
||||
CBOR_FIELD_GET_KEY_TEXT(3);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BYTES(3, "id", user.id);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_TEXT(3, "name", user.parent.name);
|
||||
@@ -105,82 +112,105 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
|
||||
CBOR_PARSE_MAP_END(map, 1);
|
||||
|
||||
if (subcommand != 0x03 && subcommand != 0x05) {
|
||||
if (pinUvAuthParam.present == false)
|
||||
if (pinUvAuthParam.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_PUAT_REQUIRED);
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2)
|
||||
}
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
}
|
||||
|
||||
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
|
||||
if(subcommand == 0x01) {
|
||||
if (verify(pinUvAuthProtocol, paut.data, (const uint8_t *)"\x01", 1, pinUvAuthParam.data) != CborNoError)
|
||||
if (subcommand == 0x01) {
|
||||
if (verify(pinUvAuthProtocol, paut.data, (const uint8_t *) "\x01", 1,
|
||||
pinUvAuthParam.data) != CborNoError) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (is_preview == false && (!(paut.permissions & CTAP_PERMISSION_CM) || paut.has_rp_id == true))
|
||||
}
|
||||
if (is_preview == false &&
|
||||
(!(paut.permissions & CTAP_PERMISSION_CM) || paut.has_rp_id == true)) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
uint8_t existing = 0;
|
||||
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
|
||||
if (file_has_data(search_dynamic_file(EF_CRED + i)))
|
||||
if (file_has_data(search_dynamic_file(EF_CRED + i))) {
|
||||
existing++;
|
||||
}
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 2));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, existing));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x02));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, MAX_RESIDENT_CREDENTIALS-existing));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, MAX_RESIDENT_CREDENTIALS - existing));
|
||||
}
|
||||
else if (subcommand == 0x02 || subcommand == 0x03) {
|
||||
file_t *rp_ef = NULL;
|
||||
if (subcommand == 0x02) {
|
||||
if (verify(pinUvAuthProtocol, paut.data, (const uint8_t *)"\x02", 1, pinUvAuthParam.data) != CborNoError)
|
||||
if (verify(pinUvAuthProtocol, paut.data, (const uint8_t *) "\x02", 1,
|
||||
pinUvAuthParam.data) != CborNoError) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (is_preview == false && (!(paut.permissions & CTAP_PERMISSION_CM) || paut.has_rp_id == true))
|
||||
}
|
||||
if (is_preview == false &&
|
||||
(!(paut.permissions & CTAP_PERMISSION_CM) || paut.has_rp_id == true)) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
rp_counter = 1;
|
||||
rp_total = 0;
|
||||
}
|
||||
else {
|
||||
if (rp_counter > rp_total)
|
||||
if (rp_counter > rp_total) {
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
}
|
||||
}
|
||||
uint8_t skip = 0;
|
||||
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
|
||||
file_t *tef = search_dynamic_file(EF_RP + i);
|
||||
if (file_has_data(tef) && *file_get_data(tef) > 0) {
|
||||
if (++skip == rp_counter) {
|
||||
if (rp_ef == NULL)
|
||||
if (rp_ef == NULL) {
|
||||
rp_ef = tef;
|
||||
if (subcommand == 0x03)
|
||||
}
|
||||
if (subcommand == 0x03) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (subcommand == 0x02)
|
||||
if (subcommand == 0x02) {
|
||||
rp_total++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rp_ef == NULL)
|
||||
if (rp_ef == NULL) {
|
||||
CBOR_ERROR(CTAP2_ERR_NO_CREDENTIALS);
|
||||
}
|
||||
rp_counter++;
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, subcommand == 0x02 ? 3 : 2));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03));
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, 1));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "id"));
|
||||
CBOR_CHECK(cbor_encode_text_string(&mapEncoder2, (char *)file_get_data(rp_ef)+33, file_get_size(rp_ef)-33));
|
||||
CBOR_CHECK(cbor_encode_text_string(&mapEncoder2, (char *) file_get_data(rp_ef) + 33,
|
||||
file_get_size(rp_ef) - 33));
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, file_get_data(rp_ef)+1, 32));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, file_get_data(rp_ef) + 1, 32));
|
||||
if (subcommand == 0x02) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x05));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, rp_total));
|
||||
}
|
||||
}
|
||||
else if (subcommand == 0x04 || subcommand == 0x05) {
|
||||
if (subcommand == 0x04 && rpIdHash.present == false)
|
||||
if (subcommand == 0x04 && rpIdHash.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
if (subcommand == 0x04) {
|
||||
*(raw_subpara-1) = 0x04;
|
||||
if (verify(pinUvAuthProtocol, paut.data, raw_subpara-1, raw_subpara_len+1, pinUvAuthParam.data) != CborNoError)
|
||||
*(raw_subpara - 1) = 0x04;
|
||||
if (verify(pinUvAuthProtocol, paut.data, raw_subpara - 1, raw_subpara_len + 1,
|
||||
pinUvAuthParam.data) != CborNoError) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (is_preview == false && (!(paut.permissions & CTAP_PERMISSION_CM) || (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0)))
|
||||
}
|
||||
if (is_preview == false &&
|
||||
(!(paut.permissions & CTAP_PERMISSION_CM) ||
|
||||
(paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0))) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
cred_counter = 1;
|
||||
cred_total = 0;
|
||||
}
|
||||
@@ -196,21 +226,27 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
|
||||
file_t *tef = search_dynamic_file(EF_CRED + i);
|
||||
if (file_has_data(tef) && memcmp(file_get_data(tef), rpIdHash.data, 32) == 0) {
|
||||
if (++skip == cred_counter) {
|
||||
if (cred_ef == NULL)
|
||||
if (cred_ef == NULL) {
|
||||
cred_ef = tef;
|
||||
if (subcommand == 0x05)
|
||||
}
|
||||
if (subcommand == 0x05) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (subcommand == 0x04)
|
||||
if (subcommand == 0x04) {
|
||||
cred_total++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!file_has_data(cred_ef))
|
||||
if (!file_has_data(cred_ef)) {
|
||||
CBOR_ERROR(CTAP2_ERR_NO_CREDENTIALS);
|
||||
}
|
||||
|
||||
Credential cred = {0};
|
||||
if (credential_load(file_get_data(cred_ef)+32, file_get_size(cred_ef)-32, rpIdHash.data, &cred) != 0)
|
||||
Credential cred = { 0 };
|
||||
if (credential_load(file_get_data(cred_ef) + 32, file_get_size(cred_ef) - 32, rpIdHash.data,
|
||||
&cred) != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
}
|
||||
|
||||
mbedtls_ecdsa_context key;
|
||||
mbedtls_ecdsa_init(&key);
|
||||
@@ -222,25 +258,31 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
|
||||
|
||||
cred_counter++;
|
||||
|
||||
uint8_t l = 4;
|
||||
if (subcommand == 0x04)
|
||||
uint8_t l = 3;
|
||||
if (subcommand == 0x04) {
|
||||
l++;
|
||||
}
|
||||
if (cred.extensions.present == true) {
|
||||
if (cred.extensions.credProtect > 0)
|
||||
l++;
|
||||
if (cred.extensions.largeBlobKey == ptrue)
|
||||
l++;
|
||||
if (cred.extensions.credProtect > 0) {
|
||||
l++;
|
||||
}
|
||||
if (cred.extensions.largeBlobKey == ptrue) {
|
||||
l++;
|
||||
}
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, l));
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x06));
|
||||
l = 0;
|
||||
if (cred.userId.present == true)
|
||||
if (cred.userId.present == true) {
|
||||
l++;
|
||||
if (cred.userName.present == true)
|
||||
}
|
||||
if (cred.userName.present == true) {
|
||||
l++;
|
||||
if (cred.userDisplayName.present == true)
|
||||
}
|
||||
if (cred.userDisplayName.present == true) {
|
||||
l++;
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, l));
|
||||
if (cred.userId.present == true) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "id"));
|
||||
@@ -248,11 +290,13 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
|
||||
}
|
||||
if (cred.userName.present == true) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "name"));
|
||||
CBOR_CHECK(cbor_encode_text_string(&mapEncoder2, cred.userName.data, cred.userName.len));
|
||||
CBOR_CHECK(cbor_encode_text_string(&mapEncoder2, cred.userName.data,
|
||||
cred.userName.len));
|
||||
}
|
||||
if (cred.userDisplayName.present == true) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "displayName"));
|
||||
CBOR_CHECK(cbor_encode_text_string(&mapEncoder2, cred.userDisplayName.data, cred.userDisplayName.len));
|
||||
CBOR_CHECK(cbor_encode_text_string(&mapEncoder2, cred.userDisplayName.data,
|
||||
cred.userDisplayName.len));
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
|
||||
|
||||
@@ -301,7 +345,8 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
|
||||
CBOR_ERROR(CTAP2_ERR_PROCESSING);
|
||||
}
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0B));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, largeBlobKey, sizeof(largeBlobKey)));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, largeBlobKey,
|
||||
sizeof(largeBlobKey)));
|
||||
mbedtls_platform_zeroize(largeBlobKey, sizeof(largeBlobKey));
|
||||
}
|
||||
}
|
||||
@@ -309,29 +354,41 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
|
||||
mbedtls_ecdsa_free(&key);
|
||||
}
|
||||
else if (subcommand == 0x06) {
|
||||
if (credentialId.id.present == false)
|
||||
if (credentialId.id.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
*(raw_subpara - 1) = 0x06;
|
||||
if (verify(pinUvAuthProtocol, paut.data, raw_subpara-1, raw_subpara_len+1, pinUvAuthParam.data) != CborNoError)
|
||||
if (verify(pinUvAuthProtocol, paut.data, raw_subpara - 1, raw_subpara_len + 1,
|
||||
pinUvAuthParam.data) != CborNoError) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (is_preview == false && (!(paut.permissions & CTAP_PERMISSION_CM) || (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0)))
|
||||
}
|
||||
if (is_preview == false &&
|
||||
(!(paut.permissions & CTAP_PERMISSION_CM) ||
|
||||
(paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0))) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
|
||||
file_t *ef = search_dynamic_file(EF_CRED + i);
|
||||
if (file_has_data(ef) && memcmp(file_get_data(ef)+32, credentialId.id.data, MIN(file_get_size(ef)-32, credentialId.id.len)) == 0) {
|
||||
if (file_has_data(ef) &&
|
||||
memcmp(file_get_data(ef) + 32, credentialId.id.data,
|
||||
MIN(file_get_size(ef) - 32, credentialId.id.len)) == 0) {
|
||||
uint8_t *rp_id_hash = file_get_data(ef);
|
||||
if (delete_file(ef) != 0)
|
||||
if (delete_file(ef) != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
}
|
||||
for (int j = 0; j < MAX_RESIDENT_CREDENTIALS; j++) {
|
||||
file_t *rp_ef = search_dynamic_file(EF_RP + j);
|
||||
if (file_has_data(rp_ef) && memcmp(file_get_data(rp_ef)+1, rp_id_hash, 32) == 0) {
|
||||
uint8_t *rp_data = (uint8_t *)calloc(1, file_get_size(rp_ef));
|
||||
if (file_has_data(rp_ef) &&
|
||||
memcmp(file_get_data(rp_ef) + 1, rp_id_hash, 32) == 0) {
|
||||
uint8_t *rp_data = (uint8_t *) calloc(1, file_get_size(rp_ef));
|
||||
memcpy(rp_data, file_get_data(rp_ef), file_get_size(rp_ef));
|
||||
rp_data[0] -= 1;
|
||||
if (rp_data[0] == 0)
|
||||
if (rp_data[0] == 0) {
|
||||
delete_file(rp_ef);
|
||||
else
|
||||
}
|
||||
else {
|
||||
flash_write_data_to_file(rp_ef, rp_data, file_get_size(rp_ef));
|
||||
}
|
||||
free(rp_data);
|
||||
break;
|
||||
}
|
||||
@@ -343,27 +400,41 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
|
||||
CBOR_ERROR(CTAP2_ERR_NO_CREDENTIALS);
|
||||
}
|
||||
else if (subcommand == 0x07) {
|
||||
if (credentialId.id.present == false || user.id.present == false)
|
||||
if (credentialId.id.present == false || user.id.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
*(raw_subpara - 1) = 0x07;
|
||||
if (verify(pinUvAuthProtocol, paut.data, raw_subpara-1, raw_subpara_len+1, pinUvAuthParam.data) != CborNoError)
|
||||
if (verify(pinUvAuthProtocol, paut.data, raw_subpara - 1, raw_subpara_len + 1,
|
||||
pinUvAuthParam.data) != CborNoError) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (is_preview == false && (!(paut.permissions & CTAP_PERMISSION_CM) || (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0)))
|
||||
}
|
||||
if (is_preview == false &&
|
||||
(!(paut.permissions & CTAP_PERMISSION_CM) ||
|
||||
(paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0))) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
|
||||
file_t *ef = search_dynamic_file(EF_CRED + i);
|
||||
if (file_has_data(ef) && memcmp(file_get_data(ef)+32, credentialId.id.data, MIN(file_get_size(ef)-32, credentialId.id.len)) == 0) {
|
||||
Credential cred = {0};
|
||||
if (file_has_data(ef) &&
|
||||
memcmp(file_get_data(ef) + 32, credentialId.id.data,
|
||||
MIN(file_get_size(ef) - 32, credentialId.id.len)) == 0) {
|
||||
Credential cred = { 0 };
|
||||
uint8_t *rp_id_hash = file_get_data(ef);
|
||||
if (credential_load(rp_id_hash+32, file_get_size(ef)-32, rp_id_hash, &cred) != 0)
|
||||
if (credential_load(rp_id_hash + 32, file_get_size(ef) - 32, rp_id_hash,
|
||||
&cred) != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
if (memcmp(user.id.data, cred.userId.data, MIN(user.id.len, cred.userId.len)) != 0) {
|
||||
}
|
||||
if (memcmp(user.id.data, cred.userId.data,
|
||||
MIN(user.id.len, cred.userId.len)) != 0) {
|
||||
credential_free(&cred);
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
uint8_t newcred[MAX_CRED_ID_LENGTH];
|
||||
size_t newcred_len = 0;
|
||||
if (credential_create(&cred.rpId, &cred.userId, &user.parent.name, &user.displayName, &cred.opts, &cred.extensions, cred.use_sign_count, cred.alg, cred.curve, newcred, &newcred_len) != 0) {
|
||||
if (credential_create(&cred.rpId, &cred.userId, &user.parent.name,
|
||||
&user.displayName, &cred.opts, &cred.extensions,
|
||||
cred.use_sign_count, cred.alg,
|
||||
cred.curve, newcred, &newcred_len) != 0) {
|
||||
credential_free(&cred);
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
}
|
||||
@@ -379,7 +450,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
resp_size = cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1);
|
||||
err:
|
||||
err:
|
||||
CBOR_FREE_BYTE_STRING(pinUvAuthParam);
|
||||
|
||||
if (asserted == false) {
|
||||
@@ -390,11 +461,12 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
|
||||
CBOR_FREE_BYTE_STRING(user.parent.name);
|
||||
CBOR_FREE_BYTE_STRING(credentialId.type);
|
||||
for (int n = 0; n < credentialId.transports_len; n++) {
|
||||
CBOR_FREE_BYTE_STRING(credentialId.transports[n]);
|
||||
}
|
||||
CBOR_FREE_BYTE_STRING(credentialId.transports[n]);
|
||||
}
|
||||
if (error != CborNoError) {
|
||||
if (error == CborErrorImproperValue)
|
||||
if (error == CborErrorImproperValue) {
|
||||
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
res_APDU_size = resp_size;
|
||||
|
||||
@@ -17,22 +17,24 @@
|
||||
|
||||
#include "cbor.h"
|
||||
#include "ctap.h"
|
||||
#include "ctap2_cbor.h"
|
||||
#ifndef ENABLE_EMULATION
|
||||
#include "bsp/board.h"
|
||||
#endif
|
||||
#include "hid/ctap_hid.h"
|
||||
#include "fido.h"
|
||||
#include "files.h"
|
||||
#include "random.h"
|
||||
#include "crypto_utils.h"
|
||||
#include "hsm.h"
|
||||
#include "apdu.h"
|
||||
#include "cbor_make_credential.h"
|
||||
#include "credential.h"
|
||||
#include <math.h>
|
||||
#include "mbedtls/sha256.h"
|
||||
#include "random.h"
|
||||
|
||||
int cbor_get_assertion(const uint8_t *data, size_t len, bool next);
|
||||
|
||||
bool residentx = false;
|
||||
Credential credsx[MAX_CREDENTIAL_COUNT_IN_LIST] = {0};
|
||||
Credential credsx[MAX_CREDENTIAL_COUNT_IN_LIST] = { 0 };
|
||||
uint8_t credentialCounter = 1;
|
||||
uint8_t numberOfCredentialsx = 0;
|
||||
uint8_t flagsx = 0;
|
||||
@@ -42,17 +44,20 @@ size_t lenx = 0;
|
||||
|
||||
int cbor_get_next_assertion(const uint8_t *data, size_t len) {
|
||||
CborError error = CborNoError;
|
||||
if (credentialCounter >= numberOfCredentialsx)
|
||||
if (credentialCounter >= numberOfCredentialsx) {
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
if (timerx+30*1000 < board_millis())
|
||||
}
|
||||
if (timerx + 30 * 1000 < board_millis()) {
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
}
|
||||
CBOR_CHECK(cbor_get_assertion(datax, lenx, true));
|
||||
timerx = board_millis();
|
||||
credentialCounter++;
|
||||
err:
|
||||
if (error != CborNoError || credentialCounter == numberOfCredentialsx) {
|
||||
for (int i = 0; i < MAX_CREDENTIAL_COUNT_IN_LIST; i++)
|
||||
for (int i = 0; i < MAX_CREDENTIAL_COUNT_IN_LIST; i++) {
|
||||
credential_free(&credsx[i]);
|
||||
}
|
||||
if (datax) {
|
||||
free(datax);
|
||||
datax = NULL;
|
||||
@@ -63,8 +68,9 @@ err:
|
||||
flagsx = 0;
|
||||
credentialCounter = 0;
|
||||
numberOfCredentialsx = 0;
|
||||
if (error == CborErrorImproperValue)
|
||||
if (error == CborErrorImproperValue) {
|
||||
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
return 0;
|
||||
@@ -73,32 +79,35 @@ err:
|
||||
int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
size_t resp_size = 0;
|
||||
uint64_t pinUvAuthProtocol = 0, hmacSecretPinUvAuthProtocol = 1;
|
||||
CredOptions options = {0};
|
||||
CredExtensions extensions = {0};
|
||||
CredOptions options = { 0 };
|
||||
CredExtensions extensions = { 0 };
|
||||
CborParser parser;
|
||||
CborEncoder encoder, mapEncoder, mapEncoder2;
|
||||
CborValue map;
|
||||
CborError error = CborNoError;
|
||||
CborByteString pinUvAuthParam = {0}, clientDataHash = {0};
|
||||
CborCharString rpId = {0};
|
||||
PublicKeyCredentialDescriptor allowList[MAX_CREDENTIAL_COUNT_IN_LIST] = {0};
|
||||
Credential creds[MAX_CREDENTIAL_COUNT_IN_LIST] = {0};
|
||||
CborByteString pinUvAuthParam = { 0 }, clientDataHash = { 0 };
|
||||
CborCharString rpId = { 0 };
|
||||
PublicKeyCredentialDescriptor allowList[MAX_CREDENTIAL_COUNT_IN_LIST] = { 0 };
|
||||
Credential creds[MAX_CREDENTIAL_COUNT_IN_LIST] = { 0 };
|
||||
size_t allowList_len = 0, creds_len = 0;
|
||||
uint8_t *aut_data = NULL;
|
||||
bool asserted = false, up = true, uv = false;
|
||||
int64_t kty = 2, alg = 0, crv = 0;
|
||||
CborByteString kax = {0}, kay = {0}, salt_enc = {0}, salt_auth = {0};
|
||||
CborByteString kax = { 0 }, kay = { 0 }, salt_enc = { 0 }, salt_auth = { 0 };
|
||||
const bool *credBlob = NULL;
|
||||
|
||||
CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map));
|
||||
uint64_t val_c = 1;
|
||||
CBOR_PARSE_MAP_START(map, 1) {
|
||||
CBOR_PARSE_MAP_START(map, 1)
|
||||
{
|
||||
uint64_t val_u = 0;
|
||||
CBOR_FIELD_GET_UINT(val_u, 1);
|
||||
if (val_c <= 2 && val_c != val_u)
|
||||
if (val_c <= 2 && val_c != val_u) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (val_u < val_c)
|
||||
}
|
||||
if (val_u < val_c) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
|
||||
}
|
||||
val_c = val_u + 1;
|
||||
if (val_u == 0x01) {
|
||||
CBOR_FIELD_GET_TEXT(rpId, 1);
|
||||
@@ -107,14 +116,17 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
CBOR_FIELD_GET_BYTES(clientDataHash, 1);
|
||||
}
|
||||
else if (val_u == 0x03) { // excludeList
|
||||
CBOR_PARSE_ARRAY_START(_f1, 2) {
|
||||
PublicKeyCredentialDescriptor *pc = &allowList[allowList_len];
|
||||
CBOR_PARSE_MAP_START(_f2, 3) {
|
||||
CBOR_PARSE_ARRAY_START(_f1, 2)
|
||||
{
|
||||
PublicKeyCredentialDescriptor *pc = &allowList[allowList_len];
|
||||
CBOR_PARSE_MAP_START(_f2, 3)
|
||||
{
|
||||
CBOR_FIELD_GET_KEY_TEXT(3);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BYTES(3, "id", pc->id);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_TEXT(3, "type", pc->type);
|
||||
if (strcmp(_fd3, "transports") == 0) {
|
||||
CBOR_PARSE_ARRAY_START(_f3, 4) {
|
||||
CBOR_PARSE_ARRAY_START(_f3, 4)
|
||||
{
|
||||
CBOR_FIELD_GET_TEXT(pc->transports[pc->transports_len], 4);
|
||||
pc->transports_len++;
|
||||
}
|
||||
@@ -128,16 +140,19 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
}
|
||||
else if (val_u == 0x04) { // extensions
|
||||
extensions.present = true;
|
||||
CBOR_PARSE_MAP_START(_f1, 2) {
|
||||
CBOR_PARSE_MAP_START(_f1, 2)
|
||||
{
|
||||
CBOR_FIELD_GET_KEY_TEXT(2);
|
||||
if (strcmp(_fd2, "hmac-secret") == 0) {
|
||||
extensions.hmac_secret = ptrue;
|
||||
uint64_t ukey = 0;
|
||||
CBOR_PARSE_MAP_START(_f2, 3) {
|
||||
CBOR_PARSE_MAP_START(_f2, 3)
|
||||
{
|
||||
CBOR_FIELD_GET_UINT(ukey, 3);
|
||||
if (ukey == 0x01) {
|
||||
int64_t kkey = 0;
|
||||
CBOR_PARSE_MAP_START(_f3, 4) {
|
||||
CBOR_PARSE_MAP_START(_f3, 4)
|
||||
{
|
||||
CBOR_FIELD_GET_INT(kkey, 4);
|
||||
if (kkey == 1) {
|
||||
CBOR_FIELD_GET_INT(kty, 4);
|
||||
@@ -154,8 +169,9 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
else if (kkey == -3) {
|
||||
CBOR_FIELD_GET_BYTES(kay, 4);
|
||||
}
|
||||
else
|
||||
else {
|
||||
CBOR_ADVANCE(4);
|
||||
}
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f3, 4);
|
||||
}
|
||||
@@ -168,8 +184,9 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
else if (ukey == 0x04) {
|
||||
CBOR_FIELD_GET_UINT(hmacSecretPinUvAuthProtocol, 3);
|
||||
}
|
||||
else
|
||||
else {
|
||||
CBOR_ADVANCE(3);
|
||||
}
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f2, 3);
|
||||
continue;
|
||||
@@ -182,7 +199,8 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
}
|
||||
else if (val_u == 0x05) { // options
|
||||
options.present = true;
|
||||
CBOR_PARSE_MAP_START(_f1, 2) {
|
||||
CBOR_PARSE_MAP_START(_f1, 2)
|
||||
{
|
||||
CBOR_FIELD_GET_KEY_TEXT(2);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "rk", options.rk);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "up", options.up);
|
||||
@@ -200,12 +218,13 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
}
|
||||
CBOR_PARSE_MAP_END(map, 1);
|
||||
|
||||
if (rpId.present == false || clientDataHash.present == false)
|
||||
if (rpId.present == false || clientDataHash.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
|
||||
uint8_t flags = 0;
|
||||
uint8_t rp_id_hash[32];
|
||||
mbedtls_sha256((uint8_t *)rpId.data, rpId.len, rp_id_hash, 0);
|
||||
mbedtls_sha256((uint8_t *) rpId.data, rpId.len, rp_id_hash, 0);
|
||||
|
||||
bool resident = false;
|
||||
uint8_t numberOfCredentials = 0;
|
||||
@@ -213,18 +232,23 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
if (next == false) {
|
||||
if (pinUvAuthParam.present == true) {
|
||||
if (pinUvAuthParam.len == 0 || pinUvAuthParam.data == NULL) {
|
||||
if (check_user_presence() == false)
|
||||
if (check_user_presence() == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_OPERATION_DENIED);
|
||||
if (!file_has_data(ef_pin))
|
||||
}
|
||||
if (!file_has_data(ef_pin)) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_NOT_SET);
|
||||
else
|
||||
}
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (pinUvAuthProtocol == 0)
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2)
|
||||
if (pinUvAuthProtocol == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (options.present) {
|
||||
@@ -238,76 +262,109 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION);
|
||||
}
|
||||
//else if (options.up == NULL) //5.7
|
||||
//rup = ptrue;
|
||||
if (options.uv != NULL)
|
||||
//rup = ptrue;
|
||||
if (options.uv != NULL) {
|
||||
uv = *options.uv;
|
||||
if (options.up != NULL)
|
||||
}
|
||||
if (options.up != NULL) {
|
||||
up = *options.up;
|
||||
}
|
||||
}
|
||||
|
||||
if (pinUvAuthParam.present == true) { //6.1
|
||||
int ret = verify(pinUvAuthProtocol, paut.data, clientDataHash.data, clientDataHash.len, pinUvAuthParam.data);
|
||||
if (ret != CborNoError)
|
||||
int ret = verify(pinUvAuthProtocol,
|
||||
paut.data,
|
||||
clientDataHash.data,
|
||||
clientDataHash.len,
|
||||
pinUvAuthParam.data);
|
||||
if (ret != CborNoError) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (getUserVerifiedFlagValue() == false)
|
||||
}
|
||||
if (getUserVerifiedFlagValue() == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (!(paut.permissions & CTAP_PERMISSION_GA))
|
||||
}
|
||||
if (!(paut.permissions & CTAP_PERMISSION_GA)) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rp_id_hash, 32) != 0)
|
||||
}
|
||||
if (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rp_id_hash, 32) != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
flags |= FIDO2_AUT_FLAG_UV;
|
||||
// Check pinUvAuthToken permissions. See 6.2.2.4
|
||||
}
|
||||
if (extensions.present == true && extensions.hmac_secret == ptrue) {
|
||||
if (kax.present == false || kay.present == false || crv == 0 || alg == 0 || salt_enc.present == false || salt_auth.present == false)
|
||||
if (kax.present == false || kay.present == false || crv == 0 || alg == 0 ||
|
||||
salt_enc.present == false || salt_auth.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (salt_enc.len != 32+(hmacSecretPinUvAuthProtocol-1)*IV_SIZE && salt_enc.len != 64+(hmacSecretPinUvAuthProtocol-1)*IV_SIZE)
|
||||
}
|
||||
if (salt_enc.len != 32 + (hmacSecretPinUvAuthProtocol - 1) * IV_SIZE &&
|
||||
salt_enc.len != 64 + (hmacSecretPinUvAuthProtocol - 1) * IV_SIZE) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
if (allowList_len > 0) {
|
||||
for (int e = 0; e < allowList_len; e++) {
|
||||
if (allowList[e].type.present == false || allowList[e].id.present == false)
|
||||
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)
|
||||
}
|
||||
if (strcmp(allowList[e].type.data, "public-key") != 0) {
|
||||
continue;
|
||||
if (credential_load(allowList[e].id.data, allowList[e].id.len, rp_id_hash, &creds[creds_len]) != 0) {
|
||||
}
|
||||
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
|
||||
else {
|
||||
creds_len++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS && creds_len < MAX_CREDENTIAL_COUNT_IN_LIST; i++) {
|
||||
for (int i = 0;
|
||||
i < MAX_RESIDENT_CREDENTIALS && creds_len < MAX_CREDENTIAL_COUNT_IN_LIST;
|
||||
i++) {
|
||||
file_t *ef = search_dynamic_file(EF_CRED + i);
|
||||
if (!file_has_data(ef) || memcmp(file_get_data(ef), rp_id_hash, 32) != 0)
|
||||
if (!file_has_data(ef) || memcmp(file_get_data(ef), rp_id_hash, 32) != 0) {
|
||||
continue;
|
||||
int ret = credential_load(file_get_data(ef) + 32, file_get_size(ef) - 32, rp_id_hash, &creds[creds_len]);
|
||||
if (ret != 0)
|
||||
}
|
||||
int ret = credential_load(file_get_data(ef) + 32,
|
||||
file_get_size(ef) - 32,
|
||||
rp_id_hash,
|
||||
&creds[creds_len]);
|
||||
if (ret != 0) {
|
||||
credential_free(&creds[creds_len]);
|
||||
else
|
||||
}
|
||||
else {
|
||||
creds_len++;
|
||||
}
|
||||
}
|
||||
resident = true;
|
||||
}
|
||||
for (int i = 0; i < creds_len; i++) {
|
||||
if (creds[i].present == true) {
|
||||
if (creds[i].extensions.present == true) {
|
||||
if (creds[i].extensions.credProtect == CRED_PROT_UV_REQUIRED && !(flags & FIDO2_AUT_FLAG_UV))
|
||||
if (creds[i].extensions.credProtect == CRED_PROT_UV_REQUIRED &&
|
||||
!(flags & FIDO2_AUT_FLAG_UV)) {
|
||||
credential_free(&creds[i]);
|
||||
else if (creds[i].extensions.credProtect == CRED_PROT_UV_OPTIONAL_WITH_LIST && resident == true && !(flags & FIDO2_AUT_FLAG_UV))
|
||||
}
|
||||
else if (creds[i].extensions.credProtect == CRED_PROT_UV_OPTIONAL_WITH_LIST &&
|
||||
resident == true && !(flags & FIDO2_AUT_FLAG_UV)) {
|
||||
credential_free(&creds[i]);
|
||||
else
|
||||
}
|
||||
else {
|
||||
creds[numberOfCredentials++] = creds[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
else {
|
||||
creds[numberOfCredentials++] = creds[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (numberOfCredentials == 0)
|
||||
if (numberOfCredentials == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_NO_CREDENTIALS);
|
||||
}
|
||||
|
||||
for (int i = 0; i < numberOfCredentials; i++) {
|
||||
for (int j = i + 1; j < numberOfCredentials; j++) {
|
||||
@@ -322,14 +379,16 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
if (options.up == ptrue || options.present == false || options.up == NULL) { //9.1
|
||||
if (pinUvAuthParam.present == true) {
|
||||
if (getUserPresentFlagValue() == false) {
|
||||
if (check_user_presence() == false)
|
||||
if (check_user_presence() == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_OPERATION_DENIED);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!(flags & FIDO2_AUT_FLAG_UP)) {
|
||||
if (check_user_presence() == false)
|
||||
if (check_user_presence() == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_OPERATION_DENIED);
|
||||
}
|
||||
}
|
||||
}
|
||||
flags |= FIDO2_AUT_FLAG_UP;
|
||||
@@ -350,10 +409,11 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
if (numberOfCredentials > 1) {
|
||||
asserted = true;
|
||||
residentx = resident;
|
||||
for (int i = 0; i < MAX_CREDENTIAL_COUNT_IN_LIST; i++)
|
||||
for (int i = 0; i < MAX_CREDENTIAL_COUNT_IN_LIST; i++) {
|
||||
credsx[i] = creds[i];
|
||||
}
|
||||
numberOfCredentialsx = numberOfCredentials;
|
||||
datax = (uint8_t *)calloc(1, len);
|
||||
datax = (uint8_t *) calloc(1, len);
|
||||
memcpy(datax, data, len);
|
||||
lenx = len;
|
||||
flagsx = flags;
|
||||
@@ -387,23 +447,29 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
}
|
||||
|
||||
size_t ext_len = 0;
|
||||
uint8_t ext [512];
|
||||
uint8_t ext[512];
|
||||
if (extensions.present == true) {
|
||||
cbor_encoder_init(&encoder, ext, sizeof(ext), 0);
|
||||
int l = 0;
|
||||
if (options.up == pfalse)
|
||||
if (options.up == pfalse) {
|
||||
extensions.hmac_secret = NULL;
|
||||
if (extensions.hmac_secret != NULL)
|
||||
}
|
||||
if (extensions.hmac_secret != NULL) {
|
||||
l++;
|
||||
if (credBlob == ptrue)
|
||||
}
|
||||
if (credBlob == ptrue) {
|
||||
l++;
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, l));
|
||||
if (credBlob == ptrue) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "credBlob"));
|
||||
if (selcred->extensions.credBlob.present == true)
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, selcred->extensions.credBlob.data, selcred->extensions.credBlob.len));
|
||||
else
|
||||
if (selcred->extensions.credBlob.present == true) {
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, selcred->extensions.credBlob.data,
|
||||
selcred->extensions.credBlob.len));
|
||||
}
|
||||
else {
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, NULL, 0));
|
||||
}
|
||||
}
|
||||
if (extensions.hmac_secret != NULL) {
|
||||
|
||||
@@ -427,12 +493,17 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
if (verify(hmacSecretPinUvAuthProtocol, sharedSecret, salt_enc.data, salt_enc.len, salt_auth.data) != 0) {
|
||||
if (verify(hmacSecretPinUvAuthProtocol, sharedSecret, salt_enc.data, salt_enc.len,
|
||||
salt_auth.data) != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP2_ERR_EXTENSION_FIRST);
|
||||
}
|
||||
uint8_t salt_dec[64], poff = (hmacSecretPinUvAuthProtocol-1)*IV_SIZE;
|
||||
ret = decrypt(hmacSecretPinUvAuthProtocol, sharedSecret, salt_enc.data, salt_enc.len, salt_dec);
|
||||
uint8_t salt_dec[64], poff = (hmacSecretPinUvAuthProtocol - 1) * IV_SIZE;
|
||||
ret = decrypt(hmacSecretPinUvAuthProtocol,
|
||||
sharedSecret,
|
||||
salt_enc.data,
|
||||
salt_enc.len,
|
||||
salt_dec);
|
||||
if (ret != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
@@ -443,15 +514,28 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
if (flags & FIDO2_AUT_FLAG_UV)
|
||||
if (flags & FIDO2_AUT_FLAG_UV) {
|
||||
crd = cred_random + 32;
|
||||
else
|
||||
}
|
||||
else {
|
||||
crd = cred_random;
|
||||
}
|
||||
uint8_t out1[64], hmac_res[80];
|
||||
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), crd, 32, salt_dec, 32, out1);
|
||||
if (salt_enc.len == 64+poff)
|
||||
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), crd, 32, salt_dec+32, 32, out1+32);
|
||||
encrypt(hmacSecretPinUvAuthProtocol, sharedSecret, out1, salt_enc.len-poff, hmac_res);
|
||||
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
|
||||
crd,
|
||||
32,
|
||||
salt_dec,
|
||||
32,
|
||||
out1);
|
||||
if (salt_enc.len == 64 + poff) {
|
||||
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
|
||||
crd,
|
||||
32,
|
||||
salt_dec + 32,
|
||||
32,
|
||||
out1 + 32);
|
||||
}
|
||||
encrypt(hmacSecretPinUvAuthProtocol, sharedSecret, out1, salt_enc.len - poff, hmac_res);
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, hmac_res, salt_enc.len));
|
||||
}
|
||||
|
||||
@@ -463,7 +547,7 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
uint32_t ctr = get_sign_counter();
|
||||
|
||||
size_t aut_data_len = 32 + 1 + 4 + ext_len;
|
||||
aut_data = (uint8_t *)calloc(1, aut_data_len + clientDataHash.len);
|
||||
aut_data = (uint8_t *) calloc(1, aut_data_len + clientDataHash.len);
|
||||
uint8_t *pa = aut_data;
|
||||
memcpy(pa, rp_id_hash, 32); pa += 32;
|
||||
*pa++ = flags;
|
||||
@@ -472,23 +556,38 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
*pa++ = ctr >> 8;
|
||||
*pa++ = ctr & 0xff;
|
||||
memcpy(pa, ext, ext_len); pa += ext_len;
|
||||
if (pa-aut_data != aut_data_len)
|
||||
if (pa - aut_data != aut_data_len) {
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
}
|
||||
|
||||
memcpy(pa, clientDataHash.data, clientDataHash.len);
|
||||
uint8_t hash[32], sig[MBEDTLS_ECDSA_MAX_LEN];
|
||||
ret = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), aut_data, aut_data_len+clientDataHash.len, hash);
|
||||
ret = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
|
||||
aut_data,
|
||||
aut_data_len + clientDataHash.len,
|
||||
hash);
|
||||
size_t olen = 0;
|
||||
ret = mbedtls_ecdsa_write_signature(&ekey, MBEDTLS_MD_SHA256, hash, 32, sig, sizeof(sig), &olen, random_gen, NULL);
|
||||
ret = mbedtls_ecdsa_write_signature(&ekey,
|
||||
MBEDTLS_MD_SHA256,
|
||||
hash,
|
||||
32,
|
||||
sig,
|
||||
sizeof(sig),
|
||||
&olen,
|
||||
random_gen,
|
||||
NULL);
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
|
||||
uint8_t lfields = 3;
|
||||
if (selcred->opts.present == true && selcred->opts.rk == ptrue)
|
||||
if (selcred->opts.present == true && selcred->opts.rk == ptrue) {
|
||||
lfields++;
|
||||
if (numberOfCredentials > 1 && next == false)
|
||||
}
|
||||
if (numberOfCredentials > 1 && next == false) {
|
||||
lfields++;
|
||||
if (extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue)
|
||||
}
|
||||
if (extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
|
||||
lfields++;
|
||||
}
|
||||
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, lfields));
|
||||
|
||||
@@ -509,14 +608,17 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04));
|
||||
uint8_t lu = 1;
|
||||
if (numberOfCredentials > 1 && allowList_len == 0) {
|
||||
if (selcred->userName.present == true)
|
||||
if (selcred->userName.present == true) {
|
||||
lu++;
|
||||
if (selcred->userDisplayName.present == true)
|
||||
}
|
||||
if (selcred->userDisplayName.present == true) {
|
||||
lu++;
|
||||
}
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, lu));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "id"));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, selcred->userId.data, selcred->userId.len));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, selcred->userId.data,
|
||||
selcred->userId.len));
|
||||
if (numberOfCredentials > 1 && allowList_len == 0) {
|
||||
if (selcred->userName.present == true) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "name"));
|
||||
@@ -541,15 +643,16 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
resp_size = cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1);
|
||||
ctr++;
|
||||
flash_write_data_to_file(ef_counter, (uint8_t *)&ctr, sizeof(ctr));
|
||||
flash_write_data_to_file(ef_counter, (uint8_t *) &ctr, sizeof(ctr));
|
||||
low_flash_available();
|
||||
err:
|
||||
err:
|
||||
CBOR_FREE_BYTE_STRING(clientDataHash);
|
||||
CBOR_FREE_BYTE_STRING(pinUvAuthParam);
|
||||
CBOR_FREE_BYTE_STRING(rpId);
|
||||
if (asserted == false) {
|
||||
for (int i = 0; i < MAX_CREDENTIAL_COUNT_IN_LIST; i++)
|
||||
for (int i = 0; i < MAX_CREDENTIAL_COUNT_IN_LIST; i++) {
|
||||
credential_free(&creds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int m = 0; m < allowList_len; m++) {
|
||||
@@ -559,11 +662,13 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
CBOR_FREE_BYTE_STRING(allowList[m].transports[n]);
|
||||
}
|
||||
}
|
||||
if (aut_data)
|
||||
if (aut_data) {
|
||||
free(aut_data);
|
||||
}
|
||||
if (error != CborNoError) {
|
||||
if (error == CborErrorImproperValue)
|
||||
if (error == CborErrorImproperValue) {
|
||||
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
res_APDU_size = resp_size;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
#include "ctap2_cbor.h"
|
||||
#include "hid/ctap_hid.h"
|
||||
#include "fido.h"
|
||||
#include "ctap.h"
|
||||
#include "files.h"
|
||||
@@ -58,10 +59,12 @@ int cbor_get_info() {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "authnrCfg"));
|
||||
CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, true));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "clientPin"));
|
||||
if (file_has_data(ef_pin))
|
||||
if (file_has_data(ef_pin)) {
|
||||
CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, true));
|
||||
else
|
||||
}
|
||||
else {
|
||||
CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, false));
|
||||
}
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "largeBlobs"));
|
||||
CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, true));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "pinUvAuthToken"));
|
||||
@@ -112,16 +115,20 @@ int cbor_get_info() {
|
||||
|
||||
file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0C));
|
||||
if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1)
|
||||
if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1) {
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, true));
|
||||
else
|
||||
}
|
||||
else {
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, false));
|
||||
}
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0D));
|
||||
if (file_has_data(ef_minpin))
|
||||
if (file_has_data(ef_minpin)) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, *file_get_data(ef_minpin))); // minPINLength
|
||||
else
|
||||
}
|
||||
else {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 4)); // minPINLength
|
||||
|
||||
}
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0E));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, PICO_FIDO_VERSION)); // firmwareVersion
|
||||
|
||||
@@ -135,9 +142,10 @@ int cbor_get_info() {
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &arrayEncoder));
|
||||
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
err:
|
||||
if (error != CborNoError)
|
||||
err:
|
||||
if (error != CborNoError) {
|
||||
return -CTAP2_ERR_INVALID_CBOR;
|
||||
}
|
||||
res_APDU_size = cbor_encoder_get_buffer_size(&encoder, res_APDU + 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
#include "ctap2_cbor.h"
|
||||
#include "fido.h"
|
||||
#include "ctap.h"
|
||||
#include "hid/ctap_hid.h"
|
||||
#include "files.h"
|
||||
#include "apdu.h"
|
||||
#include "version.h"
|
||||
#include "hsm.h"
|
||||
#include "mbedtls/sha256.h"
|
||||
|
||||
@@ -33,17 +33,20 @@ int cbor_large_blobs(const uint8_t *data, size_t len) {
|
||||
CborEncoder encoder, mapEncoder;
|
||||
CborError error = CborNoError;
|
||||
uint64_t get = 0, offset = UINT64_MAX, length = 0, pinUvAuthProtocol = 0;
|
||||
CborByteString set = {0}, pinUvAuthParam = {0};
|
||||
CborByteString set = { 0 }, pinUvAuthParam = { 0 };
|
||||
|
||||
CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map));
|
||||
uint64_t val_c = 1;
|
||||
CBOR_PARSE_MAP_START(map, 1) {
|
||||
CBOR_PARSE_MAP_START(map, 1)
|
||||
{
|
||||
uint64_t val_u = 0;
|
||||
CBOR_FIELD_GET_UINT(val_u, 1);
|
||||
if (val_c <= 0 && val_c != val_u)
|
||||
if (val_c <= 0 && val_c != val_u) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (val_u < val_c)
|
||||
}
|
||||
if (val_u < val_c) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
|
||||
}
|
||||
val_c = val_u + 1;
|
||||
if (val_u == 0x01) {
|
||||
CBOR_FIELD_GET_UINT(get, 1);
|
||||
@@ -66,31 +69,40 @@ int cbor_large_blobs(const uint8_t *data, size_t len) {
|
||||
}
|
||||
CBOR_PARSE_MAP_END(map, 1);
|
||||
|
||||
if (offset == UINT64_MAX)
|
||||
if (offset == UINT64_MAX) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
if (get == 0 && set.present == false)
|
||||
}
|
||||
if (get == 0 && set.present == false) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
if (get != 0 && set.present == true)
|
||||
}
|
||||
if (get != 0 && set.present == true) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
|
||||
if (get > 0) {
|
||||
if (length != 0)
|
||||
if (length != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
if (length > MAX_FRAGMENT_LENGTH)
|
||||
}
|
||||
if (length > MAX_FRAGMENT_LENGTH) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_LEN);
|
||||
if (offset > file_get_size(ef_largeblob))
|
||||
}
|
||||
if (offset > file_get_size(ef_largeblob)) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, file_get_data(ef_largeblob)+offset, MIN(get, file_get_size(ef_largeblob)-offset)));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, file_get_data(ef_largeblob) + offset,
|
||||
MIN(get, file_get_size(ef_largeblob) - offset)));
|
||||
}
|
||||
else {
|
||||
if (set.len > MAX_FRAGMENT_LENGTH)
|
||||
if (set.len > MAX_FRAGMENT_LENGTH) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_LEN);
|
||||
}
|
||||
if (offset == 0) {
|
||||
if (length == 0)
|
||||
if (length == 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
if (length > MAX_LARGE_BLOB_SIZE) {
|
||||
CBOR_ERROR(CTAP2_ERR_LARGE_BLOB_STORAGE_FULL);
|
||||
}
|
||||
@@ -101,38 +113,48 @@ int cbor_large_blobs(const uint8_t *data, size_t len) {
|
||||
expectedNextOffset = 0;
|
||||
}
|
||||
else {
|
||||
if (length != 0)
|
||||
if (length != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
}
|
||||
if (offset != expectedNextOffset)
|
||||
if (offset != expectedNextOffset) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_SEQ);
|
||||
if (pinUvAuthParam.present == false)
|
||||
}
|
||||
if (pinUvAuthParam.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_PUAT_REQUIRED);
|
||||
if (pinUvAuthProtocol == 0)
|
||||
}
|
||||
if (pinUvAuthProtocol == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
uint8_t verify_data[70] = {0};
|
||||
}
|
||||
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;
|
||||
verify_data[36] = offset >> 16;
|
||||
verify_data[37] = offset >> 24;
|
||||
mbedtls_sha256(set.data, set.len, verify_data+38, 0);
|
||||
if (verify(pinUvAuthProtocol, paut.data, verify_data, sizeof(verify_data), pinUvAuthParam.data) != 0)
|
||||
mbedtls_sha256(set.data, set.len, verify_data + 38, 0);
|
||||
if (verify(pinUvAuthProtocol, paut.data, verify_data, sizeof(verify_data),
|
||||
pinUvAuthParam.data) != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (!(paut.permissions & CTAP_PERMISSION_LBW))
|
||||
}
|
||||
if (!(paut.permissions & CTAP_PERMISSION_LBW)) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (offset+set.len > expectedLength)
|
||||
}
|
||||
if (offset + set.len > expectedLength) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
if (offset == 0)
|
||||
}
|
||||
if (offset == 0) {
|
||||
memset(temp_lba, 0, sizeof(temp_lba));
|
||||
memcpy(temp_lba+expectedNextOffset, set.data, set.len);
|
||||
}
|
||||
memcpy(temp_lba + expectedNextOffset, set.data, set.len);
|
||||
expectedNextOffset += set.len;
|
||||
if (expectedNextOffset == expectedLength) {
|
||||
uint8_t sha[32];
|
||||
mbedtls_sha256(temp_lba, expectedLength-16, sha, 0);
|
||||
if (expectedLength > 17 && memcmp(sha, temp_lba+expectedLength-16, 16) != 0)
|
||||
mbedtls_sha256(temp_lba, expectedLength - 16, sha, 0);
|
||||
if (expectedLength > 17 && memcmp(sha, temp_lba + expectedLength - 16, 16) != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_INTEGRITY_FAILURE);
|
||||
}
|
||||
flash_write_data_to_file(ef_largeblob, temp_lba, expectedLength);
|
||||
low_flash_available();
|
||||
}
|
||||
@@ -140,11 +162,12 @@ int cbor_large_blobs(const uint8_t *data, size_t len) {
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
|
||||
err:
|
||||
err:
|
||||
CBOR_FREE_BYTE_STRING(pinUvAuthParam);
|
||||
CBOR_FREE_BYTE_STRING(set);
|
||||
if (error != CborNoError)
|
||||
if (error != CborNoError) {
|
||||
return -CTAP2_ERR_INVALID_CBOR;
|
||||
}
|
||||
res_APDU_size = cbor_encoder_get_buffer_size(&encoder, res_APDU + 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -15,34 +15,34 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "ctap2_cbor.h"
|
||||
#include "cbor_make_credential.h"
|
||||
#include "ctap2_cbor.h"
|
||||
#include "hid/ctap_hid.h"
|
||||
#include "fido.h"
|
||||
#include "ctap.h"
|
||||
#include "files.h"
|
||||
#include "random.h"
|
||||
#include "hsm.h"
|
||||
#include <math.h>
|
||||
#include "apdu.h"
|
||||
#include "credential.h"
|
||||
#include "mbedtls/sha256.h"
|
||||
#include "random.h"
|
||||
#include "hsm.h"
|
||||
|
||||
int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
CborParser parser;
|
||||
CborValue map;
|
||||
CborError error = CborNoError;
|
||||
CborByteString clientDataHash = {0}, pinUvAuthParam = {0};
|
||||
PublicKeyCredentialRpEntity rp = {0};
|
||||
PublicKeyCredentialUserEntity user = {0};
|
||||
PublicKeyCredentialParameters pubKeyCredParams[MAX_CREDENTIAL_COUNT_IN_LIST] = {0};
|
||||
CborByteString clientDataHash = { 0 }, pinUvAuthParam = { 0 };
|
||||
PublicKeyCredentialRpEntity rp = { 0 };
|
||||
PublicKeyCredentialUserEntity user = { 0 };
|
||||
PublicKeyCredentialParameters pubKeyCredParams[MAX_CREDENTIAL_COUNT_IN_LIST] = { 0 };
|
||||
size_t pubKeyCredParams_len = 0;
|
||||
PublicKeyCredentialDescriptor excludeList[MAX_CREDENTIAL_COUNT_IN_LIST] = {0};
|
||||
PublicKeyCredentialDescriptor excludeList[MAX_CREDENTIAL_COUNT_IN_LIST] = { 0 };
|
||||
size_t excludeList_len = 0;
|
||||
CredOptions options = {0};
|
||||
CredOptions options = { 0 };
|
||||
uint64_t pinUvAuthProtocol = 0, enterpriseAttestation = 0;
|
||||
uint8_t *aut_data = NULL;
|
||||
size_t resp_size = 0;
|
||||
CredExtensions extensions = {0};
|
||||
CredExtensions extensions = { 0 };
|
||||
//options.present = true;
|
||||
//options.up = ptrue;
|
||||
//options.uv = pfalse;
|
||||
@@ -50,13 +50,16 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
|
||||
CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map));
|
||||
uint64_t val_c = 1;
|
||||
CBOR_PARSE_MAP_START(map, 1) {
|
||||
CBOR_PARSE_MAP_START(map, 1)
|
||||
{
|
||||
uint64_t val_u = 0;
|
||||
CBOR_FIELD_GET_UINT(val_u, 1);
|
||||
if (val_c <= 4 && val_c != val_u)
|
||||
if (val_c <= 4 && val_c != val_u) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (val_u < val_c)
|
||||
}
|
||||
if (val_u < val_c) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
|
||||
}
|
||||
val_c = val_u + 1;
|
||||
if (val_u == 0x01) { // clientDataHash
|
||||
CBOR_FIELD_GET_BYTES(clientDataHash, 1);
|
||||
@@ -94,7 +97,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
}
|
||||
else if (val_u == 0x05) { // excludeList
|
||||
CBOR_PARSE_ARRAY_START(_f1, 2) {
|
||||
PublicKeyCredentialDescriptor *pc = &excludeList[excludeList_len];
|
||||
PublicKeyCredentialDescriptor *pc = &excludeList[excludeList_len];
|
||||
CBOR_PARSE_MAP_START(_f2, 3) {
|
||||
CBOR_FIELD_GET_KEY_TEXT(3);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BYTES(3, "id", pc->id);
|
||||
@@ -150,49 +153,66 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
|
||||
uint8_t flags = FIDO2_AUT_FLAG_AT;
|
||||
uint8_t rp_id_hash[32];
|
||||
mbedtls_sha256((uint8_t *)rp.id.data, rp.id.len, rp_id_hash, 0);
|
||||
mbedtls_sha256((uint8_t *) rp.id.data, rp.id.len, rp_id_hash, 0);
|
||||
|
||||
int curve = -1, alg = 0;
|
||||
if (pubKeyCredParams_len == 0)
|
||||
if (pubKeyCredParams_len == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
|
||||
for (int i = 0; i < pubKeyCredParams_len; i++) {
|
||||
if (strcmp(pubKeyCredParams[i].type.data, "public-key") != 0)
|
||||
if (pubKeyCredParams[i].type.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
if (strcmp(pubKeyCredParams[i].type.data, "public-key") != 0) {
|
||||
continue;
|
||||
if (pubKeyCredParams[i].alg == FIDO2_ALG_ES256)
|
||||
}
|
||||
if (pubKeyCredParams[i].alg == FIDO2_ALG_ES256) {
|
||||
curve = FIDO2_CURVE_P256;
|
||||
else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES384)
|
||||
}
|
||||
else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES384) {
|
||||
curve = FIDO2_CURVE_P384;
|
||||
else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES512)
|
||||
}
|
||||
else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES512) {
|
||||
curve = FIDO2_CURVE_P521;
|
||||
else if (pubKeyCredParams[i].alg == 0) // no present
|
||||
}
|
||||
else if (pubKeyCredParams[i].alg == 0) { // no present
|
||||
curve = -1;
|
||||
else
|
||||
}
|
||||
else {
|
||||
curve = 0;
|
||||
}
|
||||
if (curve > 0) {
|
||||
alg = pubKeyCredParams[i].alg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (curve == 0)
|
||||
if (curve == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_ALGORITHM);
|
||||
else if (curve == -1)
|
||||
}
|
||||
else if (curve == -1) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
|
||||
if (pinUvAuthParam.present == true) {
|
||||
if (pinUvAuthParam.len == 0 || pinUvAuthParam.data == NULL) {
|
||||
if (check_user_presence() == false)
|
||||
if (check_user_presence() == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_OPERATION_DENIED);
|
||||
if (!file_has_data(ef_pin))
|
||||
}
|
||||
if (!file_has_data(ef_pin)) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_NOT_SET);
|
||||
else
|
||||
}
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (pinUvAuthProtocol == 0)
|
||||
if (pinUvAuthProtocol == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2)
|
||||
}
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (options.present) {
|
||||
@@ -203,7 +223,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_OPTION);
|
||||
}
|
||||
//else if (options.up == NULL) //5.7
|
||||
//rup = ptrue;
|
||||
//rup = ptrue;
|
||||
}
|
||||
if (pinUvAuthParam.present == false && options.uv != ptrue && file_has_data(ef_pin)) { //8.1
|
||||
CBOR_ERROR(CTAP2_ERR_PUAT_REQUIRED);
|
||||
@@ -218,15 +238,23 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
//Unfinished. See 6.1.2.9
|
||||
}
|
||||
if (pinUvAuthParam.present == true) { //11.1
|
||||
int ret = verify(pinUvAuthProtocol, paut.data, clientDataHash.data, clientDataHash.len, pinUvAuthParam.data);
|
||||
if (ret != CborNoError)
|
||||
int ret = verify(pinUvAuthProtocol,
|
||||
paut.data,
|
||||
clientDataHash.data,
|
||||
clientDataHash.len,
|
||||
pinUvAuthParam.data);
|
||||
if (ret != CborNoError) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (!(paut.permissions & CTAP_PERMISSION_MC))
|
||||
}
|
||||
if (!(paut.permissions & CTAP_PERMISSION_MC)) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rp_id_hash, 32) != 0)
|
||||
}
|
||||
if (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rp_id_hash, 32) != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (getUserVerifiedFlagValue() == false)
|
||||
}
|
||||
if (getUserVerifiedFlagValue() == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
flags |= FIDO2_AUT_FLAG_UV;
|
||||
if (paut.has_rp_id == false) {
|
||||
memcpy(paut.rp_id_hash, rp_id_hash, 32);
|
||||
@@ -235,24 +263,32 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
}
|
||||
|
||||
for (int e = 0; e < excludeList_len; e++) { //12.1
|
||||
if (excludeList[e].type.present == false || excludeList[e].id.present == false)
|
||||
if (excludeList[e].type.present == false || excludeList[e].id.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (strcmp(excludeList[e].type.data, "public-key") != 0)
|
||||
}
|
||||
if (strcmp(excludeList[e].type.data, "public-key") != 0) {
|
||||
continue;
|
||||
}
|
||||
Credential ecred;
|
||||
if (credential_load(excludeList[e].id.data, excludeList[e].id.len, rp_id_hash, &ecred) == 0 && (ecred.extensions.credProtect != CRED_PROT_UV_REQUIRED || (flags & FIDO2_AUT_FLAG_UV)))
|
||||
CBOR_ERROR(CTAP2_ERR_CREDENTIAL_EXCLUDED);
|
||||
if (credential_load(excludeList[e].id.data, excludeList[e].id.len, rp_id_hash,
|
||||
&ecred) == 0 &&
|
||||
(ecred.extensions.credProtect != CRED_PROT_UV_REQUIRED ||
|
||||
(flags & FIDO2_AUT_FLAG_UV))) {
|
||||
CBOR_ERROR(CTAP2_ERR_CREDENTIAL_EXCLUDED);
|
||||
}
|
||||
}
|
||||
|
||||
if (extensions.largeBlobKey == pfalse || (extensions.largeBlobKey == ptrue && options.rk != ptrue)) {
|
||||
if (extensions.largeBlobKey == pfalse ||
|
||||
(extensions.largeBlobKey == ptrue && options.rk != ptrue)) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_OPTION);
|
||||
}
|
||||
|
||||
if (options.up == ptrue || options.up == NULL) { //14.1
|
||||
if (pinUvAuthParam.present == true) {
|
||||
if (getUserPresentFlagValue() == false) {
|
||||
if (check_user_presence() == false)
|
||||
if (check_user_presence() == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_OPERATION_DENIED);
|
||||
}
|
||||
}
|
||||
}
|
||||
flags |= FIDO2_AUT_FLAG_UP;
|
||||
@@ -266,21 +302,26 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
uint8_t cred_id[MAX_CRED_ID_LENGTH];
|
||||
size_t cred_id_len = 0;
|
||||
|
||||
CBOR_CHECK(credential_create(&rp.id, &user.id, &user.parent.name, &user.displayName, &options, &extensions, (!ka || ka->use_sign_count == ptrue), alg, curve, cred_id, &cred_id_len));
|
||||
CBOR_CHECK(credential_create(&rp.id, &user.id, &user.parent.name, &user.displayName, &options,
|
||||
&extensions, (!ka || ka->use_sign_count == ptrue), alg, curve,
|
||||
cred_id, &cred_id_len));
|
||||
|
||||
if (getUserVerifiedFlagValue())
|
||||
if (getUserVerifiedFlagValue()) {
|
||||
flags |= FIDO2_AUT_FLAG_UV;
|
||||
}
|
||||
size_t ext_len = 0;
|
||||
uint8_t ext [512];
|
||||
uint8_t ext[512];
|
||||
CborEncoder encoder, mapEncoder, mapEncoder2;
|
||||
if (extensions.present == true) {
|
||||
cbor_encoder_init(&encoder, ext, sizeof(ext), 0);
|
||||
int l = 0;
|
||||
uint8_t minPinLen = 0;
|
||||
if (extensions.hmac_secret != NULL)
|
||||
if (extensions.hmac_secret != NULL) {
|
||||
l++;
|
||||
if (extensions.credProtect != 0)
|
||||
}
|
||||
if (extensions.credProtect != 0) {
|
||||
l++;
|
||||
}
|
||||
if (extensions.minPinLength != NULL) {
|
||||
file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
|
||||
if (file_has_data(ef_minpin)) {
|
||||
@@ -288,19 +329,22 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
for (int o = 2; o < file_get_size(ef_minpin); o += 32) {
|
||||
if (memcmp(minpin_data + o, rp_id_hash, 32) == 0) {
|
||||
minPinLen = minpin_data[0];
|
||||
if (minPinLen > 0)
|
||||
if (minPinLen > 0) {
|
||||
l++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (extensions.credBlob.present == true)
|
||||
if (extensions.credBlob.present == true) {
|
||||
l++;
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, l));
|
||||
if (extensions.credBlob.present == true) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "credBlob"));
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, extensions.credBlob.len < MAX_CREDBLOB_LENGTH));
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder,
|
||||
extensions.credBlob.len < MAX_CREDBLOB_LENGTH));
|
||||
}
|
||||
if (extensions.credProtect != 0) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "credProtect"));
|
||||
@@ -356,7 +400,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
size_t rs = cbor_encoder_get_buffer_size(&encoder, cbor_buf);
|
||||
|
||||
size_t aut_data_len = 32 + 1 + 4 + (16 + 2 + cred_id_len + rs) + ext_len;
|
||||
aut_data = (uint8_t *)calloc(1, aut_data_len + clientDataHash.len);
|
||||
aut_data = (uint8_t *) calloc(1, aut_data_len + clientDataHash.len);
|
||||
uint8_t *pa = aut_data;
|
||||
memcpy(pa, rp_id_hash, 32); pa += 32;
|
||||
*pa++ = flags;
|
||||
@@ -370,14 +414,17 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
memcpy(pa, cred_id, cred_id_len); pa += cred_id_len;
|
||||
memcpy(pa, cbor_buf, rs); pa += rs;
|
||||
memcpy(pa, ext, ext_len); pa += ext_len;
|
||||
if (pa-aut_data != aut_data_len) {
|
||||
if (pa - aut_data != aut_data_len) {
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
}
|
||||
|
||||
memcpy(pa, clientDataHash.data, clientDataHash.len);
|
||||
uint8_t hash[32], sig[MBEDTLS_ECDSA_MAX_LEN];
|
||||
ret = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), aut_data, aut_data_len+clientDataHash.len, hash);
|
||||
ret = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
|
||||
aut_data,
|
||||
aut_data_len + clientDataHash.len,
|
||||
hash);
|
||||
|
||||
bool self_attestation = true;
|
||||
if (enterpriseAttestation == 2 || (ka && ka->use_self_attestation == pfalse)) {
|
||||
@@ -386,7 +433,15 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &ekey, file_get_data(ef_keydev), 32);
|
||||
self_attestation = false;
|
||||
}
|
||||
ret = mbedtls_ecdsa_write_signature(&ekey, MBEDTLS_MD_SHA256, hash, 32, sig, sizeof(sig), &olen, random_gen, NULL);
|
||||
ret = mbedtls_ecdsa_write_signature(&ekey,
|
||||
MBEDTLS_MD_SHA256,
|
||||
hash,
|
||||
32,
|
||||
sig,
|
||||
sizeof(sig),
|
||||
&olen,
|
||||
random_gen,
|
||||
NULL);
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
|
||||
uint8_t largeBlobKey[32];
|
||||
@@ -398,7 +453,9 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
}
|
||||
|
||||
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, extensions.largeBlobKey == ptrue && options.rk == ptrue ? 5 : 4));
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder,
|
||||
extensions.largeBlobKey == ptrue &&
|
||||
options.rk == ptrue ? 5 : 4));
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "packed"));
|
||||
@@ -406,7 +463,8 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, aut_data, aut_data_len));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03));
|
||||
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, self_attestation == false ? 3 : 2));
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2,
|
||||
self_attestation == false ? 3 : 2));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "alg"));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, self_attestation ? -alg : -FIDO2_ALG_ES256));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "sig"));
|
||||
@@ -414,13 +472,16 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
if (self_attestation == false) {
|
||||
CborEncoder arrEncoder;
|
||||
file_t *ef_cert = NULL;
|
||||
if (enterpriseAttestation == 2)
|
||||
if (enterpriseAttestation == 2) {
|
||||
ef_cert = search_by_fid(EF_EE_DEV_EA, NULL, SPECIFY_EF);
|
||||
if (!file_has_data(ef_cert))
|
||||
}
|
||||
if (!file_has_data(ef_cert)) {
|
||||
ef_cert = ef_certdev;
|
||||
}
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "x5c"));
|
||||
CBOR_CHECK(cbor_encoder_create_array(&mapEncoder2, &arrEncoder, 1));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&arrEncoder, file_get_data(ef_cert), file_get_size(ef_cert)));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&arrEncoder, file_get_data(ef_cert),
|
||||
file_get_size(ef_cert)));
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder2, &arrEncoder));
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
|
||||
@@ -437,11 +498,15 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
resp_size = cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1);
|
||||
|
||||
if (options.rk == ptrue) {
|
||||
if (credential_store(cred_id, cred_id_len, rp_id_hash) != 0)
|
||||
if (credential_store(cred_id, cred_id_len, rp_id_hash) != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_KEY_STORE_FULL);
|
||||
}
|
||||
}
|
||||
err:
|
||||
CBOR_FREE_BYTE_STRING(clientDataHash);
|
||||
ctr++;
|
||||
flash_write_data_to_file(ef_counter, (uint8_t *) &ctr, sizeof(ctr));
|
||||
low_flash_available();
|
||||
err:
|
||||
CBOR_FREE_BYTE_STRING(clientDataHash);
|
||||
CBOR_FREE_BYTE_STRING(pinUvAuthParam);
|
||||
CBOR_FREE_BYTE_STRING(rp.id);
|
||||
CBOR_FREE_BYTE_STRING(rp.parent.name);
|
||||
@@ -459,14 +524,15 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
CBOR_FREE_BYTE_STRING(excludeList[m].transports[n]);
|
||||
}
|
||||
}
|
||||
if (aut_data)
|
||||
if (aut_data) {
|
||||
free(aut_data);
|
||||
}
|
||||
if (error != CborNoError) {
|
||||
if (error == CborErrorImproperValue)
|
||||
if (error == CborErrorImproperValue) {
|
||||
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
res_APDU_size = resp_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,27 +18,18 @@
|
||||
#ifndef _CBOR_MAKE_CREDENTIAL_H_
|
||||
#define _CBOR_MAKE_CREDENTIAL_H_
|
||||
|
||||
#include "common.h"
|
||||
#include "mbedtls/chachapoly.h"
|
||||
#include <stdlib.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "ctap2_cbor.h"
|
||||
#include "random.h"
|
||||
#include "mbedtls/sha256.h"
|
||||
|
||||
typedef struct PublicKeyCredentialEntity
|
||||
{
|
||||
typedef struct PublicKeyCredentialEntity {
|
||||
CborCharString name;
|
||||
} PublicKeyCredentialEntity;
|
||||
|
||||
typedef struct PublicKeyCredentialRpEntity
|
||||
{
|
||||
typedef struct PublicKeyCredentialRpEntity {
|
||||
PublicKeyCredentialEntity parent;
|
||||
CborCharString id;
|
||||
} PublicKeyCredentialRpEntity;
|
||||
|
||||
typedef struct PublicKeyCredentialUserEntity
|
||||
{
|
||||
typedef struct PublicKeyCredentialUserEntity {
|
||||
PublicKeyCredentialEntity parent;
|
||||
CborByteString id;
|
||||
CborCharString displayName;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/*
|
||||
* This file is part of the Pico FIDO distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
@@ -16,23 +15,27 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ctap2_cbor.h"
|
||||
#include "file.h"
|
||||
#include "fido.h"
|
||||
#include "apdu.h"
|
||||
#include "ctap.h"
|
||||
#ifndef ENABLE_EMULATION
|
||||
#include "bsp/board.h"
|
||||
#endif
|
||||
|
||||
extern void scan_all();
|
||||
|
||||
int cbor_reset() {
|
||||
#if defined(ENABLE_POWER_ON_RESET) && ENABLE_POWER_ON_RESET==1
|
||||
if (board_millis() > 10000)
|
||||
#ifndef ENABLE_EMULATION
|
||||
#if defined(ENABLE_POWER_ON_RESET) && ENABLE_POWER_ON_RESET == 1
|
||||
if (board_millis() > 10000) {
|
||||
return CTAP2_ERR_NOT_ALLOWED;
|
||||
}
|
||||
#endif
|
||||
if (wait_button_pressed() == true)
|
||||
if (wait_button_pressed() == true) {
|
||||
return CTAP2_ERR_USER_ACTION_TIMEOUT;
|
||||
}
|
||||
#endif
|
||||
initialize_flash(true);
|
||||
init_fido(true);
|
||||
init_fido();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/*
|
||||
* This file is part of the Pico FIDO distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
@@ -16,13 +15,12 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ctap2_cbor.h"
|
||||
#include "fido.h"
|
||||
#include "ctap.h"
|
||||
#include "bsp/board.h"
|
||||
|
||||
int cbor_selection() {
|
||||
if (wait_button_pressed() == true)
|
||||
if (wait_button_pressed() == true) {
|
||||
return CTAP2_ERR_USER_ACTION_TIMEOUT;
|
||||
}
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "ctap2_cbor.h"
|
||||
#include "fido.h"
|
||||
#include "ctap.h"
|
||||
#include "hid/ctap_hid.h"
|
||||
#include "files.h"
|
||||
#include "apdu.h"
|
||||
#include "hsm.h"
|
||||
@@ -31,13 +31,20 @@
|
||||
extern uint8_t keydev_dec[32];
|
||||
extern bool has_keydev_dec;
|
||||
|
||||
mse_t mse = {.init = false};
|
||||
mse_t mse = { .init = false };
|
||||
|
||||
int mse_decrypt_ct(uint8_t *data, size_t len) {
|
||||
mbedtls_chachapoly_context chatx;
|
||||
mbedtls_chachapoly_init(&chatx);
|
||||
mbedtls_chachapoly_setkey(&chatx, mse.key_enc + 12);
|
||||
int ret = mbedtls_chachapoly_auth_decrypt(&chatx, len - 16, mse.key_enc, mse.Qpt, 65, data + len - 16, data, data);
|
||||
int ret = mbedtls_chachapoly_auth_decrypt(&chatx,
|
||||
len - 16,
|
||||
mse.key_enc,
|
||||
mse.Qpt,
|
||||
65,
|
||||
data + len - 16,
|
||||
data,
|
||||
data);
|
||||
mbedtls_chachapoly_free(&chatx);
|
||||
return ret;
|
||||
}
|
||||
@@ -46,7 +53,7 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
CborParser parser;
|
||||
CborValue map;
|
||||
CborError error = CborNoError;
|
||||
CborByteString pinUvAuthParam = {0}, vendorParam = {0}, kax = {0}, kay = {0};
|
||||
CborByteString pinUvAuthParam = { 0 }, vendorParam = { 0 }, kax = { 0 }, kay = { 0 };
|
||||
size_t resp_size = 0;
|
||||
uint64_t vendorCmd = 0, pinUvAuthProtocol = 0;
|
||||
int64_t kty = 0, alg = 0, crv = 0;
|
||||
@@ -54,27 +61,32 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
|
||||
CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map));
|
||||
uint64_t val_c = 1;
|
||||
CBOR_PARSE_MAP_START(map, 1) {
|
||||
CBOR_PARSE_MAP_START(map, 1)
|
||||
{
|
||||
uint64_t val_u = 0;
|
||||
CBOR_FIELD_GET_UINT(val_u, 1);
|
||||
if (val_c <= 1 && val_c != val_u)
|
||||
if (val_c <= 1 && val_c != val_u) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (val_u < val_c)
|
||||
}
|
||||
if (val_u < val_c) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
|
||||
}
|
||||
val_c = val_u + 1;
|
||||
if (val_u == 0x01) {
|
||||
CBOR_FIELD_GET_UINT(vendorCmd, 1);
|
||||
}
|
||||
else if (val_u == 0x02) {
|
||||
uint64_t subpara = 0;
|
||||
CBOR_PARSE_MAP_START(_f1, 2) {
|
||||
CBOR_PARSE_MAP_START(_f1, 2)
|
||||
{
|
||||
CBOR_FIELD_GET_UINT(subpara, 2);
|
||||
if (subpara == 0x01) {
|
||||
CBOR_FIELD_GET_BYTES(vendorParam, 2);
|
||||
}
|
||||
else if (subpara == 0x02) {
|
||||
int64_t key = 0;
|
||||
CBOR_PARSE_MAP_START(_f2, 3) {
|
||||
CBOR_PARSE_MAP_START(_f2, 3)
|
||||
{
|
||||
CBOR_FIELD_GET_INT(key, 3);
|
||||
if (key == 1) {
|
||||
CBOR_FIELD_GET_INT(kty, 3);
|
||||
@@ -91,13 +103,15 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
else if (key == -3) {
|
||||
CBOR_FIELD_GET_BYTES(kay, 3);
|
||||
}
|
||||
else
|
||||
else {
|
||||
CBOR_ADVANCE(3);
|
||||
}
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f2, 3);
|
||||
}
|
||||
else
|
||||
else {
|
||||
CBOR_ADVANCE(2);
|
||||
}
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f1, 2);
|
||||
}
|
||||
@@ -114,17 +128,20 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
|
||||
if (cmd == CTAP_VENDOR_BACKUP) {
|
||||
if (vendorCmd == 0x01) {
|
||||
if (has_keydev_dec == false)
|
||||
if (has_keydev_dec == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
|
||||
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, file_get_data(ef_keydev_enc), file_get_size(ef_keydev_enc)));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, file_get_data(ef_keydev_enc),
|
||||
file_get_size(ef_keydev_enc)));
|
||||
}
|
||||
else if (vendorCmd == 0x02) {
|
||||
if (vendorParam.present == false)
|
||||
if (vendorParam.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
uint8_t zeros[32];
|
||||
memset(zeros, 0, sizeof(zeros));
|
||||
flash_write_data_to_file(ef_keydev_enc, vendorParam.data, vendorParam.len);
|
||||
@@ -139,13 +156,18 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
}
|
||||
else if (cmd == CTAP_VENDOR_MSE) {
|
||||
if (vendorCmd == 0x01) { // KeyAgreement
|
||||
if (kax.present == false || kay.present == false || alg == 0)
|
||||
if (kax.present == false || kay.present == false || alg == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
|
||||
mbedtls_ecdh_context hkey;
|
||||
mbedtls_ecdh_init(&hkey);
|
||||
mbedtls_ecdh_setup(&hkey, MBEDTLS_ECP_DP_SECP256R1);
|
||||
int ret = mbedtls_ecdh_gen_public(&hkey.ctx.mbed_ecdh.grp, &hkey.ctx.mbed_ecdh.d, &hkey.ctx.mbed_ecdh.Q, random_gen, NULL);
|
||||
int ret = mbedtls_ecdh_gen_public(&hkey.ctx.mbed_ecdh.grp,
|
||||
&hkey.ctx.mbed_ecdh.d,
|
||||
&hkey.ctx.mbed_ecdh.Q,
|
||||
random_gen,
|
||||
NULL);
|
||||
mbedtls_mpi_lset(&hkey.ctx.mbed_ecdh.Qp.Z, 1);
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
@@ -161,19 +183,37 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
|
||||
uint8_t buf[MBEDTLS_ECP_MAX_BYTES];
|
||||
size_t olen = 0;
|
||||
ret = mbedtls_ecp_point_write_binary(&hkey.ctx.mbed_ecdh.grp, &hkey.ctx.mbed_ecdh.Qp, MBEDTLS_ECP_PF_UNCOMPRESSED, &olen, mse.Qpt, sizeof(mse.Qpt));
|
||||
ret = mbedtls_ecp_point_write_binary(&hkey.ctx.mbed_ecdh.grp,
|
||||
&hkey.ctx.mbed_ecdh.Qp,
|
||||
MBEDTLS_ECP_PF_UNCOMPRESSED,
|
||||
&olen,
|
||||
mse.Qpt,
|
||||
sizeof(mse.Qpt));
|
||||
if (ret != 0) {
|
||||
mbedtls_ecdh_free(&hkey);
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
ret = mbedtls_ecdh_calc_secret(&hkey, &olen, buf, MBEDTLS_ECP_MAX_BYTES, random_gen, NULL);
|
||||
ret = mbedtls_ecdh_calc_secret(&hkey,
|
||||
&olen,
|
||||
buf,
|
||||
MBEDTLS_ECP_MAX_BYTES,
|
||||
random_gen,
|
||||
NULL);
|
||||
if (ret != 0) {
|
||||
mbedtls_ecdh_free(&hkey);
|
||||
mbedtls_platform_zeroize(buf, sizeof(buf));
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
ret = mbedtls_hkdf(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), NULL, 0, buf, olen, mse.Qpt, sizeof(mse.Qpt), mse.key_enc, sizeof(mse.key_enc));
|
||||
ret = mbedtls_hkdf(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
|
||||
NULL,
|
||||
0,
|
||||
buf,
|
||||
olen,
|
||||
mse.Qpt,
|
||||
sizeof(mse.Qpt),
|
||||
mse.key_enc,
|
||||
sizeof(mse.key_enc));
|
||||
mbedtls_platform_zeroize(buf, sizeof(buf));
|
||||
if (ret != 0) {
|
||||
mbedtls_ecdh_free(&hkey);
|
||||
@@ -203,8 +243,9 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
}
|
||||
}
|
||||
else if (cmd == CTAP_VENDOR_UNLOCK) {
|
||||
if (mse.init == false)
|
||||
if (mse.init == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
}
|
||||
|
||||
mbedtls_chachapoly_context chatx;
|
||||
int ret = mse_decrypt_ct(vendorParam.data, vendorParam.len);
|
||||
@@ -212,16 +253,24 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
if (!file_has_data(ef_keydev_enc))
|
||||
if (!file_has_data(ef_keydev_enc)) {
|
||||
CBOR_ERROR(CTAP2_ERR_INTEGRITY_FAILURE);
|
||||
}
|
||||
|
||||
uint8_t *keyenc = file_get_data(ef_keydev_enc);
|
||||
size_t keyenc_len = file_get_size(ef_keydev_enc);
|
||||
mbedtls_chachapoly_init(&chatx);
|
||||
mbedtls_chachapoly_setkey(&chatx, vendorParam.data);
|
||||
ret = mbedtls_chachapoly_auth_decrypt(&chatx, sizeof(keydev_dec), keyenc, NULL, 0, keyenc + keyenc_len - 16, keyenc + 12, keydev_dec);
|
||||
ret = mbedtls_chachapoly_auth_decrypt(&chatx,
|
||||
sizeof(keydev_dec),
|
||||
keyenc,
|
||||
NULL,
|
||||
0,
|
||||
keyenc + keyenc_len - 16,
|
||||
keyenc + 12,
|
||||
keydev_dec);
|
||||
mbedtls_chachapoly_free(&chatx);
|
||||
if (ret != 0){
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
has_keydev_dec = true;
|
||||
@@ -232,7 +281,10 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
uint8_t buffer[1024];
|
||||
mbedtls_ecdsa_context ekey;
|
||||
mbedtls_ecdsa_init(&ekey);
|
||||
int ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &ekey, file_get_data(ef_keydev), file_get_size(ef_keydev));
|
||||
int ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1,
|
||||
&ekey,
|
||||
file_get_data(ef_keydev),
|
||||
file_get_size(ef_keydev));
|
||||
if (ret != 0) {
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
CBOR_ERROR(CTAP2_ERR_PROCESSING);
|
||||
@@ -242,19 +294,40 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
CBOR_ERROR(CTAP2_ERR_PROCESSING);
|
||||
}
|
||||
#ifndef ENABLE_EMULATION
|
||||
pico_unique_board_id_t rpiid;
|
||||
pico_get_unique_board_id(&rpiid);
|
||||
#else
|
||||
struct {
|
||||
uint8_t id[8];
|
||||
} rpiid = { 0 };
|
||||
#endif
|
||||
mbedtls_x509write_csr ctx;
|
||||
mbedtls_x509write_csr_init(&ctx);
|
||||
snprintf((char *)buffer, sizeof(buffer), "C=ES,O=Pico Keys,OU=Authenticator Attestation,CN=Pico Fido EE Serial %llu", ((uint64_t)rpiid.id[0] << 56) | ((uint64_t)rpiid.id[1] << 48) | ((uint64_t)rpiid.id[2] << 40) | ((uint64_t)rpiid.id[3] << 32) | (rpiid.id[4] << 24) | (rpiid.id[5] << 16) | (rpiid.id[6] << 8) | rpiid.id[7]);
|
||||
mbedtls_x509write_csr_set_subject_name(&ctx, (char *)buffer);
|
||||
snprintf((char *) buffer,
|
||||
sizeof(buffer),
|
||||
"C=ES,O=Pico Keys,OU=Authenticator Attestation,CN=Pico Fido EE Serial %02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
rpiid.id[0],
|
||||
rpiid.id[1],
|
||||
rpiid.id[2],
|
||||
rpiid.id[3],
|
||||
rpiid.id[4],
|
||||
rpiid.id[5],
|
||||
rpiid.id[6],
|
||||
rpiid.id[7]);
|
||||
mbedtls_x509write_csr_set_subject_name(&ctx, (char *) buffer);
|
||||
mbedtls_pk_context key;
|
||||
mbedtls_pk_init(&key);
|
||||
mbedtls_pk_setup(&key, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY));
|
||||
key.pk_ctx = &ekey;
|
||||
mbedtls_x509write_csr_set_key(&ctx, &key);
|
||||
mbedtls_x509write_csr_set_md_alg(&ctx, MBEDTLS_MD_SHA256);
|
||||
mbedtls_x509write_csr_set_extension(&ctx, "\x2B\x06\x01\x04\x01\x82\xE5\x1C\x01\x01\x04", 0xB, 0, aaguid, sizeof(aaguid));
|
||||
mbedtls_x509write_csr_set_extension(&ctx,
|
||||
"\x2B\x06\x01\x04\x01\x82\xE5\x1C\x01\x01\x04",
|
||||
0xB,
|
||||
0,
|
||||
aaguid,
|
||||
sizeof(aaguid));
|
||||
ret = mbedtls_x509write_csr_der(&ctx, buffer, sizeof(buffer), random_gen, NULL);
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
if (ret <= 0) {
|
||||
@@ -266,27 +339,31 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, buffer + sizeof(buffer) - ret, ret));
|
||||
}
|
||||
else if (vendorCmd == 0x02) {
|
||||
if (vendorParam.present == false)
|
||||
if (vendorParam.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
file_t *ef_ee_ea = search_by_fid(EF_EE_DEV_EA, NULL, SPECIFY_EF);
|
||||
if (ef_ee_ea)
|
||||
if (ef_ee_ea) {
|
||||
flash_write_data_to_file(ef_ee_ea, vendorParam.data, vendorParam.len);
|
||||
}
|
||||
low_flash_available();
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
else
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION);
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
resp_size = cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1);
|
||||
|
||||
err:
|
||||
err:
|
||||
CBOR_FREE_BYTE_STRING(pinUvAuthParam);
|
||||
CBOR_FREE_BYTE_STRING(vendorParam);
|
||||
|
||||
if (error != CborNoError) {
|
||||
if (error == CborErrorImproperValue)
|
||||
if (error == CborErrorImproperValue) {
|
||||
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
res_APDU_size = resp_size;
|
||||
@@ -294,9 +371,11 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
}
|
||||
|
||||
int cbor_vendor(const uint8_t *data, size_t len) {
|
||||
if (len == 0)
|
||||
if (len == 0) {
|
||||
return CTAP1_ERR_INVALID_LEN;
|
||||
if (data[0] >= CTAP_VENDOR_BACKUP)
|
||||
}
|
||||
if (data[0] >= CTAP_VENDOR_BACKUP) {
|
||||
return cbor_vendor_generic(data[0], data + 1, len - 1);
|
||||
}
|
||||
return CTAP2_ERR_INVALID_CBOR;
|
||||
}
|
||||
|
||||
@@ -19,27 +19,29 @@
|
||||
#include "hsm.h"
|
||||
#include "apdu.h"
|
||||
#include "ctap.h"
|
||||
#include "mbedtls/ecdsa.h"
|
||||
#include "random.h"
|
||||
#include "files.h"
|
||||
#include "credential.h"
|
||||
|
||||
int cmd_authenticate() {
|
||||
CTAP_AUTHENTICATE_REQ *req = (CTAP_AUTHENTICATE_REQ *)apdu.data;
|
||||
CTAP_AUTHENTICATE_RESP *resp = (CTAP_AUTHENTICATE_RESP *)res_APDU;
|
||||
CTAP_AUTHENTICATE_REQ *req = (CTAP_AUTHENTICATE_REQ *) apdu.data;
|
||||
CTAP_AUTHENTICATE_RESP *resp = (CTAP_AUTHENTICATE_RESP *) res_APDU;
|
||||
//if (scan_files(true) != CCID_OK)
|
||||
// return SW_EXEC_ERROR();
|
||||
if (apdu.nc < CTAP_CHAL_SIZE+CTAP_APPID_SIZE+1+1)
|
||||
if (apdu.nc < CTAP_CHAL_SIZE + CTAP_APPID_SIZE + 1 + 1) {
|
||||
return SW_WRONG_DATA();
|
||||
if (req->keyHandleLen < KEY_HANDLE_LEN)
|
||||
}
|
||||
if (req->keyHandleLen < KEY_HANDLE_LEN) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
if (P1(apdu) == CTAP_AUTH_ENFORCE && wait_button_pressed() == true)
|
||||
}
|
||||
if (P1(apdu) == CTAP_AUTH_ENFORCE && wait_button_pressed() == true) {
|
||||
return SW_CONDITIONS_NOT_SATISFIED();
|
||||
}
|
||||
|
||||
mbedtls_ecdsa_context key;
|
||||
mbedtls_ecdsa_init(&key);
|
||||
int ret = 0;
|
||||
uint8_t *tmp_kh = (uint8_t *)calloc(1, req->keyHandleLen);
|
||||
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) {
|
||||
ret = fido_load_key(FIDO2_CURVE_P256, req->keyHandle, &key);
|
||||
@@ -69,23 +71,33 @@ int cmd_authenticate() {
|
||||
resp->ctr[3] = ctr & 0xff;
|
||||
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));
|
||||
memcpy(sig_base + CTAP_APPID_SIZE, &resp->flags, sizeof(uint8_t));
|
||||
memcpy(sig_base + CTAP_APPID_SIZE + 1, resp->ctr, 4);
|
||||
memcpy(sig_base + CTAP_APPID_SIZE + 1 + 4, req->chal, CTAP_CHAL_SIZE);
|
||||
ret = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), sig_base, sizeof(sig_base), hash);
|
||||
ret =
|
||||
mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), sig_base, sizeof(sig_base), hash);
|
||||
if (ret != 0) {
|
||||
mbedtls_ecdsa_free(&key);
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
size_t olen = 0;
|
||||
ret = mbedtls_ecdsa_write_signature(&key, MBEDTLS_MD_SHA256, hash, 32, (uint8_t *)resp->sig, CTAP_MAX_EC_SIG_SIZE, &olen, random_gen, NULL);
|
||||
ret = mbedtls_ecdsa_write_signature(&key,
|
||||
MBEDTLS_MD_SHA256,
|
||||
hash,
|
||||
32,
|
||||
(uint8_t *) resp->sig,
|
||||
CTAP_MAX_EC_SIG_SIZE,
|
||||
&olen,
|
||||
random_gen,
|
||||
NULL);
|
||||
mbedtls_ecdsa_free(&key);
|
||||
if (ret != 0)
|
||||
if (ret != 0) {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
res_APDU_size = 1 + 4 + olen;
|
||||
|
||||
ctr++;
|
||||
flash_write_data_to_file(ef_counter, (uint8_t *)&ctr, sizeof(ctr));
|
||||
flash_write_data_to_file(ef_counter, (uint8_t *) &ctr, sizeof(ctr));
|
||||
low_flash_available();
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
@@ -19,27 +19,36 @@
|
||||
#include "hsm.h"
|
||||
#include "apdu.h"
|
||||
#include "ctap.h"
|
||||
#include "mbedtls/ecdsa.h"
|
||||
#include "random.h"
|
||||
#include "files.h"
|
||||
#include "hid/ctap_hid.h"
|
||||
|
||||
const uint8_t *bogus_firefox = (const uint8_t *)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
|
||||
const uint8_t *bogus_chrome = (const uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||
const uint8_t *bogus_firefox =
|
||||
(const uint8_t *)
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
|
||||
const uint8_t *bogus_chrome = (const uint8_t *) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||
|
||||
extern int ctap_error(uint8_t error);
|
||||
int cmd_register() {
|
||||
CTAP_REGISTER_REQ *req = (CTAP_REGISTER_REQ *)apdu.data;
|
||||
CTAP_REGISTER_RESP *resp = (CTAP_REGISTER_RESP *)res_APDU;
|
||||
CTAP_REGISTER_REQ *req = (CTAP_REGISTER_REQ *) apdu.data;
|
||||
CTAP_REGISTER_RESP *resp = (CTAP_REGISTER_RESP *) res_APDU;
|
||||
resp->registerId = CTAP_REGISTER_ID;
|
||||
resp->keyHandleLen = KEY_HANDLE_LEN;
|
||||
//if (scan_files(true) != CCID_OK)
|
||||
// return SW_EXEC_ERROR();
|
||||
if (apdu.nc != CTAP_APPID_SIZE + CTAP_CHAL_SIZE)
|
||||
if (apdu.nc != CTAP_APPID_SIZE + CTAP_CHAL_SIZE) {
|
||||
return SW_WRONG_LENGTH();
|
||||
if (wait_button_pressed() == true)
|
||||
}
|
||||
if (wait_button_pressed() == true) {
|
||||
return SW_CONDITIONS_NOT_SATISFIED();
|
||||
if (memcmp(req->appId, bogus_firefox, CTAP_APPID_SIZE) == 0 || memcmp(req->appId, bogus_chrome, CTAP_APPID_SIZE) == 0)
|
||||
return ctap_error(CTAP1_ERR_CHANNEL_BUSY);
|
||||
}
|
||||
if (memcmp(req->appId, bogus_firefox,
|
||||
CTAP_APPID_SIZE) == 0 || memcmp(req->appId, bogus_chrome, CTAP_APPID_SIZE) == 0)
|
||||
#ifndef ENABLE_EMULATION
|
||||
{ return ctap_error(CTAP1_ERR_CHANNEL_BUSY); }
|
||||
#else
|
||||
{ return SW_DATA_INVALID(); }
|
||||
#endif
|
||||
mbedtls_ecdsa_context key;
|
||||
mbedtls_ecdsa_init(&key);
|
||||
int ret = derive_key(req->appId, true, resp->keyHandleCertSig, MBEDTLS_ECP_DP_SECP256R1, &key);
|
||||
@@ -48,32 +57,56 @@ int cmd_register() {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
size_t olen = 0;
|
||||
ret = mbedtls_ecp_point_write_binary(&key.grp, &key.Q, MBEDTLS_ECP_PF_UNCOMPRESSED, &olen, (uint8_t *)&resp->pubKey, CTAP_EC_POINT_SIZE);
|
||||
ret =
|
||||
mbedtls_ecp_point_write_binary(&key.grp,
|
||||
&key.Q,
|
||||
MBEDTLS_ECP_PF_UNCOMPRESSED,
|
||||
&olen,
|
||||
(uint8_t *) &resp->pubKey,
|
||||
CTAP_EC_POINT_SIZE);
|
||||
mbedtls_ecdsa_free(&key);
|
||||
if (ret != 0) {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
size_t ef_certdev_size = file_get_size(ef_certdev);
|
||||
memcpy(resp->keyHandleCertSig + KEY_HANDLE_LEN, file_get_data(ef_certdev), ef_certdev_size);
|
||||
uint8_t hash[32], sign_base[1 + CTAP_APPID_SIZE + CTAP_CHAL_SIZE + KEY_HANDLE_LEN + CTAP_EC_POINT_SIZE];
|
||||
uint8_t hash[32],
|
||||
sign_base[1 + CTAP_APPID_SIZE + CTAP_CHAL_SIZE + KEY_HANDLE_LEN + CTAP_EC_POINT_SIZE];
|
||||
sign_base[0] = CTAP_REGISTER_HASH_ID;
|
||||
memcpy(sign_base + 1, req->appId, CTAP_APPID_SIZE);
|
||||
memcpy(sign_base + 1 + CTAP_APPID_SIZE, req->chal, CTAP_CHAL_SIZE);
|
||||
memcpy(sign_base + 1 + CTAP_APPID_SIZE + CTAP_CHAL_SIZE, resp->keyHandleCertSig, KEY_HANDLE_LEN);
|
||||
memcpy(sign_base + 1 + CTAP_APPID_SIZE + CTAP_CHAL_SIZE + KEY_HANDLE_LEN, (uint8_t *)&resp->pubKey, CTAP_EC_POINT_SIZE);
|
||||
ret = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), sign_base, sizeof(sign_base), hash);
|
||||
if (ret != 0)
|
||||
memcpy(sign_base + 1 + CTAP_APPID_SIZE + CTAP_CHAL_SIZE, resp->keyHandleCertSig,
|
||||
KEY_HANDLE_LEN);
|
||||
memcpy(sign_base + 1 + CTAP_APPID_SIZE + CTAP_CHAL_SIZE + KEY_HANDLE_LEN,
|
||||
(uint8_t *) &resp->pubKey,
|
||||
CTAP_EC_POINT_SIZE);
|
||||
ret = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
|
||||
sign_base,
|
||||
sizeof(sign_base),
|
||||
hash);
|
||||
if (ret != 0) {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
mbedtls_ecdsa_init(&key);
|
||||
ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &key, file_get_data(ef_keydev), 32);
|
||||
if (ret != CCID_OK) {
|
||||
mbedtls_ecdsa_free(&key);
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
ret = mbedtls_ecdsa_write_signature(&key, MBEDTLS_MD_SHA256, hash, 32, (uint8_t *)resp->keyHandleCertSig + KEY_HANDLE_LEN + ef_certdev_size, CTAP_MAX_EC_SIG_SIZE, &olen, random_gen, NULL);
|
||||
ret = mbedtls_ecdsa_write_signature(&key,
|
||||
MBEDTLS_MD_SHA256,
|
||||
hash,
|
||||
32,
|
||||
(uint8_t *) resp->keyHandleCertSig + KEY_HANDLE_LEN + ef_certdev_size,
|
||||
CTAP_MAX_EC_SIG_SIZE,
|
||||
&olen,
|
||||
random_gen,
|
||||
NULL);
|
||||
mbedtls_ecdsa_free(&key);
|
||||
if (ret != 0)
|
||||
if (ret != 0) {
|
||||
return SW_EXEC_ERROR();
|
||||
res_APDU_size = sizeof(CTAP_REGISTER_RESP) - sizeof(resp->keyHandleCertSig) + KEY_HANDLE_LEN + ef_certdev_size + olen;
|
||||
}
|
||||
res_APDU_size = sizeof(CTAP_REGISTER_RESP) - sizeof(resp->keyHandleCertSig) + KEY_HANDLE_LEN +
|
||||
ef_certdev_size + olen;
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "apdu.h"
|
||||
#include "hsm.h"
|
||||
|
||||
|
||||
@@ -15,40 +15,60 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "mbedtls/chachapoly.h"
|
||||
#include "mbedtls/sha256.h"
|
||||
#include "credential.h"
|
||||
#ifndef ENABLE_EMULATION
|
||||
#include "bsp/board.h"
|
||||
#endif
|
||||
#include "hid/ctap_hid.h"
|
||||
#include "fido.h"
|
||||
#include "ctap.h"
|
||||
#include "random.h"
|
||||
#include "files.h"
|
||||
#include "file.h"
|
||||
#include "hsm.h"
|
||||
|
||||
int credential_derive_chacha_key(uint8_t *outk);
|
||||
|
||||
int credential_verify(uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash) {
|
||||
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;
|
||||
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);
|
||||
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);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int credential_create(CborCharString *rpId, CborByteString *userId, CborCharString *userName, CborCharString *userDisplayName, CredOptions *opts, CredExtensions *extensions, bool use_sign_count, int alg, int curve, uint8_t *cred_id, size_t *cred_id_len) {
|
||||
int credential_create(CborCharString *rpId,
|
||||
CborByteString *userId,
|
||||
CborCharString *userName,
|
||||
CborCharString *userDisplayName,
|
||||
CredOptions *opts,
|
||||
CredExtensions *extensions,
|
||||
bool use_sign_count,
|
||||
int alg,
|
||||
int curve,
|
||||
uint8_t *cred_id,
|
||||
size_t *cred_id_len) {
|
||||
CborEncoder encoder, mapEncoder, mapEncoder2;
|
||||
CborError error = CborNoError;
|
||||
uint8_t rp_id_hash[32];
|
||||
mbedtls_sha256((uint8_t *)rpId->data, rpId->len, rp_id_hash, 0);
|
||||
cbor_encoder_init(&encoder, cred_id+4+12, MAX_CRED_ID_LENGTH-(4+12+16), 0);
|
||||
mbedtls_sha256((uint8_t *) rpId->data, rpId->len, rp_id_hash, 0);
|
||||
cbor_encoder_init(&encoder, cred_id + 4 + 12, MAX_CRED_ID_LENGTH - (4 + 12 + 16), 0);
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, CborIndefiniteLength));
|
||||
CBOR_APPEND_KEY_UINT_VAL_STRING(mapEncoder, 0x01, *rpId);
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x02));
|
||||
@@ -60,9 +80,11 @@ int credential_create(CborCharString *rpId, CborByteString *userId, CborCharStri
|
||||
if (extensions->present == true) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x07));
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, CborIndefiniteLength));
|
||||
if (extensions->credBlob.present == true && extensions->credBlob.len < MAX_CREDBLOB_LENGTH) {
|
||||
if (extensions->credBlob.present == true &&
|
||||
extensions->credBlob.len < MAX_CREDBLOB_LENGTH) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "credBlob"));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, extensions->credBlob.data, extensions->credBlob.len));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, extensions->credBlob.data,
|
||||
extensions->credBlob.len));
|
||||
}
|
||||
if (extensions->credProtect != 0) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "credProtect"));
|
||||
@@ -104,7 +126,14 @@ int credential_create(CborCharString *rpId, CborByteString *userId, CborCharStri
|
||||
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);
|
||||
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);
|
||||
mbedtls_chachapoly_free(&chatx);
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
@@ -112,24 +141,29 @@ int credential_create(CborCharString *rpId, CborByteString *userId, CborCharStri
|
||||
memcpy(cred_id, CRED_PROTO, 4);
|
||||
memcpy(cred_id + 4, iv, 12);
|
||||
|
||||
err:
|
||||
err:
|
||||
if (error != CborNoError) {
|
||||
if (error == CborErrorImproperValue)
|
||||
if (error == CborErrorImproperValue) {
|
||||
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int credential_load(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash, Credential *cred) {
|
||||
int credential_load(const uint8_t *cred_id,
|
||||
size_t cred_id_len,
|
||||
const uint8_t *rp_id_hash,
|
||||
Credential *cred) {
|
||||
int ret = 0;
|
||||
CborError error;
|
||||
uint8_t *copy_cred_id = (uint8_t *)calloc(1, cred_id_len);
|
||||
CborError error = CborNoError;
|
||||
uint8_t *copy_cred_id = (uint8_t *) calloc(1, cred_id_len);
|
||||
memcpy(copy_cred_id, cred_id, cred_id_len);
|
||||
ret = credential_verify(copy_cred_id, cred_id_len, rp_id_hash);
|
||||
if (ret != 0) { // U2F?
|
||||
if (cred_id_len != KEY_HANDLE_LEN || verify_key(rp_id_hash, cred_id, NULL) != 0)
|
||||
if (cred_id_len != KEY_HANDLE_LEN || verify_key(rp_id_hash, cred_id, NULL) != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_CREDENTIAL);
|
||||
}
|
||||
}
|
||||
else {
|
||||
CborParser parser;
|
||||
@@ -137,8 +171,10 @@ int credential_load(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *r
|
||||
memset(cred, 0, sizeof(Credential));
|
||||
cred->curve = FIDO2_CURVE_P256;
|
||||
cred->alg = FIDO2_ALG_ES256;
|
||||
CBOR_CHECK(cbor_parser_init(copy_cred_id + 4 + 12, cred_id_len - (4 + 12 + 16), 0, &parser, &map));
|
||||
CBOR_PARSE_MAP_START(map, 1) {
|
||||
CBOR_CHECK(cbor_parser_init(copy_cred_id + 4 + 12, cred_id_len - (4 + 12 + 16), 0, &parser,
|
||||
&map));
|
||||
CBOR_PARSE_MAP_START(map, 1)
|
||||
{
|
||||
uint64_t val_u = 0;
|
||||
CBOR_FIELD_GET_UINT(val_u, 1);
|
||||
if (val_u == 0x01) {
|
||||
@@ -194,15 +230,16 @@ int credential_load(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *r
|
||||
}
|
||||
}
|
||||
cred->id.present = true;
|
||||
cred->id.data = (uint8_t *)calloc(1, cred_id_len);
|
||||
cred->id.data = (uint8_t *) calloc(1, cred_id_len);
|
||||
memcpy(cred->id.data, cred_id, cred_id_len);
|
||||
cred->id.len = cred_id_len;
|
||||
cred->present = true;
|
||||
err:
|
||||
err:
|
||||
free(copy_cred_id);
|
||||
if (error != CborNoError) {
|
||||
if (error == CborErrorImproperValue)
|
||||
if (error == CborErrorImproperValue) {
|
||||
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
return 0;
|
||||
@@ -221,7 +258,7 @@ void credential_free(Credential *cred) {
|
||||
|
||||
int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash) {
|
||||
int sloti = -1;
|
||||
Credential cred = {0};
|
||||
Credential cred = { 0 };
|
||||
int ret = 0;
|
||||
bool new_record = true;
|
||||
ret = credential_load(cred_id, cred_id_len, rp_id_hash, &cred);
|
||||
@@ -231,20 +268,23 @@ int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *
|
||||
}
|
||||
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
|
||||
file_t *ef = search_dynamic_file(EF_CRED + i);
|
||||
Credential rcred = {0};
|
||||
Credential rcred = { 0 };
|
||||
if (!file_has_data(ef)) {
|
||||
if (sloti == -1)
|
||||
if (sloti == -1) {
|
||||
sloti = i;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (memcmp(file_get_data(ef), rp_id_hash, 32) != 0)
|
||||
if (memcmp(file_get_data(ef), rp_id_hash, 32) != 0) {
|
||||
continue;
|
||||
ret = credential_load(file_get_data(ef) + 32, file_get_size(ef)-32, rp_id_hash, &rcred);
|
||||
}
|
||||
ret = credential_load(file_get_data(ef) + 32, file_get_size(ef) - 32, rp_id_hash, &rcred);
|
||||
if (ret != 0) {
|
||||
credential_free(&rcred);
|
||||
continue;
|
||||
}
|
||||
if (memcmp(rcred.userId.data, cred.userId.data, MIN(rcred.userId.len, cred.userId.len)) == 0) {
|
||||
if (memcmp(rcred.userId.data, cred.userId.data,
|
||||
MIN(rcred.userId.len, cred.userId.len)) == 0) {
|
||||
sloti = i;
|
||||
credential_free(&rcred);
|
||||
new_record = false;
|
||||
@@ -252,12 +292,13 @@ int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *
|
||||
}
|
||||
credential_free(&rcred);
|
||||
}
|
||||
if (sloti == -1)
|
||||
if (sloti == -1) {
|
||||
return -1;
|
||||
uint8_t *data = (uint8_t *)calloc(1, cred_id_len+32);
|
||||
}
|
||||
uint8_t *data = (uint8_t *) calloc(1, cred_id_len + 32);
|
||||
memcpy(data, rp_id_hash, 32);
|
||||
memcpy(data + 32, cred_id, cred_id_len);
|
||||
file_t *ef = file_new(EF_CRED+sloti);
|
||||
file_t *ef = file_new(EF_CRED + sloti);
|
||||
flash_write_data_to_file(ef, data, cred_id_len + 32);
|
||||
free(data);
|
||||
|
||||
@@ -266,30 +307,32 @@ int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *
|
||||
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
|
||||
ef = search_dynamic_file(EF_RP + i);
|
||||
if (!file_has_data(ef)) {
|
||||
if (sloti == -1)
|
||||
if (sloti == -1) {
|
||||
sloti = i;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (memcmp(file_get_data(ef)+1, rp_id_hash, 32) == 0) {
|
||||
if (memcmp(file_get_data(ef) + 1, rp_id_hash, 32) == 0) {
|
||||
sloti = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sloti == -1)
|
||||
if (sloti == -1) {
|
||||
return -1;
|
||||
}
|
||||
ef = search_dynamic_file(EF_RP + sloti);
|
||||
if (file_has_data(ef)) {
|
||||
data = (uint8_t *)calloc(1, file_get_size(ef));
|
||||
data = (uint8_t *) calloc(1, file_get_size(ef));
|
||||
memcpy(data, file_get_data(ef), file_get_size(ef));
|
||||
data[0] += 1;
|
||||
flash_write_data_to_file(ef, data, file_get_size(ef));
|
||||
free(data);
|
||||
}
|
||||
else {
|
||||
ef = file_new(EF_RP+sloti);
|
||||
data = (uint8_t *)calloc(1, 1 + 32 + cred.rpId.len);
|
||||
ef = file_new(EF_RP + sloti);
|
||||
data = (uint8_t *) calloc(1, 1 + 32 + cred.rpId.len);
|
||||
data[0] = 1;
|
||||
memcpy(data+1, rp_id_hash, 32);
|
||||
memcpy(data + 1, rp_id_hash, 32);
|
||||
memcpy(data + 1 + 32, cred.rpId.data, cred.rpId.len);
|
||||
flash_write_data_to_file(ef, data, 1 + 32 + cred.rpId.len);
|
||||
free(data);
|
||||
@@ -303,13 +346,14 @@ int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *
|
||||
int credential_derive_hmac_key(const uint8_t *cred_id, size_t cred_id_len, uint8_t *outk) {
|
||||
memset(outk, 0, 64);
|
||||
int r = 0;
|
||||
if ((r = load_keydev(outk)) != 0)
|
||||
if ((r = load_keydev(outk)) != 0) {
|
||||
return r;
|
||||
}
|
||||
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 *)"hmac-secret", 11, outk);
|
||||
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 *) "hmac-secret", 11, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, cred_id, cred_id_len, outk);
|
||||
return 0;
|
||||
}
|
||||
@@ -317,26 +361,28 @@ int credential_derive_hmac_key(const uint8_t *cred_id, size_t cred_id_len, uint8
|
||||
int credential_derive_chacha_key(uint8_t *outk) {
|
||||
memset(outk, 0, 32);
|
||||
int r = 0;
|
||||
if ((r = load_keydev(outk)) != 0)
|
||||
if ((r = load_keydev(outk)) != 0) {
|
||||
return r;
|
||||
}
|
||||
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 *)"Encryption key", 14, outk);
|
||||
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 *) "Encryption key", 14, outk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int credential_derive_large_blob_key(const uint8_t *cred_id, size_t cred_id_len, uint8_t *outk) {
|
||||
memset(outk, 0, 32);
|
||||
int r = 0;
|
||||
if ((r = load_keydev(outk)) != 0)
|
||||
if ((r = load_keydev(outk)) != 0) {
|
||||
return r;
|
||||
}
|
||||
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 *)"largeBlobKey", 12, outk);
|
||||
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 *) "largeBlobKey", 12, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, cred_id, cred_id_len, outk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -36,8 +36,7 @@ typedef struct CredExtensions {
|
||||
bool present;
|
||||
} CredExtensions;
|
||||
|
||||
typedef struct Credential
|
||||
{
|
||||
typedef struct Credential {
|
||||
CborCharString rpId;
|
||||
CborByteString userId;
|
||||
CborCharString userName;
|
||||
@@ -59,11 +58,26 @@ typedef struct Credential
|
||||
#define CRED_PROTO "\xf1\xd0\x02\x01"
|
||||
|
||||
extern int credential_verify(uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash);
|
||||
extern int credential_create(CborCharString *rpId, CborByteString *userId, CborCharString *userName, CborCharString *userDisplayName, CredOptions *opts, CredExtensions *extensions, bool use_sign_count, int alg, int curve, uint8_t *cred_id, size_t *cred_id_len);
|
||||
extern int credential_create(CborCharString *rpId,
|
||||
CborByteString *userId,
|
||||
CborCharString *userName,
|
||||
CborCharString *userDisplayName,
|
||||
CredOptions *opts,
|
||||
CredExtensions *extensions,
|
||||
bool use_sign_count,
|
||||
int alg,
|
||||
int curve,
|
||||
uint8_t *cred_id,
|
||||
size_t *cred_id_len);
|
||||
extern void credential_free(Credential *cred);
|
||||
extern int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash);
|
||||
extern int credential_load(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash, Credential *cred);
|
||||
extern int credential_load(const uint8_t *cred_id,
|
||||
size_t cred_id_len,
|
||||
const uint8_t *rp_id_hash,
|
||||
Credential *cred);
|
||||
extern int credential_derive_hmac_key(const uint8_t *cred_id, size_t cred_id_len, uint8_t *outk);
|
||||
extern int credential_derive_large_blob_key(const uint8_t *cred_id, size_t cred_id_len, uint8_t *outk);
|
||||
extern int credential_derive_large_blob_key(const uint8_t *cred_id,
|
||||
size_t cred_id_len,
|
||||
uint8_t *outk);
|
||||
|
||||
#endif // _CREDENTIAL_H_
|
||||
|
||||
@@ -24,7 +24,9 @@ typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned long int uint64_t;
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -18,9 +18,6 @@
|
||||
#ifndef _CTAP2_CBOR_H_
|
||||
#define _CTAP2_CBOR_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include <stdio.h>
|
||||
#include "cbor.h"
|
||||
|
||||
extern uint8_t *driver_prepare_response();
|
||||
@@ -49,9 +46,9 @@ extern const bool _btrue, _bfalse;
|
||||
if (x) \
|
||||
{ \
|
||||
free(x); \
|
||||
x = NULL;\
|
||||
x = NULL; \
|
||||
} \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define CBOR_ERROR(e) \
|
||||
do \
|
||||
@@ -59,7 +56,7 @@ extern const bool _btrue, _bfalse;
|
||||
error = e; \
|
||||
printf("Cbor ERROR [%s:%d]: %d\n", __FILE__, __LINE__, e); \
|
||||
goto err; \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define CBOR_ASSERT(c) \
|
||||
do \
|
||||
@@ -70,7 +67,7 @@ extern const bool _btrue, _bfalse;
|
||||
printf("Cbor ASSERT [%s:%d]: %s\n", __FILE__, __LINE__, #c); \
|
||||
goto err; \
|
||||
} \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define PINUVAUTHTOKEN_MC 0x1
|
||||
#define PINUVAUTHTOKEN_GA 0x2
|
||||
@@ -97,20 +94,20 @@ typedef struct CborCharString {
|
||||
do \
|
||||
{ \
|
||||
if ((v).nofree != true) \
|
||||
CBOR_FREE((v).data); \
|
||||
CBOR_FREE((v).data); \
|
||||
else \
|
||||
(v).data = NULL; \
|
||||
(v).data = NULL; \
|
||||
(v).len = 0; \
|
||||
(v).present = false; \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define CBOR_PARSE_MAP_START(_p,_n) \
|
||||
#define CBOR_PARSE_MAP_START(_p, _n) \
|
||||
CBOR_ASSERT(cbor_value_is_map(&(_p)) == true); \
|
||||
CborValue _f##_n; \
|
||||
CBOR_CHECK(cbor_value_enter_container(&(_p), &(_f##_n))); \
|
||||
while (cbor_value_at_end(&(_f##_n)) == false)
|
||||
|
||||
#define CBOR_PARSE_ARRAY_START(_p,_n) \
|
||||
#define CBOR_PARSE_ARRAY_START(_p, _n) \
|
||||
CBOR_ASSERT(cbor_value_is_array(&(_p)) == true); \
|
||||
CborValue _f##_n; \
|
||||
CBOR_CHECK(cbor_value_enter_container(&(_p), &(_f##_n))); \
|
||||
@@ -121,14 +118,14 @@ typedef struct CborCharString {
|
||||
CBOR_ASSERT(cbor_value_is_unsigned_integer(&(_f##_n)) == true); \
|
||||
CBOR_CHECK(cbor_value_get_uint64(&(_f##_n), &(v))); \
|
||||
CBOR_CHECK(cbor_value_advance_fixed(&(_f##_n))); \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define CBOR_FIELD_GET_INT(v, _n) \
|
||||
do { \
|
||||
CBOR_ASSERT(cbor_value_is_integer(&(_f##_n)) == true); \
|
||||
CBOR_CHECK(cbor_value_get_int64(&(_f##_n), &(v))); \
|
||||
CBOR_CHECK(cbor_value_advance_fixed(&(_f##_n))); \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define CBOR_FIELD_GET_BYTES(v, _n) \
|
||||
do { \
|
||||
@@ -151,7 +148,7 @@ typedef struct CborCharString {
|
||||
CBOR_CHECK(cbor_value_get_boolean(&(_f##_n), &val)); \
|
||||
v = (val == true ? ptrue : pfalse); \
|
||||
CBOR_CHECK(cbor_value_advance_fixed(&(_f##_n))); \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define CBOR_FIELD_GET_KEY_TEXT(_n) \
|
||||
CBOR_ASSERT(cbor_value_is_text_string(&(_f##_n)) == true); \
|
||||
@@ -177,26 +174,26 @@ typedef struct CborCharString {
|
||||
|
||||
#define CBOR_FIELD_KEY_TEXT_VAL_INT(_n, _t, _v) \
|
||||
if (strcmp(_fd##_n, _t) == 0) { \
|
||||
CBOR_FIELD_GET_INT(_v, _n);\
|
||||
CBOR_FIELD_GET_INT(_v, _n); \
|
||||
continue; \
|
||||
}
|
||||
|
||||
#define CBOR_FIELD_KEY_TEXT_VAL_UINT(_n, _t, _v) \
|
||||
if (strcmp(_fd##_n, _t) == 0) { \
|
||||
CBOR_FIELD_GET_UINT(_v, _n);\
|
||||
CBOR_FIELD_GET_UINT(_v, _n); \
|
||||
continue; \
|
||||
}
|
||||
|
||||
#define CBOR_FIELD_KEY_TEXT_VAL_BOOL(_n, _t, _v) \
|
||||
if (strcmp(_fd##_n, _t) == 0) { \
|
||||
CBOR_FIELD_GET_BOOL(_v, _n);\
|
||||
CBOR_FIELD_GET_BOOL(_v, _n); \
|
||||
continue; \
|
||||
}
|
||||
|
||||
#define CBOR_PARSE_MAP_END(_p,_n) \
|
||||
#define CBOR_PARSE_MAP_END(_p, _n) \
|
||||
CBOR_CHECK(cbor_value_leave_container(&(_p), &(_f##_n)))
|
||||
|
||||
#define CBOR_PARSE_ARRAY_END(_p,_n) CBOR_PARSE_MAP_END(_p, _n)
|
||||
#define CBOR_PARSE_ARRAY_END(_p, _n) CBOR_PARSE_MAP_END(_p, _n)
|
||||
|
||||
#define CBOR_ADVANCE(_n) CBOR_CHECK(cbor_value_advance(&_f##_n));
|
||||
|
||||
@@ -205,39 +202,39 @@ typedef struct CborCharString {
|
||||
if ((v).data && (v).len > 0) { \
|
||||
CBOR_CHECK(cbor_encode_uint(&(p), (k))); \
|
||||
CBOR_CHECK(cbor_encode_byte_string(&(p), (v).data, (v).len)); \
|
||||
} } while(0)
|
||||
} } while (0)
|
||||
|
||||
#define CBOR_APPEND_KEY_UINT_VAL_STRING(p, k, v) \
|
||||
do { \
|
||||
if ((v).data && (v).len > 0) { \
|
||||
CBOR_CHECK(cbor_encode_uint(&(p), (k))); \
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&(p), (v).data)); \
|
||||
} } while(0)
|
||||
} } while (0)
|
||||
|
||||
|
||||
#define CBOR_APPEND_KEY_UINT_VAL_UINT(p, k, v) \
|
||||
do { \
|
||||
CBOR_CHECK(cbor_encode_uint(&(p), (k))); \
|
||||
CBOR_CHECK(cbor_encode_uint(&(p), (v))); \
|
||||
} while(0)
|
||||
CBOR_CHECK(cbor_encode_uint(&(p), (k))); \
|
||||
CBOR_CHECK(cbor_encode_uint(&(p), (v))); \
|
||||
} while (0)
|
||||
|
||||
#define CBOR_APPEND_KEY_UINT_VAL_INT(p, k, v) \
|
||||
do { \
|
||||
CBOR_CHECK(cbor_encode_int(&(p), (k))); \
|
||||
CBOR_CHECK(cbor_encode_int(&(p), (v))); \
|
||||
} while(0)
|
||||
CBOR_CHECK(cbor_encode_int(&(p), (k))); \
|
||||
CBOR_CHECK(cbor_encode_int(&(p), (v))); \
|
||||
} while (0)
|
||||
|
||||
#define CBOR_APPEND_KEY_UINT_VAL_BOOL(p, k, v) \
|
||||
do { \
|
||||
CBOR_CHECK(cbor_encode_uint(&(p), (k))); \
|
||||
CBOR_CHECK(cbor_encode_boolean(&(p), (v))); \
|
||||
} while(0)
|
||||
CBOR_CHECK(cbor_encode_uint(&(p), (k))); \
|
||||
CBOR_CHECK(cbor_encode_boolean(&(p), (v))); \
|
||||
} while (0)
|
||||
|
||||
#define CBOR_APPEND_KEY_UINT_VAL_PBOOL(p, k, v) \
|
||||
do { \
|
||||
if (v != NULL) {\
|
||||
if (v != NULL) { \
|
||||
CBOR_CHECK(cbor_encode_uint(&(p), (k))); \
|
||||
CBOR_CHECK(cbor_encode_boolean(&(p), v == ptrue ? true : false)); \
|
||||
} } while(0)
|
||||
} } while (0)
|
||||
|
||||
#endif //_CTAP2_CBOR_H_
|
||||
|
||||
212
src/fido/fido.c
212
src/fido/fido.c
@@ -15,29 +15,27 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "fido.h"
|
||||
#include "hsm.h"
|
||||
#include "apdu.h"
|
||||
#include "ctap.h"
|
||||
#include "files.h"
|
||||
#include "file.h"
|
||||
#include "usb.h"
|
||||
#include "random.h"
|
||||
#include "bsp/board.h"
|
||||
#include "mbedtls/ecdsa.h"
|
||||
#include "mbedtls/x509_crt.h"
|
||||
#include "mbedtls/hkdf.h"
|
||||
#include "pk_wrap.h"
|
||||
#include "crypto_utils.h"
|
||||
#if defined(USB_ITF_CCID) || defined(ENABLE_EMULATION)
|
||||
#include "ccid/ccid.h"
|
||||
#endif
|
||||
#ifndef ENABLE_EMULATION
|
||||
#include "bsp/board.h"
|
||||
#endif
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void init_fido(bool);
|
||||
int fido_process_apdu();
|
||||
int fido_unload();
|
||||
|
||||
pinUvAuthToken_t paut = {0};
|
||||
pinUvAuthToken_t paut = { 0 };
|
||||
|
||||
uint8_t keydev_dec[32];
|
||||
bool has_keydev_dec = false;
|
||||
@@ -47,18 +45,27 @@ const uint8_t fido_aid[] = {
|
||||
0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01
|
||||
};
|
||||
|
||||
app_t *fido_select(app_t *a) {
|
||||
a->aid = fido_aid;
|
||||
a->process_apdu = fido_process_apdu;
|
||||
a->unload = fido_unload;
|
||||
current_app = a;
|
||||
//init_fido(false);
|
||||
return a;
|
||||
const uint8_t atr_fido[] = {
|
||||
23,
|
||||
0x3b, 0xfd, 0x13, 0x00, 0x00, 0x81, 0x31, 0xfe, 0x15, 0x80, 0x73, 0xc0, 0x21, 0xc0, 0x57, 0x59,
|
||||
0x75, 0x62, 0x69, 0x4b, 0x65, 0x79, 0x40
|
||||
};
|
||||
|
||||
app_t *fido_select(app_t *a, const uint8_t *aid, uint8_t aid_len) {
|
||||
if (!memcmp(aid, fido_aid + 1, MIN(aid_len, fido_aid[0]))) {
|
||||
a->aid = fido_aid;
|
||||
a->process_apdu = fido_process_apdu;
|
||||
a->unload = fido_unload;
|
||||
return a;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void __attribute__ ((constructor)) fido_ctor() {
|
||||
void __attribute__((constructor)) fido_ctor() {
|
||||
#if defined(USB_ITF_CCID) || defined(ENABLE_EMULATION)
|
||||
ccid_atr = atr_fido;
|
||||
#endif
|
||||
register_app(fido_select);
|
||||
//fido_select(&apps[0]);
|
||||
}
|
||||
|
||||
int fido_unload() {
|
||||
@@ -66,43 +73,51 @@ int fido_unload() {
|
||||
}
|
||||
|
||||
mbedtls_ecp_group_id fido_curve_to_mbedtls(int curve) {
|
||||
if (curve == FIDO2_CURVE_P256)
|
||||
if (curve == FIDO2_CURVE_P256) {
|
||||
return MBEDTLS_ECP_DP_SECP256R1;
|
||||
else if (curve == FIDO2_CURVE_P384)
|
||||
}
|
||||
else if (curve == FIDO2_CURVE_P384) {
|
||||
return MBEDTLS_ECP_DP_SECP384R1;
|
||||
else if (curve == FIDO2_CURVE_P521)
|
||||
}
|
||||
else if (curve == FIDO2_CURVE_P521) {
|
||||
return MBEDTLS_ECP_DP_SECP521R1;
|
||||
else if (curve == FIDO2_CURVE_P256K1)
|
||||
}
|
||||
else if (curve == FIDO2_CURVE_P256K1) {
|
||||
return MBEDTLS_ECP_DP_SECP256K1;
|
||||
else if (curve == FIDO2_CURVE_X25519)
|
||||
}
|
||||
else if (curve == FIDO2_CURVE_X25519) {
|
||||
return MBEDTLS_ECP_DP_CURVE25519;
|
||||
else if (curve == FIDO2_CURVE_X448)
|
||||
}
|
||||
else if (curve == FIDO2_CURVE_X448) {
|
||||
return MBEDTLS_ECP_DP_CURVE448;
|
||||
}
|
||||
return MBEDTLS_ECP_DP_NONE;
|
||||
}
|
||||
|
||||
int fido_load_key(int curve, const uint8_t *cred_id, mbedtls_ecdsa_context *key) {
|
||||
mbedtls_ecp_group_id mbedtls_curve = fido_curve_to_mbedtls(curve);
|
||||
if (mbedtls_curve == MBEDTLS_ECP_DP_NONE)
|
||||
if (mbedtls_curve == MBEDTLS_ECP_DP_NONE) {
|
||||
return CTAP2_ERR_UNSUPPORTED_ALGORITHM;
|
||||
}
|
||||
uint8_t key_path[KEY_PATH_LEN];
|
||||
memcpy(key_path, cred_id, KEY_PATH_LEN);
|
||||
*(uint32_t *)key_path = 0x80000000 | 10022;
|
||||
for (int i = 1; i < KEY_PATH_ENTRIES; i++)
|
||||
*(uint32_t *)(key_path+i*sizeof(uint32_t)) |= 0x80000000;
|
||||
*(uint32_t *) key_path = 0x80000000 | 10022;
|
||||
for (int i = 1; i < KEY_PATH_ENTRIES; i++) {
|
||||
*(uint32_t *) (key_path + i * sizeof(uint32_t)) |= 0x80000000;
|
||||
}
|
||||
return derive_key(NULL, false, key_path, mbedtls_curve, key);
|
||||
}
|
||||
|
||||
int x509_create_cert(mbedtls_ecdsa_context *ecdsa, uint8_t *buffer, size_t buffer_size, bool core1) {
|
||||
int x509_create_cert(mbedtls_ecdsa_context *ecdsa, uint8_t *buffer, size_t buffer_size) {
|
||||
mbedtls_x509write_cert ctx;
|
||||
mbedtls_x509write_crt_init(&ctx);
|
||||
mbedtls_x509write_crt_set_version(&ctx, MBEDTLS_X509_CRT_VERSION_3);
|
||||
mbedtls_x509write_crt_set_validity(&ctx, "20220901000000", "20320831235959" );
|
||||
mbedtls_x509write_crt_set_validity(&ctx, "20220901000000", "20720831235959");
|
||||
mbedtls_x509write_crt_set_issuer_name(&ctx, "C=ES,O=Pico HSM,CN=Pico FIDO");
|
||||
mbedtls_x509write_crt_set_subject_name(&ctx, "C=ES,O=Pico HSM,CN=Pico FIDO");
|
||||
mbedtls_mpi serial;
|
||||
mbedtls_mpi_init(&serial);
|
||||
mbedtls_mpi_fill_random(&serial, 32, core1 ? random_gen : random_gen_core0, NULL);
|
||||
mbedtls_mpi_fill_random(&serial, 32, random_gen, NULL);
|
||||
mbedtls_x509write_crt_set_serial(&ctx, &serial);
|
||||
mbedtls_pk_context key;
|
||||
mbedtls_pk_init(&key);
|
||||
@@ -114,27 +129,32 @@ int x509_create_cert(mbedtls_ecdsa_context *ecdsa, uint8_t *buffer, size_t buffe
|
||||
mbedtls_x509write_crt_set_basic_constraints(&ctx, 0, 0);
|
||||
mbedtls_x509write_crt_set_subject_key_identifier(&ctx);
|
||||
mbedtls_x509write_crt_set_authority_key_identifier(&ctx);
|
||||
mbedtls_x509write_crt_set_key_usage(&ctx, MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_KEY_CERT_SIGN);
|
||||
int ret = mbedtls_x509write_crt_der(&ctx, buffer, buffer_size, core1 ? random_gen : random_gen_core0, NULL);
|
||||
mbedtls_x509write_crt_set_key_usage(&ctx,
|
||||
MBEDTLS_X509_KU_DIGITAL_SIGNATURE |
|
||||
MBEDTLS_X509_KU_KEY_CERT_SIGN);
|
||||
int ret = mbedtls_x509write_crt_der(&ctx, buffer, buffer_size, random_gen, NULL);
|
||||
/* pk cannot be freed, as it is freed later */
|
||||
//mbedtls_pk_free(&key);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int load_keydev(uint8_t *key) {
|
||||
if (has_keydev_dec == false && !file_has_data(ef_keydev))
|
||||
if (has_keydev_dec == false && !file_has_data(ef_keydev)) {
|
||||
return CCID_ERR_MEMORY_FATAL;
|
||||
if (has_keydev_dec == true)
|
||||
}
|
||||
if (has_keydev_dec == true) {
|
||||
memcpy(key, keydev_dec, sizeof(keydev_dec));
|
||||
else
|
||||
}
|
||||
else {
|
||||
memcpy(key, file_get_data(ef_keydev), file_get_size(ef_keydev));
|
||||
}
|
||||
//return mkek_decrypt(key, file_get_size(ef_keydev));
|
||||
return CCID_OK;
|
||||
}
|
||||
|
||||
int verify_key(const uint8_t *appId, const uint8_t *keyHandle, mbedtls_ecdsa_context *key) {
|
||||
for (int i = 0; i < KEY_PATH_ENTRIES; i++) {
|
||||
uint32_t k = *(uint32_t *)&keyHandle[i*sizeof(uint32_t)];
|
||||
uint32_t k = *(uint32_t *) &keyHandle[i * sizeof(uint32_t)];
|
||||
if (!(k & 0x80000000)) {
|
||||
return -1;
|
||||
}
|
||||
@@ -143,40 +163,61 @@ int verify_key(const uint8_t *appId, const uint8_t *keyHandle, mbedtls_ecdsa_con
|
||||
if (key == NULL) {
|
||||
mbedtls_ecdsa_init(&ctx);
|
||||
key = &ctx;
|
||||
if (derive_key(appId, false, (uint8_t *)keyHandle, MBEDTLS_ECP_DP_SECP256R1, &ctx) != 0) {
|
||||
if (derive_key(appId, false, (uint8_t *) keyHandle, MBEDTLS_ECP_DP_SECP256R1, &ctx) != 0) {
|
||||
mbedtls_ecdsa_free(&ctx);
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
uint8_t hmac[32], d[32];
|
||||
int ret = mbedtls_ecp_write_key(key, d, sizeof(d));
|
||||
if (key == NULL)
|
||||
if (key == NULL) {
|
||||
mbedtls_ecdsa_free(&ctx);
|
||||
if (ret != 0)
|
||||
}
|
||||
if (ret != 0) {
|
||||
return -2;
|
||||
}
|
||||
uint8_t key_base[CTAP_APPID_SIZE + KEY_PATH_LEN];
|
||||
memcpy(key_base, appId, CTAP_APPID_SIZE);
|
||||
memcpy(key_base + CTAP_APPID_SIZE, keyHandle, KEY_PATH_LEN);
|
||||
ret = mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), d, 32, key_base, sizeof(key_base), hmac);
|
||||
ret =
|
||||
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
|
||||
d,
|
||||
32,
|
||||
key_base,
|
||||
sizeof(key_base),
|
||||
hmac);
|
||||
mbedtls_platform_zeroize(d, sizeof(d));
|
||||
return memcmp(keyHandle + KEY_PATH_LEN, hmac, sizeof(hmac));
|
||||
}
|
||||
|
||||
int derive_key(const uint8_t *app_id, bool new_key, uint8_t *key_handle, int curve, mbedtls_ecdsa_context *key) {
|
||||
uint8_t outk[64] = {0};
|
||||
int derive_key(const uint8_t *app_id,
|
||||
bool new_key,
|
||||
uint8_t *key_handle,
|
||||
int curve,
|
||||
mbedtls_ecdsa_context *key) {
|
||||
uint8_t outk[64] = { 0 };
|
||||
int r = 0;
|
||||
memset(outk, 0, sizeof(outk));
|
||||
if ((r = load_keydev(outk)) != CCID_OK)
|
||||
if ((r = load_keydev(outk)) != CCID_OK) {
|
||||
return r;
|
||||
}
|
||||
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
|
||||
for (int i = 0; i < KEY_PATH_ENTRIES; i++) {
|
||||
if (new_key == true) {
|
||||
uint32_t val = 0;
|
||||
random_gen(NULL, (uint8_t *) &val, sizeof(val));
|
||||
val |= 0x80000000;
|
||||
memcpy(&key_handle[i*sizeof(uint32_t)], &val, sizeof(uint32_t));
|
||||
memcpy(&key_handle[i * sizeof(uint32_t)], &val, sizeof(uint32_t));
|
||||
}
|
||||
r = mbedtls_hkdf(md_info, &key_handle[i * sizeof(uint32_t)], sizeof(uint32_t), outk, 32, outk + 32, 32, outk, sizeof(outk));
|
||||
r = mbedtls_hkdf(md_info,
|
||||
&key_handle[i * sizeof(uint32_t)],
|
||||
sizeof(uint32_t),
|
||||
outk,
|
||||
32,
|
||||
outk + 32,
|
||||
32,
|
||||
outk,
|
||||
sizeof(outk));
|
||||
if (r != 0) {
|
||||
mbedtls_platform_zeroize(outk, sizeof(outk));
|
||||
return r;
|
||||
@@ -186,8 +227,9 @@ int derive_key(const uint8_t *app_id, bool new_key, uint8_t *key_handle, int cur
|
||||
uint8_t key_base[CTAP_APPID_SIZE + KEY_PATH_LEN];
|
||||
memcpy(key_base, app_id, CTAP_APPID_SIZE);
|
||||
memcpy(key_base + CTAP_APPID_SIZE, key_handle, KEY_PATH_LEN);
|
||||
if ((r = mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), outk, 32, key_base, sizeof(key_base), key_handle + 32)) != 0)
|
||||
{
|
||||
if ((r =
|
||||
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), outk, 32, key_base,
|
||||
sizeof(key_base), key_handle + 32)) != 0) {
|
||||
mbedtls_platform_zeroize(outk, sizeof(outk));
|
||||
return r;
|
||||
}
|
||||
@@ -195,19 +237,21 @@ int derive_key(const uint8_t *app_id, bool new_key, uint8_t *key_handle, int cur
|
||||
if (key != NULL) {
|
||||
mbedtls_ecp_group_load(&key->grp, curve);
|
||||
const mbedtls_ecp_curve_info *cinfo = mbedtls_ecp_curve_info_from_grp_id(curve);
|
||||
if (cinfo == NULL)
|
||||
if (cinfo == NULL) {
|
||||
return 1;
|
||||
r = mbedtls_ecp_read_key(curve, key, outk, ceil((float)cinfo->bit_size/8));
|
||||
}
|
||||
r = mbedtls_ecp_read_key(curve, key, outk, ceil((float) cinfo->bit_size / 8));
|
||||
mbedtls_platform_zeroize(outk, sizeof(outk));
|
||||
if (r != 0)
|
||||
if (r != 0) {
|
||||
return r;
|
||||
}
|
||||
return mbedtls_ecp_mul(&key->grp, &key->Q, &key->d, &key->grp.G, random_gen, NULL);
|
||||
}
|
||||
mbedtls_platform_zeroize(outk, sizeof(outk));
|
||||
return r;
|
||||
}
|
||||
|
||||
int scan_files(bool core1) {
|
||||
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);
|
||||
if (ef_keydev) {
|
||||
@@ -216,7 +260,7 @@ int scan_files(bool core1) {
|
||||
mbedtls_ecdsa_context ecdsa;
|
||||
mbedtls_ecdsa_init(&ecdsa);
|
||||
uint8_t index = 0;
|
||||
int ret = mbedtls_ecdsa_genkey(&ecdsa, MBEDTLS_ECP_DP_SECP256R1, core1 ? random_gen : random_gen_core0, &index);
|
||||
int ret = mbedtls_ecdsa_genkey(&ecdsa, MBEDTLS_ECP_DP_SECP256R1, random_gen, &index);
|
||||
if (ret != 0) {
|
||||
mbedtls_ecdsa_free(&ecdsa);
|
||||
return ret;
|
||||
@@ -242,20 +286,24 @@ int scan_files(bool core1) {
|
||||
uint8_t cert[4096];
|
||||
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));
|
||||
int ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1,
|
||||
&key,
|
||||
file_get_data(ef_keydev),
|
||||
file_get_size(ef_keydev));
|
||||
if (ret != 0) {
|
||||
mbedtls_ecdsa_free(&key);
|
||||
return ret;
|
||||
}
|
||||
ret = mbedtls_ecp_mul(&key.grp, &key.Q, &key.d, &key.grp.G, core1 ? random_gen : random_gen_core0, NULL);
|
||||
ret = mbedtls_ecp_mul(&key.grp, &key.Q, &key.d, &key.grp.G, random_gen, NULL);
|
||||
if (ret != 0) {
|
||||
mbedtls_ecdsa_free(&key);
|
||||
return ret;
|
||||
}
|
||||
ret = x509_create_cert(&key, cert, sizeof(cert), core1);
|
||||
ret = x509_create_cert(&key, cert, sizeof(cert));
|
||||
mbedtls_ecdsa_free(&key);
|
||||
if (ret <= 0)
|
||||
if (ret <= 0) {
|
||||
return ret;
|
||||
}
|
||||
flash_write_data_to_file(ef_certdev, cert + sizeof(cert) - ret, ret);
|
||||
}
|
||||
}
|
||||
@@ -266,7 +314,7 @@ int scan_files(bool core1) {
|
||||
if (ef_counter) {
|
||||
if (!file_has_data(ef_counter)) {
|
||||
uint32_t v = 0;
|
||||
flash_write_data_to_file(ef_counter, (uint8_t *)&v, sizeof(v));
|
||||
flash_write_data_to_file(ef_counter, (uint8_t *) &v, sizeof(v));
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -277,10 +325,7 @@ int scan_files(bool core1) {
|
||||
if (ef_authtoken) {
|
||||
if (!file_has_data(ef_authtoken)) {
|
||||
uint8_t t[32];
|
||||
if (core1)
|
||||
random_gen(NULL, t, sizeof(t));
|
||||
else
|
||||
random_gen_core0(NULL, t, sizeof(t));
|
||||
random_gen(NULL, t, sizeof(t));
|
||||
flash_write_data_to_file(ef_authtoken, t, sizeof(t));
|
||||
}
|
||||
paut.data = file_get_data(ef_authtoken);
|
||||
@@ -291,39 +336,45 @@ int scan_files(bool core1) {
|
||||
}
|
||||
ef_largeblob = search_by_fid(EF_LARGEBLOB, NULL, SPECIFY_EF);
|
||||
if (!file_has_data(ef_largeblob)) {
|
||||
flash_write_data_to_file(ef_largeblob, (const uint8_t *)"\x80\x76\xbe\x8b\x52\x8d\x00\x75\xf7\xaa\xe9\x8d\x6f\xa5\x7a\x6d\x3c", 17);
|
||||
flash_write_data_to_file(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 CCID_OK;
|
||||
}
|
||||
|
||||
void scan_all(bool core1) {
|
||||
void scan_all() {
|
||||
scan_flash();
|
||||
scan_files(core1);
|
||||
scan_files();
|
||||
}
|
||||
|
||||
void init_fido(bool core1) {
|
||||
scan_all(core1);
|
||||
void init_fido() {
|
||||
scan_all();
|
||||
}
|
||||
|
||||
bool wait_button_pressed() {
|
||||
uint32_t val = EV_PRESS_BUTTON;
|
||||
#if defined(ENABLE_UP_BUTTON) && ENABLE_UP_BUTTON==1
|
||||
#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
|
||||
return (val == EV_BUTTON_TIMEOUT);
|
||||
#endif
|
||||
return val == EV_BUTTON_TIMEOUT;
|
||||
}
|
||||
|
||||
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 (wait_button_pressed() == true) //timeout
|
||||
#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 (wait_button_pressed() == true) { //timeout
|
||||
return false;
|
||||
}
|
||||
//user_present_time_limit = board_millis();
|
||||
}
|
||||
#endif
|
||||
@@ -337,8 +388,9 @@ uint32_t get_sign_counter() {
|
||||
|
||||
uint8_t get_opts() {
|
||||
file_t *ef = search_by_fid(EF_OPTS, NULL, SPECIFY_EF);
|
||||
if (file_has_data(ef))
|
||||
if (file_has_data(ef)) {
|
||||
return *file_get_data(ef);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -348,12 +400,6 @@ void set_opts(uint8_t opts) {
|
||||
low_flash_available();
|
||||
}
|
||||
|
||||
typedef struct cmd
|
||||
{
|
||||
uint8_t ins;
|
||||
int (*cmd_handler)();
|
||||
} cmd_t;
|
||||
|
||||
extern int cmd_register();
|
||||
extern int cmd_authenticate();
|
||||
extern int cmd_version();
|
||||
@@ -362,14 +408,14 @@ static const cmd_t cmds[] = {
|
||||
{ CTAP_REGISTER, cmd_register },
|
||||
{ CTAP_AUTHENTICATE, cmd_authenticate },
|
||||
{ CTAP_VERSION, cmd_version },
|
||||
{ 0x00, 0x0}
|
||||
{ 0x00, 0x0 }
|
||||
};
|
||||
|
||||
int fido_process_apdu() {
|
||||
if (CLA(apdu) != 0x00)
|
||||
if (CLA(apdu) != 0x00) {
|
||||
return SW_CLA_NOT_SUPPORTED();
|
||||
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++)
|
||||
{
|
||||
}
|
||||
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
|
||||
if (cmd->ins == INS(apdu)) {
|
||||
int r = cmd->cmd_handler();
|
||||
return r;
|
||||
|
||||
@@ -18,11 +18,16 @@
|
||||
#ifndef _FIDO_H_
|
||||
#define _FIDO_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#ifndef ENABLE_EMULATION
|
||||
#include "pico/stdlib.h"
|
||||
#endif
|
||||
#include "common.h"
|
||||
#include "mbedtls/ecdsa.h"
|
||||
#ifndef ENABLE_EMULATION
|
||||
#include "ctap_hid.h"
|
||||
#else
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
#define CTAP_PUBKEY_LEN (65)
|
||||
#define KEY_PATH_LEN (32)
|
||||
@@ -30,17 +35,28 @@
|
||||
#define SHA256_DIGEST_LENGTH (32)
|
||||
#define KEY_HANDLE_LEN (KEY_PATH_LEN + SHA256_DIGEST_LENGTH)
|
||||
|
||||
extern int scan_files(bool);
|
||||
extern int derive_key(const uint8_t *app_id, bool new_key, uint8_t *key_handle, int, mbedtls_ecdsa_context *key);
|
||||
extern int scan_files();
|
||||
extern int derive_key(const uint8_t *app_id,
|
||||
bool new_key,
|
||||
uint8_t *key_handle,
|
||||
int,
|
||||
mbedtls_ecdsa_context *key);
|
||||
extern int verify_key(const uint8_t *appId, const uint8_t *keyHandle, mbedtls_ecdsa_context *);
|
||||
extern bool wait_button_pressed();
|
||||
extern CTAPHID_FRAME *ctap_req, *ctap_resp;
|
||||
extern void init_fido(bool);
|
||||
extern void init_fido();
|
||||
extern mbedtls_ecp_group_id fido_curve_to_mbedtls(int curve);
|
||||
extern int fido_load_key(int curve, const uint8_t *cred_id, mbedtls_ecdsa_context *key);
|
||||
extern int load_keydev(uint8_t *key);
|
||||
extern int encrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, size_t in_len, uint8_t *out);
|
||||
extern int decrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, size_t in_len, uint8_t *out);
|
||||
extern int encrypt(uint8_t protocol,
|
||||
const uint8_t *key,
|
||||
const uint8_t *in,
|
||||
size_t in_len,
|
||||
uint8_t *out);
|
||||
extern int decrypt(uint8_t protocol,
|
||||
const uint8_t *key,
|
||||
const uint8_t *in,
|
||||
size_t in_len,
|
||||
uint8_t *out);
|
||||
extern int ecdh(uint8_t protocol, const mbedtls_ecp_point *Q, uint8_t *sharedSecret);
|
||||
|
||||
#define FIDO2_ALG_ES256 -7 //ECDSA-SHA256 P256
|
||||
@@ -92,7 +108,7 @@ typedef struct known_app {
|
||||
|
||||
extern const known_app_t *find_app_by_rp_id_hash(const uint8_t *rp_id_hash);
|
||||
|
||||
#define TRANSPORT_TIME_LIMIT (30*1000) //USB
|
||||
#define TRANSPORT_TIME_LIMIT (30 * 1000) //USB
|
||||
|
||||
bool check_user_presence();
|
||||
|
||||
@@ -110,6 +126,10 @@ typedef struct pinUvAuthToken {
|
||||
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, size_t len, uint8_t *sign);
|
||||
extern int verify(uint8_t protocol,
|
||||
const uint8_t *key,
|
||||
const uint8_t *data,
|
||||
size_t len,
|
||||
uint8_t *sign);
|
||||
|
||||
#endif //_FIDO_H
|
||||
|
||||
@@ -15,26 +15,43 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "file.h"
|
||||
#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 = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_UNKNOWN, .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_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 = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_UNKNOWN, .data = NULL,
|
||||
.ef_structure = 0, .acl = { 0 } } //end
|
||||
};
|
||||
|
||||
const file_t *MF = &file_entries[0];
|
||||
const file_t *file_last = &file_entries[sizeof(file_entries)/sizeof(file_t)-1];
|
||||
const file_t *file_last = &file_entries[sizeof(file_entries) / sizeof(file_t) - 1];
|
||||
file_t *ef_keydev = NULL;
|
||||
file_t *ef_certdev = NULL;
|
||||
file_t *ef_counter = NULL;
|
||||
|
||||
@@ -32,6 +32,10 @@
|
||||
#define EF_CRED 0xCF00 // Creds at 0xCF00 - 0xCFFF
|
||||
#define EF_RP 0xD000 // RPs at 0xD000 - 0xD0FF
|
||||
#define EF_LARGEBLOB 0x1101 // Large Blob Array
|
||||
#define EF_OATH_CRED 0xBA00 // OATH Creds at 0xBA00 - 0xBAFE
|
||||
#define EF_OATH_CODE 0xBAFF
|
||||
#define EF_OTP_SLOT1 0xBB00
|
||||
#define EF_OTP_SLOT2 0xBB01
|
||||
|
||||
extern file_t *ef_keydev;
|
||||
extern file_t *ef_certdev;
|
||||
|
||||
@@ -15,280 +15,359 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "fido.h"
|
||||
#include "ctap2_cbor.h"
|
||||
|
||||
static const known_app_t kapps[] = {
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x96\x89\x78\xa2\x99\x53\xde\x52\xd3\xef\x0f\x0c\x71\xb7\xb7\xb6\xb1\xaf\x9f\x08\xe2\x57\x89\x6a\x8d\x81\x26\x91\x85\x30\x29\x3b",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x96\x89\x78\xa2\x99\x53\xde\x52\xd3\xef\x0f\x0c\x71\xb7\xb7\xb6\xb1\xaf\x9f\x08\xe2\x57\x89\x6a\x8d\x81\x26\x91\x85\x30\x29\x3b",
|
||||
.label = "aws.amazon.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xc3\x40\x8c\x04\x47\x88\xae\xa5\xb3\xdf\x30\x89\x52\xfd\x8c\xa3\xc7\x0e\x21\xfe\xf4\xf6\xc1\xc2\x37\x4c\xaa\x1d\xf9\xb2\x8d\xdd",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xc3\x40\x8c\x04\x47\x88\xae\xa5\xb3\xdf\x30\x89\x52\xfd\x8c\xa3\xc7\x0e\x21\xfe\xf4\xf6\xc1\xc2\x37\x4c\xaa\x1d\xf9\xb2\x8d\xdd",
|
||||
.label = "www.binance.com",
|
||||
.use_sign_count = pfalse,
|
||||
.use_self_attestation = ptrue,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x20\xf6\x61\xb1\x94\x0c\x34\x70\xac\x54\xfa\x2e\xb4\x99\x90\xfd\x33\xb5\x6d\xe8\xde\x60\x18\x70\xff\x02\xa8\x06\x0f\x3b\x7c\x58",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x20\xf6\x61\xb1\x94\x0c\x34\x70\xac\x54\xfa\x2e\xb4\x99\x90\xfd\x33\xb5\x6d\xe8\xde\x60\x18\x70\xff\x02\xa8\x06\x0f\x3b\x7c\x58",
|
||||
.label = "binance.com",
|
||||
.use_sign_count = pfalse,
|
||||
.use_self_attestation = ptrue,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x12\x74\x3b\x92\x12\x97\xb7\x7f\x11\x35\xe4\x1f\xde\xdd\x4a\x84\x6a\xfe\x82\xe1\xf3\x69\x32\xa9\x91\x2f\x3b\x0d\x8d\xfb\x7d\x0e",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x12\x74\x3b\x92\x12\x97\xb7\x7f\x11\x35\xe4\x1f\xde\xdd\x4a\x84\x6a\xfe\x82\xe1\xf3\x69\x32\xa9\x91\x2f\x3b\x0d\x8d\xfb\x7d\x0e",
|
||||
//U2F key for Bitbucket
|
||||
.label = "bitbucket.org",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x30\x2f\xd5\xb4\x49\x2a\x07\xb9\xfe\xbb\x30\xe7\x32\x69\xec\xa5\x01\x20\x5c\xcf\xe0\xc2\x0b\xf7\xb4\x72\xfa\x2d\x31\xe2\x1e\x63",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x30\x2f\xd5\xb4\x49\x2a\x07\xb9\xfe\xbb\x30\xe7\x32\x69\xec\xa5\x01\x20\x5c\xcf\xe0\xc2\x0b\xf7\xb4\x72\xfa\x2d\x31\xe2\x1e\x63",
|
||||
//U2F key for Bitfinex
|
||||
.label = "www.bitfinex.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xa3\x4d\x30\x9f\xfa\x28\xc1\x24\x14\xb8\xba\x6c\x07\xee\x1e\xfa\xe1\xa8\x5e\x8a\x04\x61\x48\x59\xa6\x7c\x04\x93\xb6\x95\x61\x90",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xa3\x4d\x30\x9f\xfa\x28\xc1\x24\x14\xb8\xba\x6c\x07\xee\x1e\xfa\xe1\xa8\x5e\x8a\x04\x61\x48\x59\xa6\x7c\x04\x93\xb6\x95\x61\x90",
|
||||
//U2F key for Bitwarden
|
||||
.label = "vault.bitwarden.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x19\x81\x5c\xb9\xa5\xfb\x25\xd8\x05\xde\xbd\x7b\x32\x53\x7e\xd5\x78\x63\x9b\x3e\xd1\x08\xec\x7c\x5b\xb9\xe8\xf0\xdf\xb1\x68\x73",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x19\x81\x5c\xb9\xa5\xfb\x25\xd8\x05\xde\xbd\x7b\x32\x53\x7e\xd5\x78\x63\x9b\x3e\xd1\x08\xec\x7c\x5b\xb9\xe8\xf0\xdf\xb1\x68\x73",
|
||||
//WebAuthn key for Cloudflare
|
||||
.label = "dash.cloudflare.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xe2\x7d\x61\xb4\xe9\x9d\xe0\xed\x98\x16\x3c\xb3\x8b\x7a\xf9\x33\xc6\x66\x5e\x55\x09\xe8\x49\x08\x37\x05\x58\x13\x77\x8e\x23\x6a",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xe2\x7d\x61\xb4\xe9\x9d\xe0\xed\x98\x16\x3c\xb3\x8b\x7a\xf9\x33\xc6\x66\x5e\x55\x09\xe8\x49\x08\x37\x05\x58\x13\x77\x8e\x23\x6a",
|
||||
//WebAuthn key for Coinbase
|
||||
.label = "coinbase.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x68\x20\x19\x15\xd7\x4c\xb4\x2a\xf5\xb3\xcc\x5c\x95\xb9\x55\x3e\x3e\x3a\x83\xb4\xd2\xa9\x3b\x45\xfb\xad\xaa\x84\x69\xff\x8e\x6e",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x68\x20\x19\x15\xd7\x4c\xb4\x2a\xf5\xb3\xcc\x5c\x95\xb9\x55\x3e\x3e\x3a\x83\xb4\xd2\xa9\x3b\x45\xfb\xad\xaa\x84\x69\xff\x8e\x6e",
|
||||
//U2F key for Dashlane
|
||||
.label = "www.dashlane.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xc5\x0f\x8a\x7b\x70\x8e\x92\xf8\x2e\x7a\x50\xe2\xbd\xc5\x5d\x8f\xd9\x1a\x22\xfe\x6b\x29\xc0\xcd\xf7\x80\x55\x30\x84\x2a\xf5\x81",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xc5\x0f\x8a\x7b\x70\x8e\x92\xf8\x2e\x7a\x50\xe2\xbd\xc5\x5d\x8f\xd9\x1a\x22\xfe\x6b\x29\xc0\xcd\xf7\x80\x55\x30\x84\x2a\xf5\x81",
|
||||
//U2F key for Dropbox
|
||||
.label = "www.dropbox.com",
|
||||
.use_sign_count = pfalse,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x82\xf4\xa8\xc9\x5f\xec\x94\xb2\x6b\xaf\x9e\x37\x25\x0e\x95\x63\xd9\xa3\x66\xc7\xbe\x26\x1c\xa4\xdd\x01\x01\xf4\xd5\xef\xcb\x83",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x82\xf4\xa8\xc9\x5f\xec\x94\xb2\x6b\xaf\x9e\x37\x25\x0e\x95\x63\xd9\xa3\x66\xc7\xbe\x26\x1c\xa4\xdd\x01\x01\xf4\xd5\xef\xcb\x83",
|
||||
//WebAuthn key for Dropbox
|
||||
.label = "www.dropbox.com",
|
||||
.use_sign_count = pfalse,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xf3\xe2\x04\x2f\x94\x60\x7d\xa0\xa9\xc1\xf3\xb9\x5e\x0d\x2f\x2b\xb2\xe0\x69\xc5\xbb\x4f\xa7\x64\xaf\xfa\x64\x7d\x84\x7b\x7e\xd6",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xf3\xe2\x04\x2f\x94\x60\x7d\xa0\xa9\xc1\xf3\xb9\x5e\x0d\x2f\x2b\xb2\xe0\x69\xc5\xbb\x4f\xa7\x64\xaf\xfa\x64\x7d\x84\x7b\x7e\xd6",
|
||||
//U2F key for Duo
|
||||
.label = "duosecurity.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x31\x19\x33\x28\xf8\xe2\x1d\xfb\x6c\x99\xf3\x22\xd2\x2d\x7b\x0b\x50\x87\x78\xe6\x4f\xfb\xba\x86\xe5\x22\x93\x37\x90\x31\xb8\x74",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x31\x19\x33\x28\xf8\xe2\x1d\xfb\x6c\x99\xf3\x22\xd2\x2d\x7b\x0b\x50\x87\x78\xe6\x4f\xfb\xba\x86\xe5\x22\x93\x37\x90\x31\xb8\x74",
|
||||
//WebAuthn key for Facebook
|
||||
.label = "facebook.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x69\x66\xab\xe3\x67\x4e\xa2\xf5\x30\x79\xeb\x71\x01\x97\x84\x8c\x9b\xe6\xf3\x63\x99\x2f\xd0\x29\xe9\x89\x84\x47\xcb\x9f\x00\x84",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x69\x66\xab\xe3\x67\x4e\xa2\xf5\x30\x79\xeb\x71\x01\x97\x84\x8c\x9b\xe6\xf3\x63\x99\x2f\xd0\x29\xe9\x89\x84\x47\xcb\x9f\x00\x84",
|
||||
//U2F key for FastMail
|
||||
.label = "www.fastmail.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x3f\xcb\x82\x82\xb8\x46\x76\xeb\xee\x71\x40\xe3\x9e\xca\xe1\x6e\xeb\x19\x90\x64\xc7\xc7\xe4\x43\x2e\x28\xc9\xb5\x7e\x4b\x60\x39",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x3f\xcb\x82\x82\xb8\x46\x76\xeb\xee\x71\x40\xe3\x9e\xca\xe1\x6e\xeb\x19\x90\x64\xc7\xc7\xe4\x43\x2e\x28\xc9\xb5\x7e\x4b\x60\x39",
|
||||
.label = "fastmail.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x9d\x61\x44\x2f\x5c\xe1\x33\xbd\x46\x54\x4f\xc4\x2f\x0a\x6d\x54\xc0\xde\xb8\x88\x40\xca\xc2\xb6\xae\xfa\x65\x14\xf8\x93\x49\xe9",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x9d\x61\x44\x2f\x5c\xe1\x33\xbd\x46\x54\x4f\xc4\x2f\x0a\x6d\x54\xc0\xde\xb8\x88\x40\xca\xc2\xb6\xae\xfa\x65\x14\xf8\x93\x49\xe9",
|
||||
.label = "fedoraproject.org",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xa4\xe2\x2d\xca\xfe\xa7\xe9\x0e\x12\x89\x50\x11\x39\x89\xfc\x45\x97\x8d\xc9\xfb\x87\x76\x75\x60\x51\x6c\x1c\x69\xdf\xdf\xd1\x96",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xa4\xe2\x2d\xca\xfe\xa7\xe9\x0e\x12\x89\x50\x11\x39\x89\xfc\x45\x97\x8d\xc9\xfb\x87\x76\x75\x60\x51\x6c\x1c\x69\xdf\xdf\xd1\x96",
|
||||
.label = "gandi.net",
|
||||
.use_sign_count = pfalse,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x54\xce\x65\x1e\xd7\x15\xb4\xaa\xa7\x55\xee\xce\xbd\x4e\xa0\x95\x08\x15\xb3\x34\xbd\x07\xd1\x09\x89\x3e\x96\x30\x18\xcd\xdb\xd9",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x54\xce\x65\x1e\xd7\x15\xb4\xaa\xa7\x55\xee\xce\xbd\x4e\xa0\x95\x08\x15\xb3\x34\xbd\x07\xd1\x09\x89\x3e\x96\x30\x18\xcd\xdb\xd9",
|
||||
//WebAuthn key for Gandi
|
||||
.label = "gandi.net",
|
||||
.use_sign_count = pfalse,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x86\x06\xc1\x68\xe5\x1f\xc1\x31\xe5\x46\xad\x57\xa1\x9f\x32\x97\xb1\x1e\x0e\x5c\xe8\x3e\x8e\x89\x31\xb2\x85\x08\x11\xcf\xa8\x81",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x86\x06\xc1\x68\xe5\x1f\xc1\x31\xe5\x46\xad\x57\xa1\x9f\x32\x97\xb1\x1e\x0e\x5c\xe8\x3e\x8e\x89\x31\xb2\x85\x08\x11\xcf\xa8\x81",
|
||||
//WebAuthn key for Gemini
|
||||
.label = "gemini.com",
|
||||
.use_sign_count = pfalse,
|
||||
.use_self_attestation = ptrue,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x70\x61\x7d\xfe\xd0\x65\x86\x3a\xf4\x7c\x15\x55\x6c\x91\x79\x88\x80\x82\x8c\xc4\x07\xfd\xf7\x0a\xe8\x50\x11\x56\x94\x65\xa0\x75",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x70\x61\x7d\xfe\xd0\x65\x86\x3a\xf4\x7c\x15\x55\x6c\x91\x79\x88\x80\x82\x8c\xc4\x07\xfd\xf7\x0a\xe8\x50\x11\x56\x94\x65\xa0\x75",
|
||||
//U2F key for GitHub
|
||||
.label = "github.com",
|
||||
.use_sign_count = ptrue,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x3a\xeb\x00\x24\x60\x38\x1c\x6f\x25\x8e\x83\x95\xd3\x02\x6f\x57\x1f\x0d\x9a\x76\x48\x8d\xcd\x83\x76\x39\xb1\x3a\xed\x31\x65\x60",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x3a\xeb\x00\x24\x60\x38\x1c\x6f\x25\x8e\x83\x95\xd3\x02\x6f\x57\x1f\x0d\x9a\x76\x48\x8d\xcd\x83\x76\x39\xb1\x3a\xed\x31\x65\x60",
|
||||
//WebAuthn key for GitHub
|
||||
.label = "github.com",
|
||||
.use_sign_count = ptrue,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xe7\xbe\x96\xa5\x1b\xd0\x19\x2a\x72\x84\x0d\x2e\x59\x09\xf7\x2b\xa8\x2a\x2f\xe9\x3f\xaa\x62\x4f\x03\x39\x6b\x30\xe4\x94\xc8\x04",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xe7\xbe\x96\xa5\x1b\xd0\x19\x2a\x72\x84\x0d\x2e\x59\x09\xf7\x2b\xa8\x2a\x2f\xe9\x3f\xaa\x62\x4f\x03\x39\x6b\x30\xe4\x94\xc8\x04",
|
||||
//U2F key for GitLab
|
||||
.label = "gitlab.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xa5\x46\x72\xb2\x22\xc4\xcf\x95\xe1\x51\xed\x8d\x4d\x3c\x76\x7a\x6c\xc3\x49\x43\x59\x43\x79\x4e\x88\x4f\x3d\x02\x3a\x82\x29\xfd",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xa5\x46\x72\xb2\x22\xc4\xcf\x95\xe1\x51\xed\x8d\x4d\x3c\x76\x7a\x6c\xc3\x49\x43\x59\x43\x79\x4e\x88\x4f\x3d\x02\x3a\x82\x29\xfd",
|
||||
//U2F key for Google
|
||||
.label = "google.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xd4\xc9\xd9\x02\x73\x26\x27\x1a\x89\xce\x51\xfc\xaf\x32\x8e\xd6\x73\xf1\x7b\xe3\x34\x69\xff\x97\x9e\x8a\xb8\xdd\x50\x1e\x66\x4f",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xd4\xc9\xd9\x02\x73\x26\x27\x1a\x89\xce\x51\xfc\xaf\x32\x8e\xd6\x73\xf1\x7b\xe3\x34\x69\xff\x97\x9e\x8a\xb8\xdd\x50\x1e\x66\x4f",
|
||||
//WebAuthn key for Google
|
||||
.label = "google.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x53\xa1\x5b\xa4\x2a\x7c\x03\x25\xb8\xdb\xee\x28\x96\x34\xa4\x8f\x58\xae\xa3\x24\x66\x45\xd5\xff\x41\x8f\x9b\xb8\x81\x98\x85\xa9",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x53\xa1\x5b\xa4\x2a\x7c\x03\x25\xb8\xdb\xee\x28\x96\x34\xa4\x8f\x58\xae\xa3\x24\x66\x45\xd5\xff\x41\x8f\x9b\xb8\x81\x98\x85\xa9",
|
||||
//U2F key for Keeper
|
||||
.label = "keepersecurity.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xd6\x5f\x00\x5e\xf4\xde\xa9\x32\x0c\x99\x73\x05\x3c\x95\xff\x60\x20\x11\x5d\x5f\xec\x1b\x7f\xee\x41\xa5\x78\xe1\x8d\xf9\xca\x8c",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xd6\x5f\x00\x5e\xf4\xde\xa9\x32\x0c\x99\x73\x05\x3c\x95\xff\x60\x20\x11\x5d\x5f\xec\x1b\x7f\xee\x41\xa5\x78\xe1\x8d\xf9\xca\x8c",
|
||||
//U2F key for Keeper
|
||||
.label = "keepersecurity.eu",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x3f\x37\x50\x85\x33\x2c\xac\x4f\xad\xf9\xe5\xdd\x28\xcd\x54\x69\x8f\xab\x98\x4b\x75\xd9\xc3\x6a\x07\x2c\xb1\x60\x77\x3f\x91\x52",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x3f\x37\x50\x85\x33\x2c\xac\x4f\xad\xf9\xe5\xdd\x28\xcd\x54\x69\x8f\xab\x98\x4b\x75\xd9\xc3\x6a\x07\x2c\xb1\x60\x77\x3f\x91\x52",
|
||||
//WebAuthn key for Kraken
|
||||
.label = "kraken.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xf8\x3f\xc3\xa1\xb2\x89\xa0\xde\xc5\xc1\xc8\xaa\x07\xe9\xb5\xdd\x9c\xbb\x76\xf6\xb2\xf5\x60\x60\x17\x66\x72\x68\xe5\xb9\xc4\x5e",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xf8\x3f\xc3\xa1\xb2\x89\xa0\xde\xc5\xc1\xc8\xaa\x07\xe9\xb5\xdd\x9c\xbb\x76\xf6\xb2\xf5\x60\x60\x17\x66\x72\x68\xe5\xb9\xc4\x5e",
|
||||
//WebAuthn key for login.gov
|
||||
.label = "secure.login.gov",
|
||||
.use_sign_count = pfalse,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x35\x6c\x9e\xd4\xa0\x93\x21\xb9\x69\x5f\x1e\xaf\x91\x82\x03\xf1\xb5\x5f\x68\x9d\xa6\x1f\xbc\x96\x18\x4c\x15\x7d\xda\x68\x0c\x81",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x35\x6c\x9e\xd4\xa0\x93\x21\xb9\x69\x5f\x1e\xaf\x91\x82\x03\xf1\xb5\x5f\x68\x9d\xa6\x1f\xbc\x96\x18\x4c\x15\x7d\xda\x68\x0c\x81",
|
||||
//WebAuthn key for Microsoft
|
||||
.label = "login.microsoft.com",
|
||||
.use_sign_count = pfalse,
|
||||
.use_self_attestation = pfalse,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xab\x2d\xaf\x07\x43\xde\x78\x2a\x70\x18\x9a\x0f\x5e\xfc\x30\x90\x2f\x92\x5b\x9f\x9a\x18\xc5\xd7\x14\x1b\x7b\x12\xf8\xa0\x10\x0c",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xab\x2d\xaf\x07\x43\xde\x78\x2a\x70\x18\x9a\x0f\x5e\xfc\x30\x90\x2f\x92\x5b\x9f\x9a\x18\xc5\xd7\x14\x1b\x7b\x12\xf8\xa0\x10\x0c",
|
||||
//WebAuthn key for mojeID
|
||||
.label = "mojeid.cz",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x85\x71\x01\x36\x1b\x20\xa9\x54\x4c\xdb\x9b\xef\x65\x85\x8b\x6b\xac\x70\x13\x55\x0d\x8f\x84\xf7\xef\xee\x25\x2b\x96\xfa\x7c\x1e",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x85\x71\x01\x36\x1b\x20\xa9\x54\x4c\xdb\x9b\xef\x65\x85\x8b\x6b\xac\x70\x13\x55\x0d\x8f\x84\xf7\xef\xee\x25\x2b\x96\xfa\x7c\x1e",
|
||||
//WebAuthn key for Namecheap
|
||||
.label = "www.namecheap.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x08\xb2\xa3\xd4\x19\x39\xaa\x31\x66\x84\x93\xcb\x36\xcd\xcc\x4f\x16\xc4\xd9\xb4\xc8\x23\x8b\x73\xc2\xf6\x72\xc0\x33\x00\x71\x97",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x08\xb2\xa3\xd4\x19\x39\xaa\x31\x66\x84\x93\xcb\x36\xcd\xcc\x4f\x16\xc4\xd9\xb4\xc8\x23\x8b\x73\xc2\xf6\x72\xc0\x33\x00\x71\x97",
|
||||
//U2F key for Slush Pool
|
||||
.label = "slushpool.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x38\x80\x4f\x2e\xff\x74\xf2\x28\xb7\x41\x51\xc2\x01\xaa\x82\xe7\xe8\xee\xfc\xac\xfe\xcf\x23\xfa\x14\x6b\x13\xa3\x76\x66\x31\x4f",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x38\x80\x4f\x2e\xff\x74\xf2\x28\xb7\x41\x51\xc2\x01\xaa\x82\xe7\xe8\xee\xfc\xac\xfe\xcf\x23\xfa\x14\x6b\x13\xa3\x76\x66\x31\x4f",
|
||||
//U2F key for Slush Pool
|
||||
.label = "slushpool.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x2a\xc6\xad\x09\xa6\xd0\x77\x2c\x44\xda\x73\xa6\x07\x2f\x9d\x24\x0f\xc6\x85\x4a\x70\xd7\x9c\x10\x24\xff\x7c\x75\x59\x59\x32\x92",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x2a\xc6\xad\x09\xa6\xd0\x77\x2c\x44\xda\x73\xa6\x07\x2f\x9d\x24\x0f\xc6\x85\x4a\x70\xd7\x9c\x10\x24\xff\x7c\x75\x59\x59\x32\x92",
|
||||
//U2F key for Stripe
|
||||
.label = "stripe.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xfa\xbe\xec\xe3\x98\x2f\xad\x9d\xdc\xc9\x8f\x91\xbd\x2e\x75\xaf\xc7\xd1\xf4\xca\x54\x49\x29\xb2\xd0\xd0\x42\x12\xdf\xfa\x30\xfa",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xfa\xbe\xec\xe3\x98\x2f\xad\x9d\xdc\xc9\x8f\x91\xbd\x2e\x75\xaf\xc7\xd1\xf4\xca\x54\x49\x29\xb2\xd0\xd0\x42\x12\xdf\xfa\x30\xfa",
|
||||
//U2F key for Tutanota
|
||||
.label = "tutanota.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x1b\x3c\x16\xdd\x2f\x7c\x46\xe2\xb4\xc2\x89\xdc\x16\x74\x6b\xcc\x60\xdf\xcf\x0f\xb8\x18\xe1\x32\x15\x52\x6e\x14\x08\xe7\xf4\x68",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x1b\x3c\x16\xdd\x2f\x7c\x46\xe2\xb4\xc2\x89\xdc\x16\x74\x6b\xcc\x60\xdf\xcf\x0f\xb8\x18\xe1\x32\x15\x52\x6e\x14\x08\xe7\xf4\x68",
|
||||
//U2F key for u2f.bin.coffee
|
||||
.label = "u2f.bin.coffee",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xa6\x42\xd2\x1b\x7c\x6d\x55\xe1\xce\x23\xc5\x39\x98\x28\xd2\xc7\x49\xbf\x6a\x6e\xf2\xfe\x03\xcc\x9e\x10\xcd\xf4\xed\x53\x08\x8b",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xa6\x42\xd2\x1b\x7c\x6d\x55\xe1\xce\x23\xc5\x39\x98\x28\xd2\xc7\x49\xbf\x6a\x6e\xf2\xfe\x03\xcc\x9e\x10\xcd\xf4\xed\x53\x08\x8b",
|
||||
//WebAuthn key for webauthn.bin.coffee
|
||||
.label = "webauthn.bin.coffee",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x74\xa6\xea\x92\x13\xc9\x9c\x2f\x74\xb2\x24\x92\xb3\x20\xcf\x40\x26\x2a\x94\xc1\xa9\x50\xa0\x39\x7f\x29\x25\x0b\x60\x84\x1e\xf0",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x74\xa6\xea\x92\x13\xc9\x9c\x2f\x74\xb2\x24\x92\xb3\x20\xcf\x40\x26\x2a\x94\xc1\xa9\x50\xa0\x39\x7f\x29\x25\x0b\x60\x84\x1e\xf0",
|
||||
//WebAuthn key for WebAuthn.io
|
||||
.label = "webauthn.io",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xf9\x5b\xc7\x38\x28\xee\x21\x0f\x9f\xd3\xbb\xe7\x2d\x97\x90\x80\x13\xb0\xa3\x75\x9e\x9a\xea\x3d\x0a\xe3\x18\x76\x6c\xd2\xe1\xad",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xf9\x5b\xc7\x38\x28\xee\x21\x0f\x9f\xd3\xbb\xe7\x2d\x97\x90\x80\x13\xb0\xa3\x75\x9e\x9a\xea\x3d\x0a\xe3\x18\x76\x6c\xd2\xe1\xad",
|
||||
//WebAuthn key for WebAuthn.me
|
||||
.label = "webauthn.me",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xc4\x6c\xef\x82\xad\x1b\x54\x64\x77\x59\x1d\x00\x8b\x08\x75\x9e\xc3\xe6\xd2\xec\xb4\xf3\x94\x74\xbf\xea\x69\x69\x92\x5d\x03\xb7",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xc4\x6c\xef\x82\xad\x1b\x54\x64\x77\x59\x1d\x00\x8b\x08\x75\x9e\xc3\xe6\xd2\xec\xb4\xf3\x94\x74\xbf\xea\x69\x69\x92\x5d\x03\xb7",
|
||||
//WebAuthn key for demo.yubico.com
|
||||
.label = "demo.yubico.com",
|
||||
.use_sign_count = NULL,
|
||||
@@ -304,8 +383,9 @@ static const known_app_t kapps[] = {
|
||||
|
||||
const known_app_t *find_app_by_rp_id_hash(const uint8_t *rp_id_hash) {
|
||||
for (const known_app_t *ka = &kapps[0]; ka->rp_id_hash != NULL; ka++) {
|
||||
if (memcmp(rp_id_hash, ka->rp_id_hash, 32) == 0)
|
||||
if (memcmp(rp_id_hash, ka->rp_id_hash, 32) == 0) {
|
||||
return ka;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
515
src/fido/oath.c
Normal file
515
src/fido/oath.c
Normal file
@@ -0,0 +1,515 @@
|
||||
/*
|
||||
* 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 "hsm.h"
|
||||
#include "apdu.h"
|
||||
#include "files.h"
|
||||
#include "random.h"
|
||||
#include "version.h"
|
||||
#include "asn1.h"
|
||||
|
||||
#define MAX_OATH_CRED 255
|
||||
#define CHALLENGE_LEN 8
|
||||
|
||||
#define TAG_NAME 0x71
|
||||
#define TAG_NAME_LIST 0x72
|
||||
#define TAG_KEY 0x73
|
||||
#define TAG_CHALLENGE 0x74
|
||||
#define TAG_RESPONSE 0x75
|
||||
#define TAG_T_RESPONSE 0x76
|
||||
#define TAG_NO_RESPONSE 0x77
|
||||
#define TAG_PROPERTY 0x78
|
||||
#define TAG_VERSION 0x79
|
||||
#define TAG_IMF 0x7a
|
||||
#define TAG_ALGO 0x7b
|
||||
#define TAG_TOUCH_RESPONSE 0x7c
|
||||
|
||||
#define ALG_HMAC_SHA1 0x01
|
||||
#define ALG_HMAC_SHA256 0x02
|
||||
#define ALG_HMAC_SHA512 0x03
|
||||
#define ALG_MASK 0x0f
|
||||
|
||||
#define OATH_TYPE_HOTP 0x10
|
||||
#define OATH_TYPE_TOTP 0x20
|
||||
#define OATH_TYPE_MASK 0xf0
|
||||
|
||||
#define PROP_INC 0x01
|
||||
#define PROP_TOUCH 0x02
|
||||
|
||||
int oath_process_apdu();
|
||||
int oath_unload();
|
||||
|
||||
static bool validated = true;
|
||||
static uint8_t challenge[CHALLENGE_LEN] = { 0 };
|
||||
|
||||
const uint8_t oath_aid[] = {
|
||||
7,
|
||||
0xa0, 0x00, 0x00, 0x05, 0x27, 0x21, 0x01
|
||||
};
|
||||
|
||||
app_t *oath_select(app_t *a, const uint8_t *aid, uint8_t aid_len) {
|
||||
if (!memcmp(aid, oath_aid + 1, MIN(aid_len, oath_aid[0]))) {
|
||||
a->aid = oath_aid;
|
||||
a->process_apdu = oath_process_apdu;
|
||||
a->unload = oath_unload;
|
||||
res_APDU_size = 0;
|
||||
res_APDU[res_APDU_size++] = TAG_VERSION;
|
||||
res_APDU[res_APDU_size++] = 3;
|
||||
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_NAME;
|
||||
res_APDU[res_APDU_size++] = 8;
|
||||
#ifndef ENABLE_EMULATION
|
||||
pico_get_unique_board_id((pico_unique_board_id_t *) (res_APDU + res_APDU_size));
|
||||
res_APDU_size += 8;
|
||||
#else
|
||||
memset(res_APDU + res_APDU_size, 0, 8); res_APDU_size += 8;
|
||||
#endif
|
||||
if (file_has_data(search_dynamic_file(EF_OATH_CODE)) == true) {
|
||||
res_APDU[res_APDU_size++] = TAG_CHALLENGE;
|
||||
res_APDU[res_APDU_size++] = sizeof(challenge);
|
||||
memcpy(res_APDU + res_APDU_size, challenge, sizeof(challenge));
|
||||
res_APDU_size += sizeof(challenge);
|
||||
}
|
||||
apdu.ne = res_APDU_size;
|
||||
return a;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void __attribute__((constructor)) oath_ctor() {
|
||||
register_app(oath_select);
|
||||
}
|
||||
|
||||
int oath_unload() {
|
||||
return CCID_OK;
|
||||
}
|
||||
|
||||
file_t *find_oath_cred(const uint8_t *name, size_t name_len) {
|
||||
size_t ef_tag_len = 0;
|
||||
uint8_t *ef_tag_data = NULL;
|
||||
for (int i = 0; i < MAX_OATH_CRED; i++) {
|
||||
file_t *ef = search_dynamic_file(EF_OATH_CRED + i);
|
||||
if (file_has_data(ef) &&
|
||||
asn1_find_tag(file_get_data(ef), file_get_size(ef), TAG_NAME, &ef_tag_len,
|
||||
&ef_tag_data) == true && ef_tag_len == name_len &&
|
||||
memcmp(ef_tag_data, name, name_len) == 0) {
|
||||
return ef;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int cmd_put() {
|
||||
if (validated == false) {
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
size_t key_len = 0, imf_len = 0, name_len = 0;
|
||||
uint8_t *key = NULL, *imf = NULL, *name = NULL;
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_KEY, &key_len, &key) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_NAME, &name_len, &name) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
if ((key[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_IMF, &imf_len, &imf) == false) {
|
||||
memcpy(apdu.data + apdu.nc, "\x7a\x08\x00\x00\x00\x00\x00\x00\x00\x00", 10);
|
||||
apdu.nc += 10;
|
||||
}
|
||||
else { //prepend zero-valued bytes
|
||||
if (imf_len < 8) {
|
||||
memmove(imf + (8 - imf_len), imf, imf_len);
|
||||
memset(imf, 0, 8 - imf_len);
|
||||
*(imf - 1) = 8;
|
||||
apdu.nc += (8 - imf_len);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
file_t *ef = find_oath_cred(name, name_len);
|
||||
if (file_has_data(ef)) {
|
||||
flash_write_data_to_file(ef, apdu.data, apdu.nc);
|
||||
low_flash_available();
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < MAX_OATH_CRED; i++) {
|
||||
file_t *ef = search_dynamic_file(EF_OATH_CRED + i);
|
||||
if (!file_has_data(ef)) {
|
||||
ef = file_new(EF_OATH_CRED + i);
|
||||
flash_write_data_to_file(ef, apdu.data, apdu.nc);
|
||||
low_flash_available();
|
||||
return SW_OK();
|
||||
}
|
||||
}
|
||||
return SW_FILE_FULL();
|
||||
}
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
|
||||
int cmd_delete() {
|
||||
size_t tag_len = 0;
|
||||
uint8_t *tag_data = NULL;
|
||||
if (validated == false) {
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_NAME, &tag_len, &tag_data) == true) {
|
||||
file_t *ef = find_oath_cred(tag_data, tag_len);
|
||||
if (ef) {
|
||||
delete_file(ef);
|
||||
return SW_OK();
|
||||
}
|
||||
return SW_DATA_INVALID();
|
||||
}
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
|
||||
const mbedtls_md_info_t *get_oath_md_info(uint8_t alg) {
|
||||
if ((alg & ALG_MASK) == ALG_HMAC_SHA1) {
|
||||
return mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
|
||||
}
|
||||
else if ((alg & ALG_MASK) == ALG_HMAC_SHA256) {
|
||||
return mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
|
||||
}
|
||||
else if ((alg & ALG_MASK) == ALG_HMAC_SHA512) {
|
||||
return mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int cmd_set_code() {
|
||||
if (validated == false) {
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
if (apdu.nc == 0) {
|
||||
delete_file(search_dynamic_file(EF_OATH_CODE));
|
||||
validated = true;
|
||||
return SW_OK();
|
||||
}
|
||||
size_t key_len = 0, chal_len = 0, resp_len = 0;
|
||||
uint8_t *key = NULL, *chal = NULL, *resp = NULL;
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_KEY, &key_len, &key) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
if (key_len == 0) {
|
||||
delete_file(search_dynamic_file(EF_OATH_CODE));
|
||||
validated = true;
|
||||
return SW_OK();
|
||||
}
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_CHALLENGE, &chal_len, &chal) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_RESPONSE, &resp_len, &resp) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
|
||||
const mbedtls_md_info_t *md_info = get_oath_md_info(key[0]);
|
||||
if (md_info == NULL) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
uint8_t hmac[64];
|
||||
int r = mbedtls_md_hmac(md_info, key + 1, key_len - 1, chal, chal_len, hmac);
|
||||
if (r != 0) {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
if (memcmp(hmac, resp, resp_len) != 0) {
|
||||
return SW_DATA_INVALID();
|
||||
}
|
||||
random_gen(NULL, challenge, sizeof(challenge));
|
||||
file_t *ef = file_new(EF_OATH_CODE);
|
||||
flash_write_data_to_file(ef, key, key_len);
|
||||
low_flash_available();
|
||||
validated = false;
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int cmd_reset() {
|
||||
if (P1(apdu) != 0xde || P2(apdu) != 0xad) {
|
||||
return SW_INCORRECT_P1P2();
|
||||
}
|
||||
for (int i = 0; i < MAX_OATH_CRED; i++) {
|
||||
file_t *ef = search_dynamic_file(EF_OATH_CRED + i);
|
||||
if (file_has_data(ef)) {
|
||||
delete_file(ef);
|
||||
}
|
||||
}
|
||||
delete_file(search_dynamic_file(EF_OATH_CODE));
|
||||
validated = true;
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int cmd_list() {
|
||||
size_t name_len = 0, key_len = 0;
|
||||
uint8_t *name = NULL, *key = NULL;
|
||||
if (validated == false) {
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
for (int i = 0; i < MAX_OATH_CRED; i++) {
|
||||
file_t *ef = search_dynamic_file(EF_OATH_CRED + i);
|
||||
if (file_has_data(ef)) {
|
||||
uint8_t *data = file_get_data(ef);
|
||||
size_t data_len = file_get_size(ef);
|
||||
if (asn1_find_tag(data, data_len, TAG_NAME, &name_len,
|
||||
&name) == true &&
|
||||
asn1_find_tag(data, data_len, TAG_KEY, &key_len, &key) == true) {
|
||||
res_APDU[res_APDU_size++] = TAG_NAME_LIST;
|
||||
res_APDU[res_APDU_size++] = name_len + 1;
|
||||
res_APDU[res_APDU_size++] = key[0];
|
||||
memcpy(res_APDU + res_APDU_size, name, name_len); res_APDU_size += name_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
apdu.ne = res_APDU_size;
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int cmd_validate() {
|
||||
size_t chal_len = 0, resp_len = 0, key_len = 0;
|
||||
uint8_t *chal = NULL, *resp = NULL, *key = NULL;
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_CHALLENGE, &chal_len, &chal) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_RESPONSE, &resp_len, &resp) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
file_t *ef = search_dynamic_file(EF_OATH_CODE);
|
||||
if (file_has_data(ef) == false) {
|
||||
validated = true;
|
||||
return SW_DATA_INVALID();
|
||||
}
|
||||
key = file_get_data(ef);
|
||||
key_len = file_get_size(ef);
|
||||
const mbedtls_md_info_t *md_info = get_oath_md_info(key[0]);
|
||||
if (md_info == NULL) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
uint8_t hmac[64];
|
||||
int ret = mbedtls_md_hmac(md_info, key + 1, key_len - 1, challenge, sizeof(challenge), hmac);
|
||||
if (ret != 0) {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
if (memcmp(hmac, resp, resp_len) != 0) {
|
||||
return SW_DATA_INVALID();
|
||||
}
|
||||
ret = mbedtls_md_hmac(md_info, key + 1, key_len - 1, chal, chal_len, hmac);
|
||||
if (ret != 0) {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
validated = true;
|
||||
res_APDU[res_APDU_size++] = TAG_RESPONSE;
|
||||
res_APDU[res_APDU_size++] = mbedtls_md_get_size(md_info);
|
||||
memcpy(res_APDU + res_APDU_size, hmac, mbedtls_md_get_size(md_info));
|
||||
res_APDU_size += mbedtls_md_get_size(md_info);
|
||||
apdu.ne = res_APDU_size;
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int calculate_oath(uint8_t truncate,
|
||||
const uint8_t *key,
|
||||
size_t key_len,
|
||||
const uint8_t *chal,
|
||||
size_t chal_len) {
|
||||
const mbedtls_md_info_t *md_info = get_oath_md_info(key[0]);
|
||||
if (md_info == NULL) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
uint8_t hmac[64];
|
||||
int r = mbedtls_md_hmac(md_info, key + 2, key_len - 2, chal, chal_len, hmac);
|
||||
size_t hmac_size = mbedtls_md_get_size(md_info);
|
||||
if (r != 0) {
|
||||
return CCID_EXEC_ERROR;
|
||||
}
|
||||
if (truncate == 0x01) {
|
||||
res_APDU[res_APDU_size++] = 4 + 1;
|
||||
res_APDU[res_APDU_size++] = key[1];
|
||||
uint8_t offset = hmac[hmac_size - 1] & 0x0f;
|
||||
res_APDU[res_APDU_size++] = hmac[offset] & 0x7f;
|
||||
res_APDU[res_APDU_size++] = hmac[offset + 1];
|
||||
res_APDU[res_APDU_size++] = hmac[offset + 2];
|
||||
res_APDU[res_APDU_size++] = hmac[offset + 3];
|
||||
}
|
||||
else {
|
||||
res_APDU[res_APDU_size++] = hmac_size + 1;
|
||||
res_APDU[res_APDU_size++] = key[1];
|
||||
memcpy(res_APDU + res_APDU_size, hmac, hmac_size); res_APDU_size += hmac_size;
|
||||
}
|
||||
apdu.ne = res_APDU_size;
|
||||
return CCID_OK;
|
||||
}
|
||||
|
||||
int cmd_calculate() {
|
||||
size_t chal_len = 0, name_len = 0, key_len = 0;
|
||||
uint8_t *chal = NULL, *name = NULL, *key = NULL;
|
||||
if (P2(apdu) != 0x0 && P2(apdu) != 0x1) {
|
||||
return SW_INCORRECT_P1P2();
|
||||
}
|
||||
if (validated == false) {
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_CHALLENGE, &chal_len, &chal) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_NAME, &name_len, &name) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
file_t *ef = find_oath_cred(name, name_len);
|
||||
if (file_has_data(ef) == false) {
|
||||
return SW_DATA_INVALID();
|
||||
}
|
||||
|
||||
if (asn1_find_tag(file_get_data(ef), file_get_size(ef), TAG_KEY, &key_len, &key) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
|
||||
if ((key[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
|
||||
if (asn1_find_tag(file_get_data(ef), file_get_size(ef), TAG_IMF, &chal_len,
|
||||
&chal) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
}
|
||||
|
||||
res_APDU[res_APDU_size++] = TAG_RESPONSE + P2(apdu);
|
||||
|
||||
int ret = calculate_oath(P2(apdu), key, key_len, chal, chal_len);
|
||||
if (ret != CCID_OK) {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
if ((key[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
|
||||
uint64_t v =
|
||||
((uint64_t) chal[0] <<
|
||||
56) |
|
||||
((uint64_t) chal[1] <<
|
||||
48) |
|
||||
((uint64_t) chal[2] <<
|
||||
40) |
|
||||
((uint64_t) chal[3] <<
|
||||
32) |
|
||||
((uint64_t) chal[4] <<
|
||||
24) | ((uint64_t) chal[5] << 16) | ((uint64_t) chal[6] << 8) | (uint64_t) chal[7];
|
||||
size_t ef_size = file_get_size(ef);
|
||||
v++;
|
||||
uint8_t *tmp = (uint8_t *) calloc(1, ef_size);
|
||||
memcpy(tmp, file_get_data(ef), ef_size);
|
||||
asn1_find_tag(tmp, ef_size, TAG_IMF, &chal_len, &chal);
|
||||
chal[0] = v >> 56;
|
||||
chal[1] = v >> 48;
|
||||
chal[2] = v >> 40;
|
||||
chal[3] = v >> 32;
|
||||
chal[4] = v >> 24;
|
||||
chal[5] = v >> 16;
|
||||
chal[6] = v >> 8;
|
||||
chal[7] = v & 0xff;
|
||||
flash_write_data_to_file(ef, tmp, ef_size);
|
||||
low_flash_available();
|
||||
free(tmp);
|
||||
}
|
||||
apdu.ne = res_APDU_size;
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int cmd_calculate_all() {
|
||||
size_t chal_len = 0, name_len = 0, key_len = 0, prop_len = 0;
|
||||
uint8_t *chal = NULL, *name = NULL, *key = NULL, *prop = NULL;
|
||||
if (P2(apdu) != 0x0 && P2(apdu) != 0x1) {
|
||||
return SW_INCORRECT_P1P2();
|
||||
}
|
||||
if (validated == false) {
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
if (asn1_find_tag(apdu.data, apdu.nc, TAG_CHALLENGE, &chal_len, &chal) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
for (int i = 0; i < MAX_OATH_CRED; i++) {
|
||||
file_t *ef = search_dynamic_file(EF_OATH_CRED + i);
|
||||
if (file_has_data(ef)) {
|
||||
const uint8_t *ef_data = file_get_data(ef);
|
||||
size_t ef_len = file_get_size(ef);
|
||||
if (asn1_find_tag(ef_data, ef_len, TAG_NAME, &name_len,
|
||||
&name) == false ||
|
||||
asn1_find_tag(ef_data, ef_len, TAG_KEY, &key_len, &key) == false) {
|
||||
continue;
|
||||
}
|
||||
res_APDU[res_APDU_size++] = TAG_NAME;
|
||||
res_APDU[res_APDU_size++] = name_len;
|
||||
memcpy(res_APDU + res_APDU_size, name, name_len); res_APDU_size += name_len;
|
||||
if ((key[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
|
||||
res_APDU[res_APDU_size++] = TAG_NO_RESPONSE;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = key[1];
|
||||
}
|
||||
else if (asn1_find_tag(ef_data, ef_len, TAG_PROPERTY, &prop_len,
|
||||
&prop) == true && (prop[0] & PROP_TOUCH)) {
|
||||
res_APDU[res_APDU_size++] = TAG_TOUCH_RESPONSE;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = key[1];
|
||||
}
|
||||
else {
|
||||
res_APDU[res_APDU_size++] = TAG_RESPONSE + P2(apdu);
|
||||
int ret = calculate_oath(P2(apdu), key, key_len, chal, chal_len);
|
||||
if (ret != CCID_OK) {
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = key[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
apdu.ne = res_APDU_size;
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int cmd_send_remaining() {
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
#define INS_PUT 0x01
|
||||
#define INS_DELETE 0x02
|
||||
#define INS_SET_CODE 0x03
|
||||
#define INS_RESET 0x04
|
||||
#define INS_LIST 0xa1
|
||||
#define INS_CALCULATE 0xa2
|
||||
#define INS_VALIDATE 0xa3
|
||||
#define INS_CALC_ALL 0xa4
|
||||
#define INS_SEND_REMAINING 0xa5
|
||||
|
||||
static const cmd_t cmds[] = {
|
||||
{ INS_PUT, cmd_put },
|
||||
{ INS_DELETE, cmd_delete },
|
||||
{ INS_SET_CODE, cmd_set_code },
|
||||
{ INS_RESET, cmd_reset },
|
||||
{ INS_LIST, cmd_list },
|
||||
{ INS_VALIDATE, cmd_validate },
|
||||
{ INS_CALCULATE, cmd_calculate },
|
||||
{ INS_CALC_ALL, cmd_calculate_all },
|
||||
{ INS_SEND_REMAINING, cmd_send_remaining },
|
||||
{ 0x00, 0x0 }
|
||||
};
|
||||
|
||||
int oath_process_apdu() {
|
||||
if (CLA(apdu) != 0x00) {
|
||||
return SW_CLA_NOT_SUPPORTED();
|
||||
}
|
||||
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
|
||||
if (cmd->ins == INS(apdu)) {
|
||||
int r = cmd->cmd_handler();
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return SW_INS_NOT_SUPPORTED();
|
||||
}
|
||||
176
src/fido/otp.c
Normal file
176
src/fido/otp.c
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* 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 "hsm.h"
|
||||
#include "apdu.h"
|
||||
#include "files.h"
|
||||
#include "random.h"
|
||||
#include "version.h"
|
||||
#include "asn1.h"
|
||||
|
||||
#define FIXED_SIZE 16
|
||||
#define KEY_SIZE 16
|
||||
#define UID_SIZE 6
|
||||
#define KEY_SIZE_OATH 20
|
||||
#define ACC_CODE_SIZE 6
|
||||
|
||||
#define CONFIG1_VALID 0x01
|
||||
#define CONFIG2_VALID 0x02
|
||||
#define CONFIG1_TOUCH 0x04
|
||||
#define CONFIG2_TOUCH 0x08
|
||||
#define CONFIG_LED_INV 0x10
|
||||
#define CONFIG_STATUS_MASK 0x1f
|
||||
|
||||
static uint8_t config_seq = { 1 };
|
||||
|
||||
typedef struct otp_config {
|
||||
uint8_t fixed_data[FIXED_SIZE];
|
||||
uint8_t uid[UID_SIZE];
|
||||
uint8_t aes_key[KEY_SIZE];
|
||||
uint8_t acc_code[ACC_CODE_SIZE];
|
||||
uint8_t fixed_size;
|
||||
uint8_t ext_flags;
|
||||
uint8_t tkt_flags;
|
||||
uint8_t cfg_flags;
|
||||
uint8_t rfu[2];
|
||||
uint16_t crc;
|
||||
} __attribute__((packed)) otp_config_t;
|
||||
|
||||
static const size_t otp_config_size = sizeof(otp_config_t);
|
||||
uint16_t otp_status();
|
||||
|
||||
int otp_process_apdu();
|
||||
int otp_unload();
|
||||
|
||||
const uint8_t otp_aid[] = {
|
||||
7,
|
||||
0xa0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01
|
||||
};
|
||||
|
||||
app_t *otp_select(app_t *a, const uint8_t *aid, uint8_t aid_len) {
|
||||
if (!memcmp(aid, otp_aid + 1, MIN(aid_len, otp_aid[0]))) {
|
||||
a->aid = otp_aid;
|
||||
a->process_apdu = otp_process_apdu;
|
||||
a->unload = otp_unload;
|
||||
if (file_has_data(search_dynamic_file(EF_OTP_SLOT1)) ||
|
||||
file_has_data(search_dynamic_file(EF_OTP_SLOT2))) {
|
||||
config_seq = 1;
|
||||
}
|
||||
else {
|
||||
config_seq = 0;
|
||||
}
|
||||
otp_status();
|
||||
apdu.ne = res_APDU_size;
|
||||
return a;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void __attribute__((constructor)) otp_ctor() {
|
||||
register_app(otp_select);
|
||||
}
|
||||
|
||||
int otp_unload() {
|
||||
return CCID_OK;
|
||||
}
|
||||
|
||||
uint16_t otp_status() {
|
||||
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++] = 0;
|
||||
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);
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int cmd_otp() {
|
||||
uint8_t p1 = P1(apdu), p2 = P2(apdu);
|
||||
if (p2 != 0x00) {
|
||||
return SW_INCORRECT_P1P2();
|
||||
}
|
||||
if (p1 == 0x01 || p1 == 0x03) { // Configure slot
|
||||
if (apdu.nc != otp_config_size + ACC_CODE_SIZE) {
|
||||
return SW_WRONG_LENGTH();
|
||||
}
|
||||
if (apdu.data[48] != 0 || apdu.data[49] != 0) {
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
file_t *ef = file_new(p1 == 0x01 ? EF_OTP_SLOT1 : EF_OTP_SLOT2);
|
||||
if (file_has_data(ef)) {
|
||||
otp_config_t *otpc = (otp_config_t *) file_get_data(ef);
|
||||
if (memcmp(otpc->acc_code, apdu.data + otp_config_size, ACC_CODE_SIZE) != 0) {
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
}
|
||||
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);
|
||||
low_flash_available();
|
||||
config_seq++;
|
||||
return otp_status();
|
||||
}
|
||||
}
|
||||
// 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();
|
||||
}
|
||||
else if (p1 == 0x10) {
|
||||
#ifndef ENABLE_EMULATION
|
||||
pico_get_unique_board_id_string((char *) res_APDU, 4);
|
||||
#endif
|
||||
res_APDU_size = 4;
|
||||
}
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
#define INS_OTP 0x01
|
||||
#define INS_DELETE 0x02
|
||||
#define INS_SET_CODE 0x03
|
||||
#define INS_RESET 0x04
|
||||
#define INS_LIST 0xa1
|
||||
#define INS_CALCULATE 0xa2
|
||||
#define INS_VALIDATE 0xa3
|
||||
#define INS_CALC_ALL 0xa4
|
||||
#define INS_SEND_REMAINING 0xa5
|
||||
|
||||
static const cmd_t cmds[] = {
|
||||
{ INS_OTP, cmd_otp },
|
||||
{ 0x00, 0x0 }
|
||||
};
|
||||
|
||||
int otp_process_apdu() {
|
||||
if (CLA(apdu) != 0x00) {
|
||||
return SW_CLA_NOT_SUPPORTED();
|
||||
}
|
||||
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
|
||||
if (cmd->ins == INS(apdu)) {
|
||||
int r = cmd->cmd_handler();
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return SW_INS_NOT_SUPPORTED();
|
||||
}
|
||||
@@ -18,10 +18,9 @@
|
||||
#ifndef __VERSION_H_
|
||||
#define __VERSION_H_
|
||||
|
||||
#define PICO_FIDO_VERSION 0x020A
|
||||
#define PICO_FIDO_VERSION 0x0300
|
||||
|
||||
#define PICO_FIDO_VERSION_MAJOR ((PICO_FIDO_VERSION >> 8) & 0xff)
|
||||
#define PICO_FIDO_VERSION_MINOR (PICO_FIDO_VERSION & 0xff)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
7
tests/build-in-docker.sh
Executable file
7
tests/build-in-docker.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash -eu
|
||||
|
||||
source tests/docker_env.sh
|
||||
#run_in_docker rm -rf CMakeFiles
|
||||
run_in_docker mkdir -p build_in_docker
|
||||
run_in_docker -w "$PWD/build_in_docker" cmake -DENABLE_EMULATION=1 ..
|
||||
run_in_docker -w "$PWD/build_in_docker" make -j ${NUM_PROC}
|
||||
@@ -1,3 +1,23 @@
|
||||
"""
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
from http import client
|
||||
from fido2.hid import CtapHidDevice
|
||||
from fido2.client import Fido2Client, WindowsClient, UserInteraction, ClientError, _Ctap1ClientBackend
|
||||
@@ -12,6 +32,8 @@ import sys
|
||||
import pytest
|
||||
import os
|
||||
import struct
|
||||
from inputimeout import inputimeout
|
||||
|
||||
|
||||
DEFAULT_PIN='12345678'
|
||||
|
||||
@@ -159,7 +181,11 @@ class Device():
|
||||
|
||||
def reboot(self):
|
||||
print("Please reboot authenticator and hit enter")
|
||||
input()
|
||||
try:
|
||||
inputimeout(prompt='>>', timeout=5)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self.__set_client(self.__origin, self.__user_interaction, self.__uv)
|
||||
self.__set_server(rp=self.__rp, attestation=self.__attestation)
|
||||
|
||||
@@ -335,20 +361,20 @@ def device():
|
||||
dev = Device()
|
||||
return dev
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
@pytest.fixture(scope="module")
|
||||
def info(device):
|
||||
return device.client()._backend.info
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
@pytest.fixture(scope="module")
|
||||
def MCRes(device, *args):
|
||||
return device.doMC(*args)
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
@pytest.fixture(scope="module")
|
||||
def resetdevice(device):
|
||||
device.reset()
|
||||
return device
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
@pytest.fixture(scope="module")
|
||||
def GARes(device, MCRes, *args):
|
||||
res = device.doGA(allow_list=[
|
||||
{"id": MCRes['res'].attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
|
||||
@@ -359,11 +385,11 @@ def GARes(device, MCRes, *args):
|
||||
verify(MCRes['res'].attestation_object, a, res['req']['client_data'].hash)
|
||||
return res
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
@pytest.fixture(scope="module")
|
||||
def MCRes_DC(device, *args):
|
||||
return device.doMC(rk=True, *args)
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
@pytest.fixture(scope="module")
|
||||
def GARes_DC(device, MCRes_DC, *args):
|
||||
res = device.GA(allow_list=[
|
||||
{"id": MCRes_DC['res'].attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
|
||||
@@ -380,7 +406,7 @@ def RegRes(resetdevice, *args):
|
||||
return res
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def AuthRes(device, RegRes, *args):
|
||||
res = device.doGA(ctap1=True, allow_list=[
|
||||
{"id": RegRes['res'].attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
|
||||
@@ -393,3 +419,30 @@ def AuthRes(device, RegRes, *args):
|
||||
@pytest.fixture(scope="class")
|
||||
def client_pin(resetdevice):
|
||||
return ClientPin(resetdevice.client()._backend.ctap2)
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
def ccid_card():
|
||||
cardtype = AnyCardType()
|
||||
try:
|
||||
# request card insertion
|
||||
cardrequest = CardRequest(timeout=10, cardType=cardtype)
|
||||
card = cardrequest.waitforcard()
|
||||
|
||||
# connect to the card and perform a few transmits
|
||||
card.connection.connect()
|
||||
return card
|
||||
|
||||
except CardRequestTimeoutException:
|
||||
print('time-out: no card inserted during last 10s')
|
||||
return None
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
def select_oath(ccid_card):
|
||||
aid = [0xa0, 0x00, 0x00, 0x05, 0x27, 0x21, 0x01, 0x01]
|
||||
resp = send_apdu(ccid_card, 0xA4, 0x04, 0x00, aid)
|
||||
return ccid_card
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
def reset_oath(select_oath):
|
||||
send_apdu(select_oath, 0x04, p1=0xde, p2=0xad)
|
||||
return select_oath
|
||||
|
||||
31
tests/docker/bullseye/Dockerfile
Normal file
31
tests/docker/bullseye/Dockerfile
Normal file
@@ -0,0 +1,31 @@
|
||||
FROM debian:bullseye
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt update && apt upgrade -y
|
||||
RUN apt install -y apt-utils
|
||||
RUN apt install -y libccid \
|
||||
libpcsclite-dev \
|
||||
git \
|
||||
autoconf \
|
||||
pkg-config \
|
||||
libtool \
|
||||
help2man \
|
||||
automake \
|
||||
gcc \
|
||||
make \
|
||||
build-essential \
|
||||
opensc \
|
||||
python3 \
|
||||
python3-pip \
|
||||
swig \
|
||||
cmake \
|
||||
libfuse-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
RUN pip3 install pytest pycvc cryptography pyscard fido2 inputimeout
|
||||
RUN git clone https://github.com/frankmorgner/vsmartcard.git
|
||||
WORKDIR /vsmartcard/virtualsmartcard
|
||||
RUN autoreconf --verbose --install
|
||||
RUN ./configure --sysconfdir=/etc
|
||||
RUN make && make install
|
||||
WORKDIR /
|
||||
269
tests/docker/fido2/__init__.py
Normal file
269
tests/docker/fido2/__init__.py
Normal file
@@ -0,0 +1,269 @@
|
||||
# Copyright (c) 2020 Yubico AB
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or
|
||||
# without modification, are permitted provided that the following
|
||||
# conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following
|
||||
# disclaimer in the documentation and/or other materials provided
|
||||
# with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from .base import HidDescriptor
|
||||
from ..ctap import CtapDevice, CtapError, STATUS
|
||||
from ..utils import LOG_LEVEL_TRAFFIC
|
||||
from threading import Event
|
||||
from enum import IntEnum, IntFlag, unique
|
||||
from typing import Tuple, Optional, Callable, Iterator
|
||||
import struct
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
if sys.platform.startswith("linux"):
|
||||
from . import linux as backend
|
||||
elif sys.platform.startswith("win32"):
|
||||
from . import windows as backend
|
||||
elif sys.platform.startswith("darwin"):
|
||||
from . import macos as backend
|
||||
elif sys.platform.startswith("freebsd"):
|
||||
from . import freebsd as backend
|
||||
elif sys.platform.startswith("openbsd"):
|
||||
from . import openbsd as backend
|
||||
else:
|
||||
raise Exception("Unsupported platform")
|
||||
from . import emulation as backend
|
||||
|
||||
|
||||
list_descriptors = backend.list_descriptors
|
||||
get_descriptor = backend.get_descriptor
|
||||
open_connection = backend.open_connection
|
||||
|
||||
|
||||
@unique
|
||||
class CTAPHID(IntEnum):
|
||||
PING = 0x01
|
||||
MSG = 0x03
|
||||
LOCK = 0x04
|
||||
INIT = 0x06
|
||||
WINK = 0x08
|
||||
CBOR = 0x10
|
||||
CANCEL = 0x11
|
||||
|
||||
ERROR = 0x3F
|
||||
KEEPALIVE = 0x3B
|
||||
|
||||
VENDOR_FIRST = 0x40
|
||||
|
||||
|
||||
@unique
|
||||
class CAPABILITY(IntFlag):
|
||||
WINK = 0x01
|
||||
LOCK = 0x02 # Not used
|
||||
CBOR = 0x04
|
||||
NMSG = 0x08
|
||||
|
||||
def supported(self, flags: CAPABILITY) -> bool:
|
||||
return bool(flags & self)
|
||||
|
||||
|
||||
TYPE_INIT = 0x80
|
||||
|
||||
|
||||
class CtapHidDevice(CtapDevice):
|
||||
"""
|
||||
CtapDevice implementation using the HID transport.
|
||||
|
||||
:cvar descriptor: Device descriptor.
|
||||
"""
|
||||
|
||||
def __init__(self, descriptor: HidDescriptor, connection):
|
||||
self.descriptor = descriptor
|
||||
self._packet_size = descriptor.report_size_out
|
||||
self._connection = connection
|
||||
|
||||
nonce = os.urandom(8)
|
||||
self._channel_id = 0xFFFFFFFF
|
||||
response = self.call(CTAPHID.INIT, nonce)
|
||||
r_nonce, response = response[:8], response[8:]
|
||||
if r_nonce != nonce:
|
||||
raise Exception("Wrong nonce")
|
||||
(
|
||||
self._channel_id,
|
||||
self._u2fhid_version,
|
||||
v1,
|
||||
v2,
|
||||
v3,
|
||||
self._capabilities,
|
||||
) = struct.unpack_from(">IBBBBB", response)
|
||||
self._device_version = (v1, v2, v3)
|
||||
|
||||
def __repr__(self):
|
||||
return f"CtapHidDevice({self.descriptor.path!r})"
|
||||
|
||||
@property
|
||||
def version(self) -> int:
|
||||
"""CTAP HID protocol version."""
|
||||
return self._u2fhid_version
|
||||
|
||||
@property
|
||||
def device_version(self) -> Tuple[int, int, int]:
|
||||
"""Device version number."""
|
||||
return self._device_version
|
||||
|
||||
@property
|
||||
def capabilities(self) -> int:
|
||||
"""Capabilities supported by the device."""
|
||||
return self._capabilities
|
||||
|
||||
@property
|
||||
def product_name(self) -> Optional[str]:
|
||||
"""Product name of device."""
|
||||
return self.descriptor.product_name
|
||||
|
||||
@property
|
||||
def serial_number(self) -> Optional[str]:
|
||||
"""Serial number of device."""
|
||||
return self.descriptor.serial_number
|
||||
|
||||
def _send_cancel(self):
|
||||
packet = struct.pack(">IB", self._channel_id, TYPE_INIT | CTAPHID.CANCEL).ljust(
|
||||
self._packet_size, b"\0"
|
||||
)
|
||||
logger.log(LOG_LEVEL_TRAFFIC, "SEND: %s", packet.hex())
|
||||
self._connection.write_packet(packet)
|
||||
|
||||
def call(
|
||||
self,
|
||||
cmd: int,
|
||||
data: bytes = b"",
|
||||
event: Optional[Event] = None,
|
||||
on_keepalive: Optional[Callable[[int], None]] = None,
|
||||
) -> bytes:
|
||||
event = event or Event()
|
||||
remaining = data
|
||||
seq = 0
|
||||
|
||||
# Send request
|
||||
header = struct.pack(">IBH", self._channel_id, TYPE_INIT | cmd, len(remaining))
|
||||
while remaining or seq == 0:
|
||||
size = min(len(remaining), self._packet_size - len(header))
|
||||
body, remaining = remaining[:size], remaining[size:]
|
||||
packet = header + body
|
||||
logger.log(LOG_LEVEL_TRAFFIC, "SEND: %s", packet.hex())
|
||||
self._connection.write_packet(packet.ljust(self._packet_size, b"\0"))
|
||||
header = struct.pack(">IB", self._channel_id, 0x7F & seq)
|
||||
seq += 1
|
||||
|
||||
try:
|
||||
# Read response
|
||||
seq = 0
|
||||
response = b""
|
||||
last_ka = None
|
||||
while True:
|
||||
if event.is_set():
|
||||
# Cancel
|
||||
logger.debug("Sending cancel...")
|
||||
self._send_cancel()
|
||||
|
||||
recv = self._connection.read_packet()
|
||||
logger.log(LOG_LEVEL_TRAFFIC, "RECV: %s", recv.hex())
|
||||
|
||||
r_channel = struct.unpack_from(">I", recv)[0]
|
||||
recv = recv[4:]
|
||||
if r_channel != self._channel_id:
|
||||
raise Exception("Wrong channel")
|
||||
|
||||
if not response: # Initialization packet
|
||||
r_cmd, r_len = struct.unpack_from(">BH", recv)
|
||||
recv = recv[3:]
|
||||
if r_cmd == TYPE_INIT | cmd:
|
||||
pass # first data packet
|
||||
elif r_cmd == TYPE_INIT | CTAPHID.KEEPALIVE:
|
||||
ka_status = struct.unpack_from(">B", recv)[0]
|
||||
logger.debug(f"Got keepalive status: {ka_status:02x}")
|
||||
if on_keepalive and ka_status != last_ka:
|
||||
try:
|
||||
ka_status = STATUS(ka_status)
|
||||
except ValueError:
|
||||
pass # Unknown status value
|
||||
last_ka = ka_status
|
||||
on_keepalive(ka_status)
|
||||
continue
|
||||
elif r_cmd == TYPE_INIT | CTAPHID.ERROR:
|
||||
raise CtapError(struct.unpack_from(">B", recv)[0])
|
||||
else:
|
||||
raise CtapError(CtapError.ERR.INVALID_COMMAND)
|
||||
else: # Continuation packet
|
||||
r_seq = struct.unpack_from(">B", recv)[0]
|
||||
recv = recv[1:]
|
||||
if r_seq != seq:
|
||||
raise Exception("Wrong sequence number")
|
||||
seq += 1
|
||||
|
||||
response += recv
|
||||
if len(response) >= r_len:
|
||||
break
|
||||
|
||||
return response[:r_len]
|
||||
except KeyboardInterrupt:
|
||||
logger.debug("Keyboard interrupt, cancelling...")
|
||||
self._send_cancel()
|
||||
|
||||
raise
|
||||
|
||||
def wink(self) -> None:
|
||||
"""Causes the authenticator to blink."""
|
||||
self.call(CTAPHID.WINK)
|
||||
|
||||
def ping(self, msg: bytes = b"Hello FIDO") -> bytes:
|
||||
"""Sends data to the authenticator, which echoes it back.
|
||||
|
||||
:param msg: The data to send.
|
||||
:return: The response from the authenticator.
|
||||
"""
|
||||
return self.call(CTAPHID.PING, msg)
|
||||
|
||||
def lock(self, lock_time: int = 10) -> None:
|
||||
"""Locks the channel."""
|
||||
self.call(CTAPHID.LOCK, struct.pack(">B", lock_time))
|
||||
|
||||
def close(self) -> None:
|
||||
if self._connection:
|
||||
self._connection.close()
|
||||
self._connection = None
|
||||
|
||||
@classmethod
|
||||
def list_devices(cls) -> Iterator[CtapHidDevice]:
|
||||
for d in list_descriptors():
|
||||
yield cls(d, open_connection(d))
|
||||
|
||||
|
||||
def list_devices() -> Iterator[CtapHidDevice]:
|
||||
return CtapHidDevice.list_devices()
|
||||
|
||||
|
||||
def open_device(path) -> CtapHidDevice:
|
||||
descriptor = get_descriptor(path)
|
||||
return CtapHidDevice(descriptor, open_connection(descriptor))
|
||||
79
tests/docker/fido2/emulation.py
Normal file
79
tests/docker/fido2/emulation.py
Normal file
@@ -0,0 +1,79 @@
|
||||
# Original work Copyright 2016 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# Modified work Copyright 2020 Yubico AB. All Rights Reserved.
|
||||
# This file, with modifications, is licensed under the above Apache License.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from .base import HidDescriptor, CtapHidConnection
|
||||
|
||||
import socket
|
||||
from typing import Set
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
HOST = '127.0.0.1'
|
||||
PORT = 35962
|
||||
|
||||
# Don't typecheck this file on Windows
|
||||
assert sys.platform != "win32" # nosec
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class EmulationCtapHidConnection(CtapHidConnection):
|
||||
def __init__(self, descriptor):
|
||||
self.descriptor = descriptor
|
||||
self.handle = descriptor.path
|
||||
self.handle.connect((HOST, PORT))
|
||||
|
||||
def write_packet(self, packet):
|
||||
if (self.handle.send(len(packet).to_bytes(2, 'big')) != 2):
|
||||
raise OSError("write_packet sending size failed")
|
||||
if (self.handle.send(packet) != len(packet)):
|
||||
raise OSError("write_packet sending packet failed")
|
||||
|
||||
def read_packet(self):
|
||||
bts = self.handle.recv(2)
|
||||
if (len(bts) != 2):
|
||||
raise OSError("read_packet failed reading size")
|
||||
size = int.from_bytes(bts, 'big')
|
||||
data = self.handle.recv(size)
|
||||
if (len(data) != size):
|
||||
raise OSError("read_packet failed reading packet")
|
||||
return data
|
||||
|
||||
def close(self) -> None:
|
||||
return self.handle.close()
|
||||
|
||||
|
||||
def open_connection(descriptor):
|
||||
return EmulationCtapHidConnection(descriptor)
|
||||
|
||||
|
||||
def get_descriptor(_):
|
||||
HOST = 'localhost' # The remote host
|
||||
PORT = 35962 # The same port as used by the server
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
return HidDescriptor(s, 0x00, 0x00, 64, 64, "Pico-Fido", "AAAAAA")
|
||||
|
||||
def list_descriptors():
|
||||
devices = []
|
||||
try:
|
||||
devices.append(get_descriptor(None))
|
||||
except ValueError:
|
||||
pass # Not a CTAP device, ignore.
|
||||
|
||||
return devices
|
||||
107
tests/docker_env.sh
Normal file
107
tests/docker_env.sh
Normal file
@@ -0,0 +1,107 @@
|
||||
#!/bin/bash -eu
|
||||
|
||||
# Taken from Mbed-TLS project
|
||||
# https://github.com/Mbed-TLS/mbedtls/blob/master/tests/scripts/docker_env.sh
|
||||
#
|
||||
# docker_env.sh
|
||||
#
|
||||
# Purpose
|
||||
# -------
|
||||
#
|
||||
# This is a helper script to enable running tests under a Docker container,
|
||||
# thus making it easier to get set up as well as isolating test dependencies
|
||||
# (which include legacy/insecure configurations of openssl and gnutls).
|
||||
#
|
||||
# WARNING: the Dockerfile used by this script is no longer maintained! See
|
||||
# https://github.com/Mbed-TLS/mbedtls-test/blob/master/README.md#quick-start
|
||||
# for the set of Docker images we use on the CI.
|
||||
#
|
||||
# Notes for users
|
||||
# ---------------
|
||||
# This script expects a Linux x86_64 system with a recent version of Docker
|
||||
# installed and available for use, as well as http/https access. If a proxy
|
||||
# server must be used, invoke this script with the usual environment variables
|
||||
# (http_proxy and https_proxy) set appropriately. If an alternate Docker
|
||||
# registry is needed, specify MBEDTLS_DOCKER_REGISTRY to point at the
|
||||
# host name.
|
||||
#
|
||||
#
|
||||
# Running this script directly will check for Docker availability and set up
|
||||
# the Docker image.
|
||||
|
||||
# Copyright The Mbed TLS Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
# default values, can be overridden by the environment
|
||||
: ${MBEDTLS_DOCKER_GUEST:=bullseye}
|
||||
|
||||
|
||||
DOCKER_IMAGE_TAG="pico-hsm-test:${MBEDTLS_DOCKER_GUEST}"
|
||||
|
||||
# Make sure docker is available
|
||||
if ! which docker > /dev/null; then
|
||||
echo "Docker is required but doesn't seem to be installed. See https://www.docker.com/ to get started"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Figure out if we need to 'sudo docker'
|
||||
if groups | grep docker > /dev/null; then
|
||||
DOCKER="docker"
|
||||
else
|
||||
echo "Using sudo to invoke docker since you're not a member of the docker group..."
|
||||
DOCKER="docker"
|
||||
fi
|
||||
|
||||
# Figure out the number of processors available
|
||||
if [ "$(uname)" == "Darwin" ]; then
|
||||
NUM_PROC="$(sysctl -n hw.logicalcpu)"
|
||||
else
|
||||
NUM_PROC="$(nproc)"
|
||||
fi
|
||||
|
||||
# Build the Docker image
|
||||
echo "Getting docker image up to date (this may take a few minutes)..."
|
||||
${DOCKER} image build \
|
||||
-t ${DOCKER_IMAGE_TAG} \
|
||||
--cache-from=${DOCKER_IMAGE_TAG} \
|
||||
--network host \
|
||||
--build-arg MAKEFLAGS_PARALLEL="-j ${NUM_PROC}" \
|
||||
tests/docker/${MBEDTLS_DOCKER_GUEST}
|
||||
|
||||
run_in_docker()
|
||||
{
|
||||
ENV_ARGS=""
|
||||
while [ "$1" == "-e" ]; do
|
||||
ENV_ARGS="${ENV_ARGS} $1 $2"
|
||||
shift 2
|
||||
done
|
||||
|
||||
WORKDIR="${PWD}"
|
||||
if [ "$1" == '-w' ]; then
|
||||
WORKDIR="$2"
|
||||
shift 2
|
||||
fi
|
||||
|
||||
${DOCKER} container run --rm \
|
||||
--cap-add ALL \
|
||||
--privileged \
|
||||
--volume $PWD:$PWD \
|
||||
--workdir ${WORKDIR} \
|
||||
-e MAKEFLAGS \
|
||||
${ENV_ARGS} \
|
||||
${DOCKER_IMAGE_TAG} \
|
||||
$@
|
||||
}
|
||||
47
tests/pico-fido/test_000_getinfo.py
Normal file
47
tests/pico-fido/test_000_getinfo.py
Normal file
@@ -0,0 +1,47 @@
|
||||
"""
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
import pytest
|
||||
from fido2.client import CtapError
|
||||
|
||||
|
||||
def test_getinfo(device):
|
||||
pass
|
||||
|
||||
|
||||
def test_get_info_version(info):
|
||||
assert "FIDO_2_0" in info.versions
|
||||
|
||||
|
||||
def test_Check_pin_protocols_field(info):
|
||||
if len(info.pin_uv_protocols):
|
||||
assert sum(info.pin_uv_protocols) > 0
|
||||
|
||||
|
||||
def test_Check_options_field(info):
|
||||
for x in info.options:
|
||||
assert info.options[x] in [True, False]
|
||||
|
||||
|
||||
def test_Check_up_option(device, info):
|
||||
if "up" not in info.options or info.options["up"]:
|
||||
with pytest.raises(CtapError) as e:
|
||||
device.MC(options={"up": True})
|
||||
assert e.value.code == CtapError.ERR.INVALID_OPTION
|
||||
@@ -1,3 +1,23 @@
|
||||
"""
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
import os
|
||||
import pytest
|
||||
from fido2.ctap import CtapError
|
||||
@@ -14,9 +34,9 @@ def test_lockout(device, resetdevice, client_pin):
|
||||
pin_token = client_pin.get_pin_token(pin)
|
||||
|
||||
for i in range(1, 10):
|
||||
err = CtapError.ERR.PIN_INVALID
|
||||
if i in (3, 6):
|
||||
err = CtapError.ERR.PIN_AUTH_BLOCKED
|
||||
err = [CtapError.ERR.PIN_INVALID]
|
||||
if 3 <= i <= 7:
|
||||
err = [CtapError.ERR.PIN_AUTH_BLOCKED]
|
||||
elif i >= 8:
|
||||
err = [CtapError.ERR.PIN_BLOCKED, CtapError.ERR.PIN_INVALID]
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
"""
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
from fido2.client import CtapError
|
||||
from fido2.cose import ES256
|
||||
import pytest
|
||||
@@ -160,5 +180,6 @@ def test_exclude_list_excluded(device):
|
||||
|
||||
assert e.value.code == CtapError.ERR.CREDENTIAL_EXCLUDED
|
||||
|
||||
def test_unknown_option(resetdevice):
|
||||
resetdevice.MC(options={"unknown": False})
|
||||
def test_unknown_option(device):
|
||||
device.reset()
|
||||
device.MC(options={"unknown": False})
|
||||
@@ -1,3 +1,23 @@
|
||||
"""
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
from fido2.utils import sha256
|
||||
from fido2.client import CtapError
|
||||
import pytest
|
||||
@@ -1,3 +1,23 @@
|
||||
"""
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
from fido2.client import CtapError
|
||||
import pytest
|
||||
import random
|
||||
@@ -1,3 +1,23 @@
|
||||
"""
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
import pytest
|
||||
from fido2.ctap import CtapError
|
||||
from fido2.ctap2.pin import PinProtocolV2, ClientPin
|
||||
@@ -1,3 +1,23 @@
|
||||
"""
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
import pytest
|
||||
from fido2.ctap2.extensions import CredProtectExtension
|
||||
from fido2.webauthn import UserVerificationRequirement
|
||||
@@ -106,10 +126,7 @@ def test_credprotect_optional_and_list_works_no_uv(device, MCCredProtectOptional
|
||||
res1 = device.doGA(allow_list=allow_list)['res'].get_assertions()[0]
|
||||
assert res1.number_of_credentials in (None, 2)
|
||||
|
||||
results = [res1]
|
||||
if res1.number_of_credentials == 2:
|
||||
res2 = device.GNA()['res']
|
||||
results.append(res2)
|
||||
results = device.doGA(allow_list=allow_list)['res'].get_assertions()
|
||||
|
||||
# the required credProtect is not returned.
|
||||
for res in results:
|
||||
@@ -1,3 +1,23 @@
|
||||
"""
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
import pytest
|
||||
from fido2.ctap import CtapError
|
||||
from fido2.ctap2.extensions import HmacSecretExtension
|
||||
@@ -1,3 +1,23 @@
|
||||
"""
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
import pytest
|
||||
from fido2.ctap2.extensions import CredProtectExtension
|
||||
from fido2.webauthn import UserVerificationRequirement
|
||||
@@ -1,3 +1,23 @@
|
||||
"""
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
import pytest
|
||||
import time
|
||||
import random
|
||||
@@ -87,7 +107,7 @@ def assert_cred_response_has_all_fields(cred_res):
|
||||
CredentialManagement.RESULT.CREDENTIAL_ID,
|
||||
CredentialManagement.RESULT.PUBLIC_KEY,
|
||||
CredentialManagement.RESULT.TOTAL_CREDENTIALS,
|
||||
CredentialManagement.RESULT.CRED_PROTECT,
|
||||
#CredentialManagement.RESULT.CRED_PROTECT,
|
||||
):
|
||||
assert i in cred_res
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
"""
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
# Test U2F register works with FIDO2 auth
|
||||
def test_ctap1_register(RegRes):
|
||||
pass
|
||||
@@ -1,3 +1,23 @@
|
||||
"""
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
import pytest
|
||||
import os
|
||||
from fido2.ctap1 import APDU, ApduError, Ctap1
|
||||
@@ -1,3 +1,23 @@
|
||||
"""
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
import os
|
||||
import socket
|
||||
import time
|
||||
@@ -154,22 +174,17 @@ class TestHID(object):
|
||||
def test_check_busy(self, device):
|
||||
t1 = time.time() * 1000
|
||||
device.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
oldcid = device.cid()
|
||||
#oldcid = device.cid().to_bytes(4, 'big')
|
||||
newcid = b"\x11\x22\x33\x44"
|
||||
device.send_raw("\x81\x04\x00")
|
||||
device.set_cid(newcid)
|
||||
device.send_raw("\x81\x04\x00")
|
||||
cmd, r = device.recv_raw() # busy response
|
||||
t2 = time.time() * 1000
|
||||
assert t2 - t1 < 100
|
||||
#t2 = time.time() * 1000
|
||||
#assert t2 - t1 < 100
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.CHANNEL_BUSY
|
||||
|
||||
device.set_cid(oldcid)
|
||||
cmd, r = device.recv_raw() # timeout response
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.TIMEOUT
|
||||
|
||||
def test_check_busy_interleaved(self, device):
|
||||
cid1 = b"\x11\x22\x33\x44"
|
||||
cid2 = b"\x01\x22\x33\x44"
|
||||
@@ -200,25 +215,26 @@ class TestHID(object):
|
||||
assert len(r) == 0x39
|
||||
|
||||
def test_cid_0(self, device):
|
||||
device.set_cid("\x00\x00\x00\x00")
|
||||
device.reset()
|
||||
device.set_cid(b"\x00\x00\x00\x00")
|
||||
device.send_raw(
|
||||
"\x86\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88", cid="\x00\x00\x00\x00"
|
||||
)
|
||||
cmd, r = device.recv_raw() # timeout
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.INVALID_CHANNEL
|
||||
device.set_cid("\x05\x04\x03\x02")
|
||||
device.set_cid(b"\x05\x04\x03\x02")
|
||||
|
||||
def test_cid_ffffffff(self, device):
|
||||
|
||||
device.set_cid("\xff\xff\xff\xff")
|
||||
device.set_cid(b"\xff\xff\xff\xff")
|
||||
device.send_raw(
|
||||
"\x81\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88", cid="\xff\xff\xff\xff"
|
||||
)
|
||||
cmd, r = device.recv_raw() # timeout
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.INVALID_CHANNEL
|
||||
device.set_cid("\x05\x04\x03\x02")
|
||||
device.set_cid(b"\x05\x04\x03\x02")
|
||||
|
||||
def test_keep_alive(self, device, check_timeouts=False):
|
||||
|
||||
258
tests/pico-fido/test_070_oath.py
Normal file
258
tests/pico-fido/test_070_oath.py
Normal file
@@ -0,0 +1,258 @@
|
||||
"""
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from utils import *
|
||||
import hmac, hashlib
|
||||
|
||||
INS_PUT = 0x01
|
||||
INS_DELETE = 0x02
|
||||
INS_SET_CODE = 0x03
|
||||
INS_RESET = 0x04
|
||||
INS_LIST = 0xa1
|
||||
INS_CALCULATE = 0xa2
|
||||
INS_VALIDATE = 0xa3
|
||||
INS_CALC_ALL = 0xa4
|
||||
INS_SEND_REMAINING = 0xa5
|
||||
|
||||
RESP_MORE_DATA = 0x61
|
||||
|
||||
TAG_NAME = 0x71
|
||||
TAG_NAME_LIST = 0x72
|
||||
TAG_KEY = 0x73
|
||||
TAG_CHALLENGE = 0x74
|
||||
TAG_RESPONSE = 0x75
|
||||
TAG_T_RESPONSE = 0x76
|
||||
TAG_NO_RESPONSE = 0x77
|
||||
TAG_PROPERTY = 0x78
|
||||
TAG_VERSION = 0x79
|
||||
TAG_IMF = 0x7a
|
||||
TAG_ALGO = 0x7b
|
||||
TAG_TOUCH_RESPONSE = 0x7c
|
||||
|
||||
TYPE_MASK = 0xf0
|
||||
TYPE_HOTP = 0x10
|
||||
TYPE_TOTP = 0x20
|
||||
|
||||
ALG_MASK = 0x0f
|
||||
ALG_SHA1 = 0x01
|
||||
ALG_SHA256 = 0x02
|
||||
|
||||
PROP_ALWAYS_INC = 0x01
|
||||
PROP_REQUIRE_TOUCH = 0x02
|
||||
|
||||
## Based on tests on https://github.com/Yubico/ykneo-oath/blob/master/test/test/pkgYkneoOathTest/YkneoOathTest.java
|
||||
|
||||
def test_select_oath(select_oath):
|
||||
pass
|
||||
|
||||
def list_apdu(ccid_card):
|
||||
resp = send_apdu(ccid_card, INS_LIST, p1=0, p2=0)
|
||||
return resp
|
||||
|
||||
name_kaka = [ord('k'), ord('a'), ord('k'), ord('a')]
|
||||
data_name = [TAG_NAME] + [len(name_kaka)] + name_kaka
|
||||
data_key = [TAG_KEY, 0x16, 0x21, 0x06, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b]
|
||||
data_chal = [TAG_CHALLENGE, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]
|
||||
|
||||
def test_life(reset_oath):
|
||||
data = data_name + data_key
|
||||
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=list(data))
|
||||
assert(len(resp) == 0)
|
||||
resp = list_apdu(reset_oath)
|
||||
exp = [TAG_NAME_LIST, 5, 0x21] + name_kaka
|
||||
assert(resp == exp)
|
||||
|
||||
data = data_name + data_chal
|
||||
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=0, data=data)
|
||||
exp = [TAG_RESPONSE, 0x15, 0x06, 0xb3, 0x99, 0xbd, 0xfc, 0x9d, 0x05, 0xd1, 0x2a, 0xc4, 0x35, 0xc4, 0xc8, 0xd6, 0xcb, 0xd2, 0x47, 0xc4, 0x0a, 0x30, 0xf1]
|
||||
assert(resp == exp)
|
||||
|
||||
data = data_name
|
||||
resp = send_apdu(reset_oath, INS_DELETE, p1=0, p2=0, data=data)
|
||||
resp = list_apdu(reset_oath)
|
||||
assert(len(resp) == 0)
|
||||
|
||||
def test_overwrite(reset_oath):
|
||||
data = data_name + data_key
|
||||
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=list(data))
|
||||
assert(len(resp) == 0)
|
||||
resp = list_apdu(reset_oath)
|
||||
exp = [TAG_NAME_LIST, 5, 0x21] + name_kaka
|
||||
assert(resp == exp)
|
||||
|
||||
data = data_name + [TAG_CHALLENGE, 0x8] + list(bytes(b'\xff'*8))
|
||||
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=0, data=data)
|
||||
exp = [TAG_RESPONSE, 0x15, 0x06, 0x79, 0x3e, 0x1b, 0xbd, 0xbf, 0xa7, 0x75, 0xa8, 0x63,0xcc, 0x80, 0x02, 0xce, 0xe4, 0xbd, 0x6c, 0xd7, 0xce, 0xb8, 0xcd]
|
||||
assert(resp == exp)
|
||||
|
||||
resp = list_apdu(reset_oath)
|
||||
exp = [TAG_NAME_LIST, 5, 0x21] + name_kaka
|
||||
assert(resp == exp)
|
||||
|
||||
data = data_name + [TAG_CHALLENGE, 0x8] + list(bytes(b'\xff\x00'*4))
|
||||
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=0, data=data)
|
||||
exp = [TAG_RESPONSE, 0x15, 0x06, 0x3b, 0x0e, 0x3c, 0x63, 0x1c, 0x01, 0x67, 0xb0, 0x93, 0xa5, 0xec, 0xb9, 0x09, 0x7d, 0x0b, 0x8e, 0x9a, 0xcc, 0x2f, 0x7f]
|
||||
assert(resp == exp)
|
||||
|
||||
def test_auth(reset_oath):
|
||||
key = list(bytes(b'kaka blahonga'))
|
||||
chal = [1,2,3,4,5,6,7,8]
|
||||
resp = [0x0c, 0x42, 0x8e, 0x9c, 0xba, 0xa3, 0xb3, 0xab, 0x18, 0x53, 0xd8, 0x79, 0xb9, 0xd2, 0x26, 0xf7, 0xce, 0xcc, 0x4a, 0x7a]
|
||||
data = [TAG_KEY, len(key)+1, ALG_SHA1 | TYPE_TOTP] + key + [TAG_CHALLENGE, len(chal)] + chal + [TAG_RESPONSE, len(resp)] + resp
|
||||
resp = send_apdu(reset_oath, INS_SET_CODE, p1=0, p2=0, data=data)
|
||||
|
||||
reset_oath.connection.reconnect()
|
||||
with pytest.raises(APDUResponse) as e:
|
||||
resp = list_apdu(reset_oath)
|
||||
assert([e.value.sw1, e.value.sw2] == [0x69, 0x82])
|
||||
|
||||
aid = [0xa0, 0x00, 0x00, 0x05, 0x27, 0x21, 0x01, 0x01]
|
||||
resp = send_apdu(reset_oath, 0xA4, 0x04, 0x00, aid)
|
||||
assert(resp[15] == TAG_CHALLENGE)
|
||||
assert(resp[16] == 8)
|
||||
resp2 = hmac.digest(bytes(key), bytes(resp[17:17+8]), 'sha1')
|
||||
data = [TAG_RESPONSE, len(resp2)] + list(resp2) + [TAG_CHALLENGE, len(chal)] + chal
|
||||
resp = send_apdu(reset_oath, INS_VALIDATE, p1=0, p2=0, data=data)
|
||||
exp = [TAG_RESPONSE, 20] + list(hmac.digest(bytes(key), bytes(chal), 'sha1'))
|
||||
assert(exp == resp)
|
||||
resp = list_apdu(reset_oath)
|
||||
|
||||
def test_bothoath(reset_oath):
|
||||
digits = 6
|
||||
tname = list(bytes(b'totp'))
|
||||
data = [TAG_NAME, len(tname)] + tname + [TAG_KEY, 9, TYPE_TOTP | ALG_SHA1, digits] + list(bytes(b'foo bar'))
|
||||
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=data)
|
||||
data[2] = ord('h')
|
||||
data[8] = TYPE_HOTP | ALG_SHA1
|
||||
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=data)
|
||||
|
||||
hname = tname[:]
|
||||
hname[0] = ord('h')
|
||||
data = [TAG_CHALLENGE, 8, 0, 0, 0, 0, 0x02, 0xbc, 0xad, 0xc8]
|
||||
resp = send_apdu(reset_oath, INS_CALC_ALL, p1=0, p2=1, data=data)
|
||||
exp = [TAG_NAME, len(tname)] + tname + [TAG_T_RESPONSE, 5, digits, 0x3d, 0xc6, 0xbf, 0x3d] + [TAG_NAME, len(hname)] + hname + [TAG_NO_RESPONSE, 0x01, digits]
|
||||
assert(exp == resp)
|
||||
|
||||
data = [TAG_NAME, len(hname)] + hname + [TAG_CHALLENGE]
|
||||
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=1, data=data)
|
||||
exp = [TAG_T_RESPONSE, 5, digits, 0x17, 0xfa, 0x2d, 0x40]
|
||||
assert(resp == exp)
|
||||
|
||||
def test_imf_overwrite(reset_oath):
|
||||
key = list(bytes(b'kaka'))
|
||||
imf = [0xff, 0x00, 0xff, 0xff]
|
||||
name = list(bytes(b'kaka'))
|
||||
|
||||
data = [TAG_NAME, len(name)] + name + [TAG_KEY, len(key)+2, ALG_SHA1 | TYPE_HOTP, 6] + key + [TAG_IMF, len(imf)] + imf
|
||||
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=data)
|
||||
data = [TAG_NAME, len(name)] + name + [TAG_CHALLENGE]
|
||||
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=1, data=data)
|
||||
exp = [TAG_T_RESPONSE, 5, 6, 0x45, 0xd9, 0x0f, 0x25]
|
||||
assert(exp == resp)
|
||||
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=1, data=data)
|
||||
exp = [TAG_T_RESPONSE, 5, 6, 0x1b, 0xc5, 0x4a, 0x85]
|
||||
assert(exp == resp)
|
||||
|
||||
data = [TAG_NAME, len(name)] + name + [TAG_KEY, len(key)+2, ALG_SHA1 | TYPE_HOTP, 6] + key
|
||||
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=data)
|
||||
data = [TAG_NAME, len(name)] + name + [TAG_CHALLENGE]
|
||||
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=1, data=data)
|
||||
exp = [TAG_T_RESPONSE, 5, 6, 0x16, 0x53, 0x24, 0xdb]
|
||||
assert(exp == resp)
|
||||
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=1, data=data)
|
||||
exp = [TAG_T_RESPONSE, 5, 6, 0x53, 0xed, 0x5e, 0xb2]
|
||||
assert(exp == resp)
|
||||
|
||||
def test_imf_more(reset_oath):
|
||||
key = [0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30,
|
||||
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30]
|
||||
imf = [0, 0, 0, 1]
|
||||
name = list(bytes(b'kaka'))
|
||||
|
||||
data = [TAG_NAME, len(name)] + name + [TAG_KEY, len(key)+2, ALG_SHA1 | TYPE_HOTP, 6] + key + [TAG_IMF, len(imf)] + imf
|
||||
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=data)
|
||||
data = [TAG_NAME, len(name)] + name + [TAG_CHALLENGE]
|
||||
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=1, data=data)
|
||||
exp = [TAG_T_RESPONSE, 5, 6, 0x41, 0x39, 0x7e, 0xea]
|
||||
assert(exp == resp)
|
||||
|
||||
def test_delete(reset_oath):
|
||||
key = list(bytes(b'blahonga!'))
|
||||
firstname = list(bytes(b'one'))
|
||||
secondname = list(bytes(b'two'))
|
||||
thirdname = list(bytes(b'three'))
|
||||
type = ALG_SHA1 | TYPE_TOTP
|
||||
|
||||
data = [TAG_NAME, len(firstname)] + firstname + [TAG_KEY, len(key)+2, type, 6] + key
|
||||
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=data)
|
||||
data = [TAG_NAME, len(secondname)] + secondname + [TAG_KEY, len(key)+2, type, 6] + key
|
||||
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=data)
|
||||
resp = list_apdu(reset_oath)
|
||||
exp = [TAG_NAME_LIST, len(firstname)+1, type] + firstname + [TAG_NAME_LIST, len(secondname)+1, type] + secondname
|
||||
assert(exp == resp)
|
||||
|
||||
data = [TAG_NAME, len(firstname)] + firstname
|
||||
resp = send_apdu(reset_oath, INS_DELETE, p1=0, p2=0, data=data)
|
||||
resp = list_apdu(reset_oath)
|
||||
exp = [TAG_NAME_LIST, len(secondname)+1, type] + secondname
|
||||
assert(exp == resp)
|
||||
|
||||
data = [TAG_NAME, len(thirdname)] + thirdname + [TAG_KEY, len(key)+2, type, 6] + key
|
||||
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=data)
|
||||
resp = list_apdu(reset_oath)
|
||||
exp = [TAG_NAME_LIST, len(thirdname)+1, type] + thirdname + [TAG_NAME_LIST, len(secondname)+1, type] + secondname
|
||||
assert(exp == resp)
|
||||
|
||||
def test_noauth(reset_oath):
|
||||
key = list(bytes(b'kaka blahonga'))
|
||||
chal = [1,2,3,4,5,6,7,8]
|
||||
resp = [0x0c, 0x42, 0x8e, 0x9c, 0xba, 0xa3, 0xb3, 0xab, 0x18, 0x53, 0xd8, 0x79, 0xb9, 0xd2, 0x26, 0xf7, 0xce, 0xcc, 0x4a, 0x7a]
|
||||
data = [TAG_KEY, len(key)+1, ALG_SHA1 | TYPE_TOTP] + key + [TAG_CHALLENGE, len(chal)] + chal + [TAG_RESPONSE, len(resp)] + resp
|
||||
resp = send_apdu(reset_oath, INS_SET_CODE, p1=0, p2=0, data=data)
|
||||
|
||||
reset_oath.connection.reconnect()
|
||||
with pytest.raises(APDUResponse) as e:
|
||||
resp = list_apdu(reset_oath)
|
||||
assert([e.value.sw1, e.value.sw2] == [0x69, 0x82])
|
||||
|
||||
with pytest.raises(APDUResponse) as e:
|
||||
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=None)
|
||||
assert([e.value.sw1, e.value.sw2] == [0x69, 0x82])
|
||||
|
||||
with pytest.raises(APDUResponse) as e:
|
||||
resp = send_apdu(reset_oath, INS_DELETE, p1=0, p2=0, data=None)
|
||||
assert([e.value.sw1, e.value.sw2] == [0x69, 0x82])
|
||||
|
||||
with pytest.raises(APDUResponse) as e:
|
||||
resp = send_apdu(reset_oath, INS_SET_CODE, p1=0, p2=0, data=None)
|
||||
assert([e.value.sw1, e.value.sw2] == [0x69, 0x82])
|
||||
|
||||
with pytest.raises(APDUResponse) as e:
|
||||
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=0, data=None)
|
||||
assert([e.value.sw1, e.value.sw2] == [0x69, 0x82])
|
||||
|
||||
with pytest.raises(APDUResponse) as e:
|
||||
resp = send_apdu(reset_oath, INS_CALC_ALL, p1=0, p2=0, data=None)
|
||||
assert([e.value.sw1, e.value.sw2] == [0x69, 0x82])
|
||||
|
||||
with pytest.raises(APDUResponse) as e:
|
||||
resp = send_apdu(reset_oath, INS_RESET, p1=0, p2=0, data=None)
|
||||
assert([e.value.sw1, e.value.sw2] == [0x6A, 0x86])
|
||||
resp = send_apdu(reset_oath, INS_RESET, p1=0xde, p2=0xad, data=None)
|
||||
@@ -1,27 +0,0 @@
|
||||
import pytest
|
||||
from fido2.client import CtapError
|
||||
|
||||
|
||||
def test_getinfo(device):
|
||||
pass
|
||||
|
||||
|
||||
def test_get_info_version(info):
|
||||
assert "FIDO_2_0" in info.versions
|
||||
|
||||
|
||||
def test_Check_pin_protocols_field(info):
|
||||
if len(info.pin_uv_protocols):
|
||||
assert sum(info.pin_uv_protocols) > 0
|
||||
|
||||
|
||||
def test_Check_options_field(info):
|
||||
for x in info.options:
|
||||
assert info.options[x] in [True, False]
|
||||
|
||||
|
||||
def test_Check_up_option(device, info):
|
||||
if "up" not in info.options or info.options["up"]:
|
||||
with pytest.raises(CtapError) as e:
|
||||
device.MC(options={"up": True})
|
||||
assert e.value.code == CtapError.ERR.INVALID_OPTION
|
||||
5
tests/run-test-in-docker.sh
Executable file
5
tests/run-test-in-docker.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash -eu
|
||||
|
||||
source tests/docker_env.sh
|
||||
run_in_docker ./tests/start-up-and-test.sh
|
||||
|
||||
8
tests/start-up-and-test.sh
Executable file
8
tests/start-up-and-test.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash -eu
|
||||
|
||||
/usr/sbin/pcscd &
|
||||
sleep 2
|
||||
rm -f memory.flash
|
||||
cp -R tests/docker/fido2/* /usr/local/lib/python3.9/dist-packages/fido2/hid
|
||||
./build_in_docker/pico_fido > /dev/null &
|
||||
pytest tests
|
||||
@@ -1,3 +1,23 @@
|
||||
"""
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
from fido2.webauthn import AttestedCredentialData
|
||||
import random
|
||||
import string
|
||||
@@ -6,6 +26,46 @@ import math
|
||||
from threading import Event, Timer
|
||||
from numbers import Number
|
||||
|
||||
import sys
|
||||
try:
|
||||
from smartcard.CardType import AnyCardType
|
||||
from smartcard.CardRequest import CardRequest
|
||||
from smartcard.Exceptions import CardRequestTimeoutException, CardConnectionException
|
||||
except ModuleNotFoundError:
|
||||
print('ERROR: smarctard module not found! Install pyscard package.\nTry with `pip install pyscard`')
|
||||
sys.exit(-1)
|
||||
|
||||
class APDUResponse(Exception):
|
||||
def __init__(self, sw1, sw2):
|
||||
self.sw1 = sw1
|
||||
self.sw2 = sw2
|
||||
super().__init__(f'SW:{sw1:02X}{sw2:02X}')
|
||||
|
||||
def send_apdu(card, command, p1, p2, data=None, ne=None):
|
||||
lc = []
|
||||
dataf = []
|
||||
if (data):
|
||||
lc = [0x00] + list(len(data).to_bytes(2, 'big'))
|
||||
dataf = data
|
||||
if (ne is None):
|
||||
le = [0x00, 0x00]
|
||||
else:
|
||||
le = list(ne.to_bytes(2, 'big'))
|
||||
if (isinstance(command, list) and len(command) > 1):
|
||||
apdu = command
|
||||
else:
|
||||
apdu = [0x00, command]
|
||||
|
||||
apdu = apdu + [p1, p2] + lc + dataf + le
|
||||
try:
|
||||
response, sw1, sw2 = card.connection.transmit(apdu)
|
||||
except CardConnectionException:
|
||||
card.connection.reconnect()
|
||||
response, sw1, sw2 = card.connection.transmit(apdu)
|
||||
if (sw1 != 0x90):
|
||||
raise APDUResponse(sw1, sw2)
|
||||
return response
|
||||
|
||||
|
||||
def verify(MC, GA, client_data_hash):
|
||||
credential_data = AttestedCredentialData(MC.auth_data.credential_data)
|
||||
|
||||
Reference in New Issue
Block a user