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_