diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index f3fc397..ba8d07f 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -36,6 +36,7 @@ jobs: language: [ 'cpp', 'python' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + mode: [ 'pico', 'esp32', 'local' ] steps: - name: Checkout repository @@ -67,7 +68,7 @@ jobs: - run: | echo "Run, Build Application using script" - ./workflows/autobuild.sh + ./workflows/autobuild.sh ${{ matrix.mode }} - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 diff --git a/CMakeLists.txt b/CMakeLists.txt index 89f4936..c89573a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,6 +77,7 @@ endif(ENABLE_OTP_APP) if(ENABLE_OTP_APP OR ENABLE_OATH_APP) set(USB_ITF_CCID 1) + set(USB_ITF_WCID 1) else() set(USB_ITF_CCID 0) endif() diff --git a/build_pico_fido.sh b/build_pico_fido.sh index b661942..8308979 100755 --- a/build_pico_fido.sh +++ b/build_pico_fido.sh @@ -2,6 +2,10 @@ VERSION_MAJOR="5" VERSION_MINOR="12" +SUFFIX="${VERSION_MAJOR}.${VERSION_MINOR}" +#if ! [[ -z "${GITHUB_SHA}" ]]; then +# SUFFIX="${SUFFIX}.${GITHUB_SHA}" +#fi rm -rf release/* mkdir -p build_release @@ -99,7 +103,6 @@ for board in 0xcb_helios \ do rm -rf * PICO_SDK_PATH="${PICO_SDK_PATH:-../../pico-sdk}" cmake .. -DPICO_BOARD=$board - make -kj20 - mv pico_fido.uf2 ../release/pico_fido_$board-$VERSION_MAJOR.$VERSION_MINOR.uf2 - + make -j`nproc` + mv pico_fido.uf2 ../release/pico_fido_$board-$SUFFIX.uf2 done diff --git a/pico-keys-sdk b/pico-keys-sdk index 9f65a2c..8c25e9b 160000 --- a/pico-keys-sdk +++ b/pico-keys-sdk @@ -1 +1 @@ -Subproject commit 9f65a2cfa024b721a6b7c16863e00558ac1a6f88 +Subproject commit 8c25e9be87f5556738550d309358198163111420 diff --git a/pico_sdk_import.cmake b/pico_sdk_import.cmake index 28efe9e..a0721d0 100644 --- a/pico_sdk_import.cmake +++ b/pico_sdk_import.cmake @@ -18,9 +18,20 @@ if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_P message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") endif () +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") if (NOT PICO_SDK_PATH) if (PICO_SDK_FETCH_FROM_GIT) @@ -29,11 +40,22 @@ if (NOT PICO_SDK_PATH) if (PICO_SDK_FETCH_FROM_GIT_PATH) get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") endif () - FetchContent_Declare( - pico_sdk - GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk - GIT_TAG master - ) + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + ) + else () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + endif () + if (NOT pico_sdk) message("Downloading Raspberry Pi Pico SDK") FetchContent_Populate(pico_sdk) diff --git a/sdkconfig.defaults b/sdkconfig.defaults index 1cb2487..2014bb3 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -11,6 +11,7 @@ CONFIG_PARTITION_TABLE_FILENAME="pico-keys-sdk/config/esp32/partitions.csv" CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y CONFIG_WL_SECTOR_SIZE_512=y CONFIG_WL_SECTOR_MODE_PERF=y +COMPILER_OPTIMIZATION="Performance" CONFIG_MBEDTLS_CMAC_C=y CONFIG_MBEDTLS_CHACHA20_C=y diff --git a/src/fido/cbor.c b/src/fido/cbor.c index 74c5822..68842b7 100644 --- a/src/fido/cbor.c +++ b/src/fido/cbor.c @@ -15,6 +15,7 @@ * along with this program. If not, see . */ +#include "pico_keys.h" #if !defined(ENABLE_EMULATION) && !defined(ESP_PLATFORM) #include "pico/stdlib.h" #endif @@ -119,8 +120,13 @@ void cbor_thread(void) { DEBUG_DATA(res_APDU + 1, res_APDU_size); } else { - res_APDU[0] = apdu.sw; - //apdu.sw = 0; + if (apdu.sw >= CTAP1_ERR_INVALID_CHANNEL) { + res_APDU[-1] = apdu.sw; + apdu.sw = 0; + } + else { + res_APDU[0] = apdu.sw; + } } finished_data_size = res_APDU_size + 1; diff --git a/src/fido/cbor_config.c b/src/fido/cbor_config.c index d12b11a..f443ea9 100644 --- a/src/fido/cbor_config.c +++ b/src/fido/cbor_config.c @@ -27,6 +27,7 @@ #include "mbedtls/ecdh.h" #include "mbedtls/chachapoly.h" #include "mbedtls/sha256.h" +#include "file.h" extern uint8_t keydev_dec[32]; extern bool has_keydev_dec; @@ -35,7 +36,7 @@ int cbor_config(const uint8_t *data, size_t len) { CborParser parser; CborValue map; CborError error = CborNoError; - uint64_t subcommand = 0, pinUvAuthProtocol = 0, vendorCommandId = 0, newMinPinLength = 0; + uint64_t subcommand = 0, pinUvAuthProtocol = 0, vendorCommandId = 0, newMinPinLength = 0, vendorParam = 0; CborByteString pinUvAuthParam = { 0 }, vendorAutCt = { 0 }; CborCharString minPinLengthRPIDs[32] = { 0 }; size_t resp_size = 0, raw_subpara_len = 0, minPinLengthRPIDs_len = 0; @@ -65,7 +66,7 @@ int cbor_config(const uint8_t *data, size_t len) { raw_subpara = (uint8_t *) cbor_value_get_next_byte(&_f1); CBOR_PARSE_MAP_START(_f1, 2) { - if (subcommand == 0x7f) { + if (subcommand == 0x7f) { // Config Aut CBOR_FIELD_GET_UINT(subpara, 2); if (subpara == 0x01) { CBOR_FIELD_GET_UINT(vendorCommandId, 2); @@ -74,7 +75,7 @@ int cbor_config(const uint8_t *data, size_t len) { CBOR_FIELD_GET_BYTES(vendorAutCt, 2); } } - else if (subcommand == 0x03) { + else if (subcommand == 0x03) { // Extensions CBOR_FIELD_GET_UINT(subpara, 2); if (subpara == 0x01) { CBOR_FIELD_GET_UINT(newMinPinLength, 2); @@ -94,6 +95,15 @@ int cbor_config(const uint8_t *data, size_t len) { CBOR_FIELD_GET_BOOL(forceChangePin, 2); } } + else if (subcommand == 0x1B) { // PHY + CBOR_FIELD_GET_UINT(subpara, 2); + if (subpara == 0x01) { + CBOR_FIELD_GET_UINT(vendorCommandId, 2); + } + else if (subpara == 0x02) { + CBOR_FIELD_GET_UINT(vendorParam, 2); + } + } } CBOR_PARSE_MAP_END(_f1, 2); raw_subpara_len = cbor_value_get_next_byte(&_f1) - raw_subpara; @@ -212,6 +222,40 @@ int cbor_config(const uint8_t *data, size_t len) { set_opts(get_opts() | FIDO2_OPT_EA); goto err; } +#ifndef ENABLE_EMULATION + else if (subcommand == 0x1B) { + if (vendorParam == 0) { + CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER); + } + if (vendorCommandId == CTAP_CONFIG_PHY_VIDPID) { + phy_data.vid = (vendorParam >> 16) & 0xFFFF; + phy_data.pid = vendorParam & 0xFFFF; + phy_data.vidpid_present = true; + } + else if (vendorCommandId == CTAP_CONFIG_PHY_LED_GPIO) { + phy_data.led_gpio = (uint8_t)vendorParam; + phy_data.led_gpio_present = true; + } + else if (vendorCommandId == CTAP_CONFIG_PHY_LED_BTNESS) { + phy_data.led_brightness = (uint8_t)vendorParam; + phy_data.led_brightness_present = true; + } + else if (vendorCommandId == CTAP_CONFIG_PHY_OPTS) { + phy_data.opts = (uint16_t)vendorParam; + } + else { + CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION); + } + uint8_t tmp[PHY_MAX_SIZE]; + uint16_t tmp_len = 0; + memset(tmp, 0, sizeof(tmp)); + if (phy_serialize_data(&phy_data, tmp, &tmp_len) != PICOKEY_OK) { + CBOR_ERROR(CTAP2_ERR_PROCESSING); + } + file_put_data(ef_phy, tmp, tmp_len); + low_flash_available(); + } +#endif else { CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION); } diff --git a/src/fido/cbor_make_credential.c b/src/fido/cbor_make_credential.c index 2a99b08..5640b2f 100644 --- a/src/fido/cbor_make_credential.c +++ b/src/fido/cbor_make_credential.c @@ -45,7 +45,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) { CredExtensions extensions = { 0 }; //options.present = true; //options.up = ptrue; - //options.uv = pfalse; + options.uv = pfalse; //options.rk = pfalse; CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map)); @@ -246,7 +246,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) { //else if (options.up == NULL) //5.7 //rup = ptrue; } - if (pinUvAuthParam.present == false && options.uv != ptrue && file_has_data(ef_pin)) { //8.1 + if (pinUvAuthParam.present == false && options.uv == pfalse && file_has_data(ef_pin)) { //8.1 CBOR_ERROR(CTAP2_ERR_PUAT_REQUIRED); } if (enterpriseAttestation > 0) { @@ -364,8 +364,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) { 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")); @@ -440,13 +439,37 @@ int cbor_make_credential(const uint8_t *data, size_t len) { if (enterpriseAttestation == 2 || (ka && ka->use_self_attestation == pfalse)) { mbedtls_ecdsa_free(&ekey); mbedtls_ecdsa_init(&ekey); - ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &ekey, file_get_data(ef_keydev), 32); + uint8_t key[32] = {0}; + if (load_keydev(key) != 0) { + CBOR_ERROR(CTAP1_ERR_OTHER); + } + ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &ekey, key, 32); + mbedtls_platform_zeroize(key, sizeof(key)); md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); self_attestation = false; } ret = mbedtls_ecdsa_write_signature(&ekey, mbedtls_md_get_type(md), hash, mbedtls_md_get_size(md), sig, sizeof(sig), &olen, random_gen, NULL); mbedtls_ecdsa_free(&ekey); + if (user.id.len > 0 && user.parent.name.len > 0 && user.displayName.len > 0) { + if (memcmp(user.parent.name.data, "+pico", 5) == 0) { + options.rk = pfalse; +#ifndef ENABLE_EMULATION + uint8_t *p = (uint8_t *)user.parent.name.data + 5; + if (memcmp(p, "CommissionProfile", 17) == 0) { + ret = phy_unserialize_data(user.id.data, user.id.len, &phy_data); + if (ret == PICOKEY_OK) { + file_put_data(ef_phy, user.id.data, user.id.len); + } + } +#endif + if (ret != 0) { + CBOR_ERROR(CTAP2_ERR_PROCESSING); + } + low_flash_available(); + } + } + uint8_t largeBlobKey[32] = {0}; if (extensions.largeBlobKey == ptrue && options.rk == ptrue) { ret = credential_derive_large_blob_key(cred_id, cred_id_len, largeBlobKey); diff --git a/src/fido/cbor_vendor.c b/src/fido/cbor_vendor.c index 3e99c92..501a22e 100644 --- a/src/fido/cbor_vendor.c +++ b/src/fido/cbor_vendor.c @@ -37,14 +37,7 @@ 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; } @@ -112,8 +105,7 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) { 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) { @@ -140,11 +132,7 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) { 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); @@ -160,37 +148,19 @@ 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); @@ -248,9 +218,7 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) { } 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 %s", pico_serial_str); + snprintf((char *) buffer, sizeof(buffer), "C=ES,O=Pico Keys,OU=Authenticator Attestation,CN=Pico Fido EE Serial %s", pico_serial_str); mbedtls_x509write_csr_set_subject_name(&ctx, (char *) buffer); mbedtls_pk_context key; mbedtls_pk_init(&key); @@ -258,12 +226,7 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) { 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) { @@ -286,6 +249,23 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) { goto err; } } +#ifndef ENABLE_EMULATION + else if (cmd == CTAP_VENDOR_PHY_OPTS) { + if (vendorCmd == 0x01) { + uint16_t opts = 0; + if (file_has_data(ef_phy)) { + uint8_t *data = file_get_data(ef_phy); + opts = (data[PHY_OPTS] << 8) | data[PHY_OPTS+1]; + } + CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1)); + CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01)); + CBOR_CHECK(cbor_encode_uint(&mapEncoder, opts)); + } + else { + CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION); + } + } + #endif else { CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION); } diff --git a/src/fido/cmd_authenticate.c b/src/fido/cmd_authenticate.c index 81e71a4..aecf75b 100644 --- a/src/fido/cmd_authenticate.c +++ b/src/fido/cmd_authenticate.c @@ -26,7 +26,7 @@ int cmd_authenticate() { CTAP_AUTHENTICATE_REQ *req = (CTAP_AUTHENTICATE_REQ *) apdu.data; CTAP_AUTHENTICATE_RESP *resp = (CTAP_AUTHENTICATE_RESP *) res_APDU; - //if (scan_files(true) != CCID_OK) + //if (scan_files(true) != PICOKEY_OK) // return SW_EXEC_ERROR(); if (apdu.nc < CTAP_CHAL_SIZE + CTAP_APPID_SIZE + 1 + 1) { return SW_WRONG_DATA(); @@ -55,7 +55,7 @@ int cmd_authenticate() { } } free(tmp_kh); - if (ret != CCID_OK) { + if (ret != PICOKEY_OK) { mbedtls_ecdsa_free(&key); return SW_EXEC_ERROR(); } diff --git a/src/fido/cmd_register.c b/src/fido/cmd_register.c index 325508c..f62bf72 100644 --- a/src/fido/cmd_register.c +++ b/src/fido/cmd_register.c @@ -37,9 +37,9 @@ int u2f_select(app_t *a, uint8_t force) { if (cap_supported(CAP_U2F)) { a->process_apdu = u2f_process_apdu; a->unload = u2f_unload; - return CCID_OK; + return PICOKEY_OK; } - return CCID_ERR_FILE_NOT_FOUND; + return PICOKEY_ERR_FILE_NOT_FOUND; } INITIALIZER ( u2f_ctor ) { @@ -47,7 +47,7 @@ INITIALIZER ( u2f_ctor ) { } int u2f_unload() { - return CCID_OK; + return PICOKEY_OK; } 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"; @@ -59,7 +59,7 @@ int cmd_register() { 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) + //if (scan_files(true) != PICOKEY_OK) // return SW_EXEC_ERROR(); if (apdu.nc != CTAP_APPID_SIZE + CTAP_CHAL_SIZE) { return SW_WRONG_LENGTH(); @@ -77,7 +77,7 @@ int cmd_register() { mbedtls_ecdsa_context key; mbedtls_ecdsa_init(&key); int ret = derive_key(req->appId, true, resp->keyHandleCertSig, MBEDTLS_ECP_DP_SECP256R1, &key); - if (ret != CCID_OK) { + if (ret != PICOKEY_OK) { mbedtls_ecdsa_free(&key); return SW_EXEC_ERROR(); } @@ -100,8 +100,14 @@ int cmd_register() { 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) { + uint8_t key_dev[32] = {0}; + ret = load_keydev(key_dev); + if (ret != PICOKEY_OK) { + return SW_EXEC_ERROR(); + } + ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &key, key_dev, 32); + mbedtls_platform_zeroize(key_dev, sizeof(key_dev)); + if (ret != PICOKEY_OK) { mbedtls_ecdsa_free(&key); return SW_EXEC_ERROR(); } diff --git a/src/fido/ctap.h b/src/fido/ctap.h index 79d00f6..6d22edf 100644 --- a/src/fido/ctap.h +++ b/src/fido/ctap.h @@ -114,6 +114,10 @@ typedef struct { #define CTAP_CONFIG_AUT_ENABLE 0x03e43f56b34285e2 #define CTAP_CONFIG_AUT_DISABLE 0x1831a40f04a25ed9 +#define CTAP_CONFIG_PHY_VIDPID 0x6fcb19b0cbe3acfa +#define CTAP_CONFIG_PHY_LED_GPIO 0x7b392a394de9f948 +#define CTAP_CONFIG_PHY_LED_BTNESS 0x76a85945985d02fd +#define CTAP_CONFIG_PHY_OPTS 0x969f3b09eceb805f #define CTAP_VENDOR_CBOR (CTAPHID_VENDOR_FIRST + 1) @@ -121,6 +125,7 @@ typedef struct { #define CTAP_VENDOR_MSE 0x02 #define CTAP_VENDOR_UNLOCK 0x03 #define CTAP_VENDOR_EA 0x04 +#define CTAP_VENDOR_PHY_OPTS 0x05 #define CTAP_PERMISSION_MC 0x01 // MakeCredential #define CTAP_PERMISSION_GA 0x02 // GetAssertion diff --git a/src/fido/fido.c b/src/fido/fido.c index 7ac7e4e..67a3a1e 100644 --- a/src/fido/fido.c +++ b/src/fido/fido.c @@ -34,10 +34,14 @@ #include "management.h" #include "hid/ctap_hid.h" #include "version.h" +#include "crypto_utils.h" +#include "otp.h" int fido_process_apdu(); int fido_unload(); +uint8_t PICO_PRODUCT = 2; // Pico FIDO + pinUvAuthToken_t paut = { 0 }; uint8_t keydev_dec[32]; @@ -66,9 +70,9 @@ int fido_select(app_t *a, uint8_t force) { if (cap_supported(CAP_FIDO2)) { a->process_apdu = fido_process_apdu; a->unload = fido_unload; - return CCID_OK; + return PICOKEY_OK; } - return CCID_ERR_FILE_NOT_FOUND; + return PICOKEY_ERR_FILE_NOT_FOUND; } extern uint8_t (*get_version_major)(); @@ -84,7 +88,7 @@ INITIALIZER ( fido_ctor ) { } int fido_unload() { - return CCID_OK; + return PICOKEY_OK; } mbedtls_ecp_group_id fido_curve_to_mbedtls(int curve) { @@ -176,16 +180,23 @@ int x509_create_cert(mbedtls_ecdsa_context *ecdsa, uint8_t *buffer, size_t buffe int load_keydev(uint8_t *key) { if (has_keydev_dec == false && !file_has_data(ef_keydev)) { - return CCID_ERR_MEMORY_FATAL; + return PICOKEY_ERR_MEMORY_FATAL; } + if (has_keydev_dec == true) { memcpy(key, keydev_dec, sizeof(keydev_dec)); } else { memcpy(key, file_get_data(ef_keydev), file_get_size(ef_keydev)); +#ifdef PICO_RP2350 + if (aes_decrypt(otp_key_1, NULL, 32 * 8, PICO_KEYS_AES_MODE_CBC, key, 32) != PICOKEY_OK) { + return PICOKEY_EXEC_ERROR; + } +#endif } + //return mkek_decrypt(key, file_get_size(ef_keydev)); - return CCID_OK; + return PICOKEY_OK; } int verify_key(const uint8_t *appId, const uint8_t *keyHandle, mbedtls_ecdsa_context *key) { @@ -225,7 +236,7 @@ int derive_key(const uint8_t *app_id, bool new_key, uint8_t *key_handle, int cur uint8_t outk[67] = { 0 }; //SECP521R1 key is 66 bytes length int r = 0; memset(outk, 0, sizeof(outk)); - if ((r = load_keydev(outk)) != CCID_OK) { + if ((r = load_keydev(outk)) != PICOKEY_OK) { printf("Error loading keydev: %d\n", r); return r; } @@ -289,13 +300,16 @@ int scan_files() { uint8_t kdata[64]; size_t key_size = 0; ret = mbedtls_ecp_write_key_ext(&ecdsa, &key_size, kdata, sizeof(kdata)); - if (ret != CCID_OK) { + if (ret != PICOKEY_OK) { return ret; } +#ifdef PICO_RP2350 + ret = aes_encrypt(otp_key_1, NULL, 32 * 8, PICO_KEYS_AES_MODE_CBC, kdata, 32); +#endif ret = file_put_data(ef_keydev, kdata, (uint16_t)key_size); mbedtls_platform_zeroize(kdata, sizeof(kdata)); mbedtls_ecdsa_free(&ecdsa); - if (ret != CCID_OK) { + if (ret != PICOKEY_OK) { return ret; } printf(" done!\n"); @@ -360,7 +374,7 @@ int scan_files() { file_put_data(ef_largeblob, (const uint8_t *) "\x80\x76\xbe\x8b\x52\x8d\x00\x75\xf7\xaa\xe9\x8d\x6f\xa5\x7a\x6d\x3c", 17); } low_flash_available(); - return CCID_OK; + return PICOKEY_OK; } void scan_all() { diff --git a/src/fido/management.c b/src/fido/management.c index 57cd47c..8c25a9c 100644 --- a/src/fido/management.c +++ b/src/fido/management.c @@ -42,7 +42,7 @@ int man_select(app_t *a, uint8_t force) { scan_all(); init_otp(); } - return CCID_OK; + return PICOKEY_OK; } INITIALIZER ( man_ctor ) { @@ -50,7 +50,7 @@ INITIALIZER ( man_ctor ) { } int man_unload() { - return CCID_OK; + return PICOKEY_OK; } bool cap_supported(uint16_t cap) { @@ -135,12 +135,20 @@ int cmd_write_config() { return SW_OK(); } +extern int cbor_reset(); +int cmd_factory_reset() { + cbor_reset(); + return SW_OK(); +} + #define INS_READ_CONFIG 0x1D #define INS_WRITE_CONFIG 0x1C +#define INS_RESET 0x1E // Reset device static const cmd_t cmds[] = { { INS_READ_CONFIG, cmd_read_config }, { INS_WRITE_CONFIG, cmd_write_config }, + { INS_RESET, cmd_factory_reset }, { 0x00, 0x0 } }; diff --git a/src/fido/oath.c b/src/fido/oath.c index b0c7cc4..8e396d1 100644 --- a/src/fido/oath.c +++ b/src/fido/oath.c @@ -100,9 +100,9 @@ int oath_select(app_t *a, uint8_t force) { res_APDU[res_APDU_size++] = 1; res_APDU[res_APDU_size++] = ALG_HMAC_SHA1; apdu.ne = res_APDU_size; - return CCID_OK; + return PICOKEY_OK; } - return CCID_ERR_FILE_NOT_FOUND; + return PICOKEY_ERR_FILE_NOT_FOUND; } INITIALIZER ( oath_ctor ) { @@ -110,7 +110,7 @@ INITIALIZER ( oath_ctor ) { } int oath_unload() { - return CCID_OK; + return PICOKEY_OK; } file_t *find_oath_cred(const uint8_t *name, size_t name_len) { @@ -337,7 +337,7 @@ int calculate_oath(uint8_t truncate, const uint8_t *key, size_t key_len, const u 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; + return PICOKEY_EXEC_ERROR; } if (truncate == 0x01) { res_APDU[res_APDU_size++] = 4 + 1; @@ -354,7 +354,7 @@ int calculate_oath(uint8_t truncate, const uint8_t *key, size_t key_len, const u memcpy(res_APDU + res_APDU_size, hmac, hmac_size); res_APDU_size += (uint16_t)hmac_size; } apdu.ne = res_APDU_size; - return CCID_OK; + return PICOKEY_OK; } int cmd_calculate() { @@ -391,7 +391,7 @@ int cmd_calculate() { res_APDU[res_APDU_size++] = TAG_RESPONSE + P2(apdu); int ret = calculate_oath(P2(apdu), key.data, key.len, chal.data, chal.len); - if (ret != CCID_OK) { + if (ret != PICOKEY_OK) { return SW_EXEC_ERROR(); } if ((key.data[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) { @@ -466,7 +466,7 @@ int cmd_calculate_all() { else { res_APDU[res_APDU_size++] = TAG_RESPONSE + P2(apdu); int ret = calculate_oath(P2(apdu), key.data, key.len, chal.data, chal.len); - if (ret != CCID_OK) { + if (ret != PICOKEY_OK) { res_APDU[res_APDU_size++] = 1; res_APDU[res_APDU_size++] = key.data[1]; } @@ -581,7 +581,7 @@ int cmd_verify_hotp() { } int ret = calculate_oath(0x01, key.data, key.len, chal.data, chal.len); - if (ret != CCID_OK) { + if (ret != PICOKEY_OK) { return SW_EXEC_ERROR(); } uint32_t res_int = (res_APDU[2] << 24) | (res_APDU[3] << 16) | (res_APDU[4] << 8) | res_APDU[5]; diff --git a/src/fido/otp.c b/src/fido/otp.c index 67089b5..2970aeb 100644 --- a/src/fido/otp.c +++ b/src/fido/otp.c @@ -144,9 +144,9 @@ int otp_select(app_t *a, uint8_t force) { memmove(res_APDU, res_APDU + 1, 6); res_APDU_size = 6; apdu.ne = res_APDU_size; - return CCID_OK; + return PICOKEY_OK; } - return CCID_ERR_FILE_NOT_FOUND; + return PICOKEY_ERR_FILE_NOT_FOUND; } uint8_t modhex_tab[] = @@ -243,7 +243,7 @@ int otp_button_pressed(uint8_t slot) { { imf >> 56, imf >> 48, imf >> 40, imf >> 32, imf >> 24, imf >> 16, imf >> 8, imf & 0xff }; res_APDU_size = 0; int ret = calculate_oath(1, tmp_key, sizeof(tmp_key), chal, sizeof(chal)); - if (ret == CCID_OK) { + if (ret == PICOKEY_OK) { uint32_t base = otp_config->cfg_flags & OATH_HOTP8 ? 1e8 : 1e6; uint32_t number = (res_APDU[2] << 24) | (res_APDU[3] << 16) | (res_APDU[4] << 8) | res_APDU[5]; @@ -348,7 +348,7 @@ INITIALIZER( otp_ctor ) { } int otp_unload() { - return CCID_OK; + return PICOKEY_OK; } uint16_t otp_status() { diff --git a/tools/pico-fido-tool.py b/tools/pico-fido-tool.py index 89d1615..377e6b6 100644 --- a/tools/pico-fido-tool.py +++ b/tools/pico-fido-tool.py @@ -77,11 +77,17 @@ class VendorConfig(Config): class PARAM(IntEnum): VENDOR_COMMAND_ID = 0x01 VENDOR_AUT_CT = 0x02 + VENDOR_PARAM = 0x02 class CMD(IntEnum): - CONFIG_AUT_ENABLE = 0x03e43f56b34285e2 - CONFIG_AUT_DISABLE = 0x1831a40f04a25ed9 + CONFIG_AUT_ENABLE = 0x03e43f56b34285e2 + CONFIG_AUT_DISABLE = 0x1831a40f04a25ed9 CONFIG_VENDOR_PROTOTYPE = 0x7f + CONFIG_VENDOR_PHY = 0x1b + CONFIG_PHY_VIDPID = 0x6fcb19b0cbe3acfa + CONFIG_PHY_OPTS = 0x969f3b09eceb805f + CONFIG_PHY_LED_GPIO = 0x7b392a394de9f948 + CONFIG_PHY_LED_BTNESS = 0x76a85945985d02fd class RESP(IntEnum): KEY_AGREEMENT = 0x01 @@ -106,6 +112,42 @@ class VendorConfig(Config): }, ) + def vidpid(self, vid, pid): + self._call( + VendorConfig.CMD.CONFIG_VENDOR_PHY, + { + VendorConfig.PARAM.VENDOR_COMMAND_ID: VendorConfig.CMD.CONFIG_PHY_VIDPID, + VendorConfig.PARAM.VENDOR_PARAM: (vid & 0xFFFF) << 16 | pid + }, + ) + + def led_gpio(self, gpio): + self._call( + VendorConfig.CMD.CONFIG_VENDOR_PHY, + { + VendorConfig.PARAM.VENDOR_COMMAND_ID: VendorConfig.CMD.CONFIG_PHY_LED_GPIO, + VendorConfig.PARAM.VENDOR_PARAM: gpio + }, + ) + + def led_brightness(self, brightness): + self._call( + VendorConfig.CMD.CONFIG_VENDOR_PHY, + { + VendorConfig.PARAM.VENDOR_COMMAND_ID: VendorConfig.CMD.CONFIG_PHY_LED_BTNESS, + VendorConfig.PARAM.VENDOR_PARAM: brightness + }, + ) + + def phy_opts(self, opts): + self._call( + VendorConfig.CMD.CONFIG_VENDOR_PHY, + { + VendorConfig.PARAM.VENDOR_COMMAND_ID: VendorConfig.CMD.CONFIG_PHY_OPTS, + VendorConfig.PARAM.VENDOR_PARAM: opts + }, + ) + class Ctap2Vendor(Ctap2): def __init__(self, device: CtapDevice, strict_cbor: bool = True): super().__init__(device=device, strict_cbor=strict_cbor) @@ -190,6 +232,7 @@ class Vendor: VENDOR_MSE = 0x02 VENDOR_UNLOCK = 0x03 VENDOR_EA = 0x04 + VENDOR_PHY = 0x05 @unique class PARAM(IntEnum): @@ -207,6 +250,10 @@ class Vendor: PARAM = 0x01 COSE_KEY = 0x02 + class PHY_OPTS(IntEnum): + PHY_OPT_WCID = 0x1 + PHY_OPT_DIMM = 0x2 + def __init__( self, ctap: Ctap2Vendor, @@ -393,6 +440,41 @@ class Vendor: } ) + def vidpid(self, vid, pid): + return self.vcfg.vidpid(vid, pid) + + def led_gpio(self, gpio): + return self.vcfg.led_gpio(gpio) + + def led_brightness(self, brightness): + if (brightness > 15): + print('ERROR: Brightness must be between 0 and 15') + return + return self.vcfg.led_brightness(brightness) + + def led_dimmable(self, onoff): + opts = self.phy_opts() + if (onoff): + opts |= Vendor.PHY_OPTS.PHY_OPT_DIMM + else: + opts &= ~Vendor.PHY_OPTS.PHY_OPT_DIMM + print(f'opts: {opts}') + return self.vcfg.phy_opts(opts) + + def wcid(self, onoff): + opts = self.phy_opts() + if (onoff): + opts |= Vendor.PHY_OPTS.PHY_OPT_WCID + else: + opts &= ~Vendor.PHY_OPTS.PHY_OPT_WCID + return self.vcfg.phy_opts(opts) + + def phy_opts(self): + return self._call( + Vendor.CMD.VENDOR_PHY, + Vendor.SUBCMD.ENABLE, + )[Vendor.RESP.PARAM] + def parse_args(): parser = argparse.ArgumentParser() subparser = parser.add_subparsers(title="commands", dest="command") @@ -408,6 +490,19 @@ def parse_args(): parser_attestation.add_argument('subcommand', choices=['csr']) parser_attestation.add_argument('--filename', help='Uploads the certificate filename to the device as enterprise attestation certificate. If not provided, it will generate an enterprise attestation certificate automatically.') + parser_phy = subparser.add_parser('phy', help='Set PHY options.') + subparser_phy = parser_phy.add_subparsers(title='commands', dest='subcommand', required=True) + parser_phy_vp = subparser_phy.add_parser('vidpid', help='Sets VID/PID. Use VID:PID format (e.g. 1234:5678)') + parser_phy_vp.add_argument('value', help='Value of the PHY option.', metavar='VAL', nargs='?') + parser_phy_ledn = subparser_phy.add_parser('led_gpio', help='Sets LED GPIO number.') + parser_phy_ledn.add_argument('value', help='Value of the PHY option.', metavar='VAL', nargs='?') + parser_phy_optwcid = subparser_phy.add_parser('wcid', help='Enable/Disable Web CCID interface.') + parser_phy_optwcid.add_argument('value', choices=['enable', 'disable'], help='Enable/Disable Web CCID interface.', nargs='?') + parser_phy_ledbtness = subparser_phy.add_parser('led_brightness', help='Sets LED max. brightness.') + parser_phy_ledbtness.add_argument('value', help='Value of the max. brightness.', metavar='VAL', nargs='?') + parser_phy_optdimm = subparser_phy.add_parser('led_dimmable', help='Enable/Disable LED dimming.') + parser_phy_optdimm.add_argument('value', choices=['enable', 'disable'], help='Enable/Disable LED dimming.', nargs='?') + args = parser.parse_args() return args @@ -441,8 +536,33 @@ def attestation(vdr, args): cert = x509.load_pem_x509_certificate(dataf) vdr.upload_ea(cert.public_bytes(Encoding.DER)) +def phy(vdr, args): + val = args.value if 'value' in args else None + if (val): + if (args.subcommand == 'vidpid'): + sp = val.split(':') + if (len(sp) != 2): + print('ERROR: VID/PID have wrong format. Use VID:PID format (e.g. 1234:5678)') + ret = vdr.vidpid(int(sp[0],16), int(sp[1],16)) + elif (args.subcommand == 'led_gpio'): + val = int(val) + ret = vdr.led_gpio(val) + elif (args.subcommand == 'led_brightness'): + val = int(val) + ret = vdr.led_brightness(val) + elif (args.subcommand == 'led_dimmable'): + ret = vdr.led_dimmable(val == 'enable') + elif (args.subcommand == 'wcid'): + ret = vdr.wcid(val == 'enable') + + if (ret): + print(f'Current value: {hexlify(ret)}') + else: + print('Command executed successfully. Please, restart your Pico Key.') + + def main(args): - print('Pico Fido Tool v1.6') + print('Pico Fido Tool v1.8') print('Author: Pol Henarejos') print('Report bugs to https://github.com/polhenarejos/pico-fido/issues') print('') @@ -460,6 +580,8 @@ def main(args): backup(vdr, args) elif (args.command == 'attestation'): attestation(vdr, args) + elif (args.command == 'phy'): + phy(vdr, args) def run(): args = parse_args() diff --git a/workflows/autobuild.sh b/workflows/autobuild.sh index 09ef1c7..d90e1a4 100755 --- a/workflows/autobuild.sh +++ b/workflows/autobuild.sh @@ -2,12 +2,38 @@ git submodule update --init --recursive sudo apt update + +if [[ $1 == "pico" ]]; then sudo apt install -y cmake gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib git clone https://github.com/raspberrypi/pico-sdk cd pico-sdk git submodule update --init cd .. +git clone https://github.com/raspberrypi/picotool +cd picotool +git submodule update --init mkdir build cd build +cmake -DPICO_SDK_PATH=../../pico-sdk .. +make -j`nproc` +sudo make install +cd ../.. +mkdir build_pico +cd build_pico cmake -DPICO_SDK_PATH=../pico-sdk .. make +elif [[ $1 == "esp32" ]]; then +sudo apt install -y git wget flex bison gperf python3 python3-pip python3-venv cmake ninja-build ccache libffi-dev libssl-dev dfu-util libusb-1.0-0 +git clone --recursive https://github.com/espressif/esp-idf.git +cd esp-idf +./install.sh esp32s3 +. ./export.sh +cd .. +idf.py set-target esp32s3 +idf.py all +else +mkdir build +cd build +cmake -DENABLE_EMULATION=1 .. +make +fi