From 3873303309eb325780ad52a89c05b793cf848ab1 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Thu, 15 Sep 2022 14:16:12 +0200 Subject: [PATCH] Refactor CTAP2 file structure. Signed-off-by: Pol Henarejos --- CMakeLists.txt | 5 + src/fido/cbor.c | 39 ++++++ src/fido/cbor_get_info.c | 78 +++++++++++ src/fido/cbor_make_credential.c | 227 ++++++++++++++++++++++++++++++++ src/fido/cbor_make_credential.h | 66 ++++++++++ src/fido/cbor_reset.c | 24 ++++ src/fido/ctap.h | 42 ++++++ src/fido/ctap2_cbor.h | 213 ++++++++++++++++++++++++++++++ src/fido/fido.c | 3 +- src/fido/fido.h | 8 ++ src/fido/files.c | 2 + src/fido/files.h | 2 + 12 files changed, 708 insertions(+), 1 deletion(-) create mode 100644 src/fido/cbor.c create mode 100644 src/fido/cbor_get_info.c create mode 100644 src/fido/cbor_make_credential.c create mode 100644 src/fido/cbor_make_credential.h create mode 100644 src/fido/cbor_reset.c create mode 100644 src/fido/ctap2_cbor.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f1af33a..ae5ddd3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,10 @@ target_sources(pico_fido PUBLIC ${CMAKE_CURRENT_LIST_DIR}/src/fido/cmd_register.c ${CMAKE_CURRENT_LIST_DIR}/src/fido/cmd_authenticate.c ${CMAKE_CURRENT_LIST_DIR}/src/fido/cmd_version.c + ${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor.c + ${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_get_info.c + ${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_make_credential.c ) set(HSM_DRIVER "hid") include(pico-hsm-sdk/pico_hsm_sdk_import.cmake) @@ -45,6 +49,7 @@ target_include_directories(pico_fido PUBLIC target_compile_options(pico_fido PUBLIC -Wall -Werror + -Wno-error=use-after-free ) pico_add_extra_outputs(pico_fido) diff --git a/src/fido/cbor.c b/src/fido/cbor.c new file mode 100644 index 0000000..f6f2cd0 --- /dev/null +++ b/src/fido/cbor.c @@ -0,0 +1,39 @@ +/* + * 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 . + */ + +#include +#include "pico/stdlib.h" +#include "ctap2_cbor.h" +#include "ctap.h" +#include "ctap_hid.h" + +bool _btrue = true, *ptrue = &_btrue, _bfalse = false, *pfalse = &_bfalse; + +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") + +int cbor_process(const uint8_t *data, size_t len) { + if (len == 0) + return -CTAP1_ERR_INVALID_LEN; + driver_prepare_response(); + if (data[0] == CTAP_MAKE_CREDENTIAL) + return cbor_make_credential(data + 1, len - 1); + if (data[0] == CTAP_GET_INFO) + return cbor_get_info(); + else if (data[0] == CTAP_RESET) + return cbor_reset(); + return -CTAP2_ERR_INVALID_CBOR; +} diff --git a/src/fido/cbor_get_info.c b/src/fido/cbor_get_info.c new file mode 100644 index 0000000..cc4a0cf --- /dev/null +++ b/src/fido/cbor_get_info.c @@ -0,0 +1,78 @@ +/* + * 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 . + */ + +#include "ctap2_cbor.h" +#include "fido.h" +#include "ctap.h" +#include "files.h" + +int cbor_get_info() { + CborEncoder encoder, mapEncoder, arrayEncoder; + CborError error = CborNoError; + cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0); + CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 7)); + + CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01)); + CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 2)); + CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "U2F_V2")); + CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "FIDO_2_0")); + CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &arrayEncoder)); + + CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x02)); + CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 1)); + CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "hmac-secret")); + CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &arrayEncoder)); + + CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03)); + CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, aaguid, sizeof(aaguid))); + + CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04)); + CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &arrayEncoder, 5)); + CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "rk")); + CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, true)); + CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "clientPin")); + if (file_has_data(ef_pin)) + CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, true)); + else + CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, false)); + CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "pinUvAuthToken")); + CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, true)); + CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "authnrCfg")); + CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, true)); + CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "credMgmt")); + CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, true)); + CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &arrayEncoder)); + + CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x06)); + CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 1)); + CBOR_CHECK(cbor_encode_uint(&arrayEncoder, 1)); // PIN protocols + CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &arrayEncoder)); + + CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x07)); + CBOR_CHECK(cbor_encode_uint(&mapEncoder, 10)); // MAX_CRED_COUNT_IN_LIST + + CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x08)); + CBOR_CHECK(cbor_encode_uint(&mapEncoder, 1024)); // CRED_ID_MAX_LENGTH + + CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder)); + err: + if (error != CborNoError) + return -CTAP2_ERR_INVALID_CBOR; + size_t rs = cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1); + driver_exec_finished(rs + 1); + return 0; +} diff --git a/src/fido/cbor_make_credential.c b/src/fido/cbor_make_credential.c new file mode 100644 index 0000000..7d1515c --- /dev/null +++ b/src/fido/cbor_make_credential.c @@ -0,0 +1,227 @@ +/* + * 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 . + */ + +#include "ctap2_cbor.h" +#include "cbor_make_credential.h" +#include "fido.h" +#include "ctap.h" +#include "files.h" + +bool credential_verify(CborByteString *cred_id, const uint8_t *rp_id_hash) { + if (cred_id->len < 4+12+16) + return false; + size_t cipher_len = cred_id->len - (4 + 12 + 16); + uint8_t key[32], *iv = cred_id->data + 4, *cipher = cred_id->data + 4 + 12, *tag = cred_id->data - 16, *data = (uint8_t *)calloc(1, cipher_len); + memset(key, 0, sizeof(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, data); + free(data); + if (ret == 0) + return true; + return false; +} + +int verify(CborByteString *clientDataHash, CborByteString *pinUvAuthParam) { + return CborNoError; +} + +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[16] = {0}; + size_t pubKeyCredParams_len = 0; + PublicKeyCredentialDescriptor excludeList[16] = {0}; + size_t excludeList_len = 0; + CredOptions options = {0}; + uint64_t pinUvAuthProtocol = 0, enterpriseAttestation = 0; + + CBOR_CHECK(cbor_parser_init(data, len, 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) { // clientDataHash + CBOR_FIELD_GET_BYTES(clientDataHash, 1); + } + else if (val_u == 0x02) { // rp + CBOR_PARSE_MAP_START(_f1, 2) { + CBOR_FIELD_GET_KEY_TEXT(2); + CBOR_FIELD_KEY_TEXT_VAL_TEXT(2, "id", rp.id); + CBOR_FIELD_KEY_TEXT_VAL_TEXT(2, "name", rp.parent.name); + } + CBOR_PARSE_MAP_END(_f1, 2); + } + else if (val_u == 0x03) { // user + CBOR_PARSE_MAP_START(_f1, 2) { + CBOR_FIELD_GET_KEY_TEXT(2); + CBOR_FIELD_KEY_TEXT_VAL_BYTES(2, "id", user.id); + CBOR_FIELD_KEY_TEXT_VAL_TEXT(2, "name", user.parent.name); + CBOR_FIELD_KEY_TEXT_VAL_TEXT(2, "displayName", user.displayName); + } + CBOR_PARSE_MAP_END(_f1, 2); + } + else if (val_u == 0x04) { // pubKeyCredParams + CBOR_PARSE_ARRAY_START(_f1, 2) { + PublicKeyCredentialParameters *pk = &pubKeyCredParams[pubKeyCredParams_len]; + CBOR_PARSE_MAP_START(_f2, 3) { + CBOR_FIELD_GET_KEY_TEXT(3); + CBOR_FIELD_KEY_TEXT_VAL_TEXT(3, "type", pk->type); + CBOR_FIELD_KEY_TEXT_VAL_INT(3, "alg", pk->alg); + } + CBOR_PARSE_MAP_END(_f2, 3); + pubKeyCredParams_len++; + } + CBOR_PARSE_ARRAY_END(_f1, 2); + } + else if (val_u == 0x05) { // excludeList + CBOR_PARSE_ARRAY_START(_f1, 2) { + PublicKeyCredentialDescriptor *pc = &excludeList[excludeList_len]; + CBOR_PARSE_MAP_START(_f1, 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(_f2, 4) { + CBOR_FIELD_GET_TEXT(pc->transports[pc->transports_len], 4); + pc->transports_len++; + } + CBOR_PARSE_ARRAY_END(_f2, 4); + } + } + CBOR_PARSE_MAP_END(_f1, 3); + excludeList_len++; + } + CBOR_PARSE_ARRAY_END(_f1, 2); + } + else if (val_u == 0x06) { // extensions + CBOR_ADVANCE(1); + } + else if (val_u == 0x07) { // options + options.present = true; + 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); + CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "uv", options.uv); + } + CBOR_PARSE_MAP_END(_f1, 2); + } + else if (val_u == 0x08) { // pinUvAuthParam + CBOR_FIELD_GET_BYTES(pinUvAuthParam, 1); + } + else if (val_u == 0x09) { // pinUvAuthProtocol + CBOR_FIELD_GET_UINT(pinUvAuthProtocol, 1); + } + else if (val_u == 0x0A) { // enterpriseAttestation + CBOR_FIELD_GET_UINT(enterpriseAttestation, 1); + } + } + CBOR_PARSE_MAP_END(map, 1); + + uint8_t rp_id_hash[32]; + mbedtls_sha256((uint8_t *)rp.id.data, rp.id.len, rp_id_hash, 0); + + for (int i = 0; i < pubKeyCredParams_len; i++) { + if (strcmp(pubKeyCredParams[i].type.data, "public-key") != 0) + continue; + if (pubKeyCredParams[i].alg != FIDO2_ALG_ES256 && pubKeyCredParams[i].alg != FIDO2_ALG_ES384 && pubKeyCredParams[i].alg != FIDO2_ALG_ES512) + CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_ALGORITHM); + } + + if (pinUvAuthParam.len == 0 || pinUvAuthParam.data == NULL) { + if (wait_button_pressed() == true) + CBOR_ERROR(CTAP2_ERR_OPERATION_DENIED); + if (!file_has_data(ef_pin)) + CBOR_ERROR(CTAP2_ERR_PIN_NOT_SET); + else + CBOR_ERROR(CTAP2_ERR_PIN_INVALID); + } + else if (pinUvAuthParam.present == true) { + if (pinUvAuthProtocol == 0) + CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER); + if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2) + CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER); + } + + bool *rup = pfalse; + if (options.present) { + if (options.uv == ptrue) { //5.3 + CBOR_ERROR(CTAP2_ERR_INVALID_OPTION); + } + if (options.up == pfalse) { //5.6 + CBOR_ERROR(CTAP2_ERR_INVALID_OPTION); + } + else if (options.up == NULL) //5.7 + rup = ptrue; + } + if (pinUvAuthParam.present == false && options.uv == pfalse && file_has_data(ef_pin)) { //8.1 + CBOR_ERROR(CTAP2_ERR_PUAT_REQUIRED); + } + if (enterpriseAttestation > 0) { + if (enterpriseAttestation != 1 && enterpriseAttestation != 2) { //9.2.1 + CBOR_ERROR(CTAP2_ERR_INVALID_OPTION); + } + //Unfinished. See 6.1.2.9 + } + if (pinUvAuthParam.present == true) { //11.1 + int ret = verify(&clientDataHash, &pinUvAuthParam); + if (ret != CborNoError) + CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID); + //Check pinUvAuthToken permissions. See 6.1.2.11 + } + + for (int e = 0; e < excludeList_len; e++) { //12.1 + if (strcmp(excludeList[e].type.data, "public-key") != 0) + continue; + if (credential_verify(&excludeList[e].id, rp_id_hash) == true) + CBOR_ERROR(CTAP2_ERR_CREDENTIAL_EXCLUDED); + } + + if (pinUvAuthParam.present && options.up == ptrue) { //14.1 + if (wait_button_pressed() == true) + CBOR_ERROR(CTAP2_ERR_OPERATION_DENIED); + rup = ptrue; + } + err: + CBOR_FREE_BYTE_STRING(clientDataHash); + CBOR_FREE_BYTE_STRING(pinUvAuthParam); + CBOR_FREE_BYTE_STRING(rp.id); + CBOR_FREE_BYTE_STRING(rp.parent.name); + CBOR_FREE_BYTE_STRING(user.id); + CBOR_FREE_BYTE_STRING(user.displayName); + CBOR_FREE_BYTE_STRING(user.parent.name); + for (int n = 0; n < pubKeyCredParams_len; n++) { + CBOR_FREE_BYTE_STRING(pubKeyCredParams[n].type); + } + + for (int m = 0; m < excludeList_len; m++) { + CBOR_FREE_BYTE_STRING(excludeList[m].type); + CBOR_FREE_BYTE_STRING(excludeList[m].id); + for (int n = 0; n < excludeList[m].transports_len; n++) { + CBOR_FREE_BYTE_STRING(excludeList[m].transports[n]); + } + } + if (error != CborNoError) + return -CTAP2_ERR_INVALID_CBOR; + driver_exec_finished(1); + return 0; +} diff --git a/src/fido/cbor_make_credential.h b/src/fido/cbor_make_credential.h new file mode 100644 index 0000000..8b7498e --- /dev/null +++ b/src/fido/cbor_make_credential.h @@ -0,0 +1,66 @@ +/* + * 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 . + */ + +#ifndef _CBOR_MAKE_CREDENTIAL_H_ +#define _CBOR_MAKE_CREDENTIAL_H_ + +#include "common.h" +#include "mbedtls/chachapoly.h" +#include +#include "pico/stdlib.h" +#include "ctap2_cbor.h" +#include "random.h" +#include "mbedtls/sha256.h" + +typedef struct PublicKeyCredentialEntity +{ + CborCharString name; +} PublicKeyCredentialEntity; + +typedef struct PublicKeyCredentialRpEntity +{ + PublicKeyCredentialEntity parent; + CborCharString id; +} PublicKeyCredentialRpEntity; + +typedef struct PublicKeyCredentialUserEntity +{ + PublicKeyCredentialEntity parent; + CborByteString id; + CborCharString displayName; +} PublicKeyCredentialUserEntity; + +typedef struct PublicKeyCredentialParameters { + CborCharString type; + int64_t alg; +} PublicKeyCredentialParameters; + +typedef struct PublicKeyCredentialDescriptor { + CborCharString type; + CborByteString id; + CborCharString transports[8]; + size_t transports_len; +} PublicKeyCredentialDescriptor; + +typedef struct CredOptions { + bool *rk; + bool *up; + bool *uv; + bool present; +} CredOptions; + +#endif //_CBOR_MAKE_CREDENTIAL_H_ diff --git a/src/fido/cbor_reset.c b/src/fido/cbor_reset.c new file mode 100644 index 0000000..54d1856 --- /dev/null +++ b/src/fido/cbor_reset.c @@ -0,0 +1,24 @@ + +/* + * 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 . + */ + +#include "ctap2_cbor.h" + +int cbor_reset() { + driver_exec_finished(1); + return 0; +} diff --git a/src/fido/ctap.h b/src/fido/ctap.h index bdd2576..56cbd96 100644 --- a/src/fido/ctap.h +++ b/src/fido/ctap.h @@ -112,6 +112,48 @@ typedef struct { #define CTAP_SW_COMMAND_NOT_ALLOWED 0x6986 // SW_COMMAND_NOT_ALLOWED #define CTAP_SW_INS_NOT_SUPPORTED 0x6D00 // SW_INS_NOT_SUPPORTED +#define CTAP2_ERR_CBOR_UNEXPECTED_TYPE 0x11 +#define CTAP2_ERR_INVALID_CBOR 0x12 +#define CTAP2_ERR_MISSING_PARAMETER 0x14 +#define CTAP2_ERR_LIMIT_EXCEEDED 0x15 +#define CTAP2_ERR_FP_DATABASE_FULL 0x17 +#define CTAP2_ERR_LARGE_BLOB_STORAGE_FULL 0x18 +#define CTAP2_ERR_CREDENTIAL_EXCLUDED 0x19 +#define CTAP2_ERR_PROCESSING 0x21 +#define CTAP2_ERR_INVALID_CREDENTIAL 0x22 +#define CTAP2_ERR_USER_ACTION_PENDING 0x23 +#define CTAP2_ERR_OPERATION_PENDING 0x24 +#define CTAP2_ERR_NO_OPERATIONS 0x25 +#define CTAP2_ERR_UNSUPPORTED_ALGORITHM 0x26 +#define CTAP2_ERR_OPERATION_DENIED 0x27 +#define CTAP2_ERR_KEY_STORE_FULL 0x28 +#define CTAP2_ERR_UNSUPPORTED_OPTION 0x2B +#define CTAP2_ERR_INVALID_OPTION 0x2C +#define CTAP2_ERR_KEEPALIVE_CANCEL 0x2D +#define CTAP2_ERR_NO_CREDENTIALS 0x2E +#define CTAP2_ERR_USER_ACTION_TIMEOUT 0x2F +#define CTAP2_ERR_NOT_ALLOWED 0x30 +#define CTAP2_ERR_PIN_INVALID 0x31 +#define CTAP2_ERR_PIN_BLOCKED 0x32 +#define CTAP2_ERR_PIN_AUTH_INVALID 0x33 +#define CTAP2_ERR_PIN_AUTH_BLOCKED 0x34 +#define CTAP2_ERR_PIN_NOT_SET 0x35 +#define CTAP2_ERR_PUAT_REQUIRED 0x36 +#define CTAP2_ERR_PIN_POLICY_VIOLATION 0x37 +#define CTAP2_ERR_REQUEST_TOO_LARGE 0x39 +#define CTAP2_ERR_ACTION_TIMEOUT 0x3A +#define CTAP2_ERR_UP_REQUIRED 0x3B +#define CTAP2_ERR_UV_BLOCKED 0x3C +#define CTAP2_ERR_INTEGRITY_FAILURE 0x3D +#define CTAP2_ERR_INVALID_SUBCOMMAND 0x3E +#define CTAP2_ERR_UV_INVALID 0x3F +#define CTAP2_ERR_UNAUTHORIZED_PERMISSION 0x40 +#define CTAP2_ERR_SPEC_LAST 0xDF +#define CTAP2_ERR_EXTENSION_FIRST 0xE0 +#define CTAP2_ERR_EXTENSION_LAST 0xEF +#define CTAP2_ERR_VENDOR_FIRST 0xF0 +#define CTAP2_ERR_VENDOR_LAST 0xFF + #ifdef __cplusplus } #endif diff --git a/src/fido/ctap2_cbor.h b/src/fido/ctap2_cbor.h new file mode 100644 index 0000000..e3c6c62 --- /dev/null +++ b/src/fido/ctap2_cbor.h @@ -0,0 +1,213 @@ +/* + * 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 . + */ + +#ifndef _CTAP2_CBOR_H_ +#define _CTAP2_CBOR_H_ + +#include +#include "pico/stdlib.h" +#include +#include "cbor.h" + +extern uint8_t *driver_prepare_response(); +extern void driver_exec_finished(size_t size_next); +extern int cbor_process(const uint8_t *data, size_t len); +extern int cbor_reset(); +extern int cbor_get_info(); +extern int cbor_make_credential(const uint8_t *data, size_t len); +extern const uint8_t aaguid[16]; + +extern bool *ptrue, *pfalse; + +#define CBOR_CHECK(f) \ + do \ + { \ + error = f; \ + if (error != CborNoError) \ + { \ + printf("Cannot encode CBOR [%d]: %s\n", __LINE__, #f); \ + goto err; \ + } \ + } while (0) + +#define CBOR_FREE(x) \ + do \ + { \ + if (x) \ + { \ + free(x); \ + x = NULL;\ + } \ + } while(0) + +#define CBOR_ERROR(e) \ + do \ + { \ + error = e; \ + printf("Cbor ERROR [%d]: %d\n", __LINE__, e); \ + goto err; \ + } while(0) + +#define CBOR_ASSERT(c) \ + do \ + { \ + if (!c) \ + { \ + error = CborErrorImproperValue; \ + printf("Cbor ASSERT [%d]: %s\n", __LINE__, #c); \ + goto err; \ + } \ + } while(0) + +#define PINUVAUTHTOKEN_MC 0x1 +#define PINUVAUTHTOKEN_GA 0x2 +#define PINUVAUTHTOKEN_CM 0x4 +#define PINUVAUTHTOKEN_BE 0x8 +#define PINUVAUTHTOKEN_LBW 0x10 +#define PINUVAUTHTOKEN_ACFG 0x20 + +typedef struct CborByteString { + uint8_t *data; + size_t len; + bool present; + bool nofree; +} CborByteString; + +typedef struct CborCharString { + char *data; + size_t len; + bool present; + bool nofree; +} CborCharString; + +#define CBOR_FREE_BYTE_STRING(v) \ + do \ + { \ + if ((v).nofree != true) \ + CBOR_FREE((v).data); \ + else \ + (v).data = NULL; \ + (v).len = 0; \ + } while(0) + +#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) \ + CBOR_ASSERT(cbor_value_is_array(&(_p)) == true); \ + CborValue _f##_n; \ + CBOR_CHECK(cbor_value_enter_container(&(_p), &(_f##_n))); \ + while (cbor_value_at_end(&(_f##_n)) == false) + +#define CBOR_FIELD_GET_UINT(v, _n) \ + do { \ + 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) + +#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) + +#define CBOR_FIELD_GET_BYTES(v, _n) \ + do { \ + CBOR_ASSERT(cbor_value_is_byte_string(&(_f##_n)) == true); \ + CBOR_CHECK(cbor_value_dup_byte_string(&(_f##_n), &(v).data, &(v).len, &(_f##_n))); \ + (v).present = true; \ + } while (0) + +#define CBOR_FIELD_GET_TEXT(v, _n) \ + do { \ + CBOR_ASSERT(cbor_value_is_text_string(&(_f##_n)) == true); \ + CBOR_CHECK(cbor_value_dup_text_string(&(_f##_n), &(v).data, &(v).len, &(_f##_n))); \ + (v).present = true; \ + } while (0) + +#define CBOR_FIELD_GET_BOOL(v, _n) \ + do { \ + CBOR_ASSERT(cbor_value_is_boolean(&(_f##_n)) == true); \ + bool val; \ + CBOR_CHECK(cbor_value_get_boolean(&(_f##_n), &val)); \ + v = (val == true ? ptrue : pfalse); \ + CBOR_CHECK(cbor_value_advance_fixed(&(_f##_n))); \ + } while(0) + +#define CBOR_FIELD_GET_KEY_TEXT(_n) \ + CBOR_ASSERT(cbor_value_is_text_string(&(_f##_n)) == true); \ + char _fd##_n[64]; \ + size_t _fdl##_n = sizeof(_fd##_n); \ + CBOR_CHECK(cbor_value_copy_text_string(&(_f##_n), _fd##_n, &_fdl##_n, &(_f##_n))) + +#define CBOR_FIELD_KEY_TEXT_VAL_TEXT(_n, _t, _v) \ + if (strcmp(_fd##_n, _t) == 0) { \ + CBOR_ASSERT(cbor_value_is_text_string(&_f##_n) == true); \ + CBOR_CHECK(cbor_value_dup_text_string(&(_f##_n), &(_v).data, &(_v).len, &(_f##_n))); \ + (_v).present = true; \ + } + +#define CBOR_FIELD_KEY_TEXT_VAL_BYTES(_n, _t, _v) \ + if (strcmp(_fd##_n, _t) == 0) { \ + CBOR_ASSERT(cbor_value_is_byte_string(&_f##_n) == true); \ + CBOR_CHECK(cbor_value_dup_byte_string(&(_f##_n), &(_v).data, &(_v).len, &(_f##_n))); \ + (_v).present = true; \ + } + +#define CBOR_FIELD_KEY_TEXT_VAL_INT(_n, _t, _v) \ + if (strcmp(_fd##_n, _t) == 0) { \ + CBOR_FIELD_GET_INT(_v, _n);\ + } + +#define CBOR_FIELD_KEY_TEXT_VAL_BOOL(_n, _t, _v) \ + if (strcmp(_fd##_n, _t) == 0) { \ + CBOR_FIELD_GET_BOOL(_v, _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_ADVANCE(_n) CBOR_CHECK(cbor_value_advance(&_f##_n)); + +#define CBOR_APPEND_KEY_UINT_VAL_TEXT(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) + +#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) + +#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) + + +#endif //_CTAP2_CBOR_H_ diff --git a/src/fido/fido.c b/src/fido/fido.c index eb4ab4a..11a835f 100644 --- a/src/fido/fido.c +++ b/src/fido/fido.c @@ -193,6 +193,7 @@ int scan_files() { else { printf("FATAL ERROR: Global counter not found in memory!\r\n"); } + ef_pin = search_by_fid(EF_PIN, NULL, SPECIFY_EF); low_flash_available(); return CCID_OK; } @@ -211,7 +212,7 @@ bool wait_button_pressed() { do { queue_remove_blocking(&usb_to_card_q, &val); } while (val != EV_BUTTON_PRESSED && val != EV_BUTTON_TIMEOUT); - return val == EV_BUTTON_TIMEOUT; + return (val == EV_BUTTON_TIMEOUT); } typedef struct cmd diff --git a/src/fido/fido.h b/src/fido/fido.h index ee2408f..f0f95bc 100644 --- a/src/fido/fido.h +++ b/src/fido/fido.h @@ -22,6 +22,7 @@ #include "pico/stdlib.h" #include "common.h" #include "mbedtls/ecdsa.h" +#include "ctap_hid.h" #define CTAP_PUBKEY_LEN (65) #define KEY_PATH_LEN (32) @@ -32,5 +33,12 @@ extern int scan_files(); extern int derive_key(const uint8_t *app_id, bool new_key, uint8_t *key_handle, mbedtls_ecdsa_context *key); extern bool wait_button_pressed(); +extern CTAPHID_FRAME *ctap_req, *ctap_resp; + +#define FIDO2_ALG_ES256 -7 //ECDSA-SHA256 P256 +#define FIDO2_ALG_EDDSA -8 //EdDSA +#define FIDO2_ALG_ES384 -35 //ECDSA-SHA384 P384 +#define FIDO2_ALG_ES512 -36 //ECDSA-SHA512 P521 + #endif //_FIDO_H diff --git a/src/fido/files.c b/src/fido/files.c index 4c031c8..df9ded8 100644 --- a/src/fido/files.c +++ b/src/fido/files.c @@ -23,6 +23,7 @@ file_t file_entries[] = { {.fid = EF_KEY_DEV, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH | FILE_PERSISTENT, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // Device Key {.fid = EF_EE_DEV, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH | FILE_PERSISTENT, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // End Entity Certificate Device {.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}}, // Global counter { .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_UNKNOWN, .data = NULL, .ef_structure = 0, .acl = {0} } //end }; @@ -31,3 +32,4 @@ 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; +file_t *ef_pin = NULL; diff --git a/src/fido/files.h b/src/fido/files.h index 950cec7..3d7e69e 100644 --- a/src/fido/files.h +++ b/src/fido/files.h @@ -23,9 +23,11 @@ #define EF_KEY_DEV 0xCC00 #define EF_EE_DEV 0xCE00 #define EF_COUNTER 0xC000 +#define EF_PIN 0x1080 extern file_t *ef_keydev; extern file_t *ef_certdev; extern file_t *ef_counter; +extern file_t *ef_pin; #endif //_FILES_H_