Compare commits
80 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8338762bcd | ||
|
|
11a0b2cb43 | ||
|
|
957bcae183 | ||
|
|
109cd4e4ea | ||
|
|
8c61cf180d | ||
|
|
4fd9e80e92 | ||
|
|
01a3c0c60e | ||
|
|
7a3996da02 | ||
|
|
48f358cb19 | ||
|
|
074dd80afe | ||
|
|
9cbb53716b | ||
|
|
7a6b8a6af4 | ||
|
|
eb318bc381 | ||
|
|
0e4532a22c | ||
|
|
bfc82d5de4 | ||
|
|
d558941311 | ||
|
|
9fa2c5d39c | ||
|
|
cd66e65b9c | ||
|
|
a165d286af | ||
|
|
9bf40e69af | ||
|
|
71564e0c79 | ||
|
|
be68d5516f | ||
|
|
2c4c618e3b | ||
|
|
dd4b52faf3 | ||
|
|
e94f6843e5 | ||
|
|
cbfe66e89b | ||
|
|
883c5fef35 | ||
|
|
40110ad602 | ||
|
|
61b10b7971 | ||
|
|
2d496fd8fc | ||
|
|
cc373e3e7e | ||
|
|
4360ab0375 | ||
|
|
73c846e985 | ||
|
|
d95bc1aba6 | ||
|
|
2d5fffedb9 | ||
|
|
f045ec8d03 | ||
|
|
e0d8ce7637 | ||
|
|
86e3c960a4 | ||
|
|
864965c1fe | ||
|
|
3b25eb295c | ||
|
|
226fcc5405 | ||
|
|
5625e0dacd | ||
|
|
cf206bf158 | ||
|
|
a44227db52 | ||
|
|
4ab898378a | ||
|
|
99fc76a385 | ||
|
|
f71624f489 | ||
|
|
08c3c3344c | ||
|
|
804970e77a | ||
|
|
c938d47bf7 | ||
|
|
22a2ea109e | ||
|
|
3a3ec97c90 | ||
|
|
8a379d9702 | ||
|
|
72ebb2b596 | ||
|
|
3dc7af05c1 | ||
|
|
a3c60f762d | ||
|
|
8feac76a73 | ||
|
|
f439b85de7 | ||
|
|
ee8f3a0965 | ||
|
|
199091e2b9 | ||
|
|
24f48e33bb | ||
|
|
479aae2ef9 | ||
|
|
fd7da11931 | ||
|
|
a80247ffa2 | ||
|
|
c9c10eca36 | ||
|
|
11642fe0a3 | ||
|
|
a9cb5ee87c | ||
|
|
2c6b14822e | ||
|
|
1b70c21588 | ||
|
|
aa15ad471b | ||
|
|
20038b1586 | ||
|
|
5da2af2c34 | ||
|
|
9b49d39ccc | ||
|
|
84a91fcbda | ||
|
|
3873303309 | ||
|
|
82b5b1cb96 | ||
|
|
1fc8b599ec | ||
|
|
4c8242f4c8 | ||
|
|
e9ab270dc3 | ||
|
|
73f88b6882 |
72
.github/workflows/codeql.yml
vendored
Normal file
72
.github/workflows/codeql.yml
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
# 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: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ "master" ]
|
||||
schedule:
|
||||
- cron: '23 5 * * 4'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'cpp' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# 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
|
||||
# uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ 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.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
|
||||
- run: |
|
||||
echo "Run, Build Application using script"
|
||||
./workflows/autobuild.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
@@ -28,12 +28,38 @@ pico_sdk_init()
|
||||
|
||||
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")
|
||||
else()
|
||||
add_definitions(-DENABLE_UP_BUTTON=0)
|
||||
message("Disabling user presence with button")
|
||||
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")
|
||||
else()
|
||||
add_definitions(-DENABLE_POWER_ON_RESET=0)
|
||||
message("Disabling power cycle on reset")
|
||||
endif(ENABLE_POWER_ON_RESET)
|
||||
|
||||
target_sources(pico_fido PUBLIC
|
||||
${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
|
||||
${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
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/known_apps.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_client_pin.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/credential.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_get_assertion.c
|
||||
)
|
||||
set(HSM_DRIVER "hid")
|
||||
include(pico-hsm-sdk/pico_hsm_sdk_import.cmake)
|
||||
@@ -45,7 +71,13 @@ target_include_directories(pico_fido PUBLIC
|
||||
target_compile_options(pico_fido PUBLIC
|
||||
-Wall
|
||||
-Werror
|
||||
)
|
||||
)
|
||||
string(FIND ${CMAKE_C_COMPILER} ":" COMPILER_COLON)
|
||||
if (${COMPILER_COLON} GREATER_EQUAL 0)
|
||||
target_compile_options(pico_fido PUBLIC
|
||||
-Wno-error=use-after-free
|
||||
)
|
||||
endif()
|
||||
|
||||
pico_add_extra_outputs(pico_fido)
|
||||
|
||||
|
||||
16
README.md
16
README.md
@@ -4,14 +4,21 @@ This project aims at transforming your Raspberry Pico into a FIDO key integrated
|
||||
## Features
|
||||
Pico FIDO has implemented the following features:
|
||||
|
||||
- ECDSA authentication.
|
||||
- App registration and login.
|
||||
- User presence enforcement through physical button.
|
||||
- CTAP 2.1 / CTAP 1
|
||||
- WebAuthn
|
||||
- U2F
|
||||
- HMAC-Secret extension
|
||||
- CredProtect extension
|
||||
- User presence enforcement through physical button
|
||||
- User Verification with PIN
|
||||
- Discoverable credentials
|
||||
- ECDSA authentication
|
||||
- App registration and login
|
||||
|
||||
All these features are compliant with the specification. Therefore, if you detect some behaviour that is not expected or it does not follow the rules of specs, please open an issue.
|
||||
|
||||
## Security considerations
|
||||
Pico FIDO is an open platform so be careful. The contents in the flash memory may be easily dumpled and obtain the private/master keys. There is no way to ensure the master key is stored securely, as the specifications do not support external passphrases or PIN numbers. Therefore, it is not possible to encrypt the content. At least, one key (the master, the supreme key) must be stored in clear text.
|
||||
Pico FIDO is an open platform so be careful. The contents in the flash memory may be easily dumpled and obtain the private/master keys. Therefore, it is not possible to encrypt the content. At least, one key (the master, the supreme key) must be stored in clear text.
|
||||
|
||||
If the Pico is stolen the contents of private and secret keys can be read.
|
||||
|
||||
@@ -66,3 +73,4 @@ Pico FIDO uses the `HID` driver, present in all OS. It should be detected by all
|
||||
Pico FIDO uses the following libraries or portion of code:
|
||||
- MbedTLS for cryptographic operations.
|
||||
- TinyUSB for low level USB procedures.
|
||||
- TinyCBOR for CBOR parsing and formatting.
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#
|
||||
|
||||
VERSION_MAJOR="3" #Version of Pico CCID Core
|
||||
VERSION_MINOR="2"
|
||||
VERSION_MINOR="4"
|
||||
|
||||
echo "----------------------------"
|
||||
echo "VID/PID patcher for Pico FIDO"
|
||||
|
||||
Submodule pico-hsm-sdk updated: 7491021102...657913d29a
80
src/fido/cbor.c
Normal file
80
src/fido/cbor.c
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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 <stdlib.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "ctap2_cbor.h"
|
||||
#include "ctap.h"
|
||||
#include "ctap_hid.h"
|
||||
#include "fido.h"
|
||||
#include "hsm.h"
|
||||
#include "usb.h"
|
||||
#include "apdu.h"
|
||||
|
||||
const bool _btrue = true, _bfalse = false;
|
||||
|
||||
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 *cbor_data = NULL;
|
||||
size_t cbor_len = 0;
|
||||
|
||||
int cbor_parse(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();
|
||||
else if (data[0] == CTAP_CLIENT_PIN)
|
||||
return cbor_client_pin(data + 1, len - 1);
|
||||
else if (data[0] == CTAP_GET_ASSERTION)
|
||||
return cbor_get_assertion(data + 1, len - 1, false);
|
||||
else if (data[0] == CTAP_GET_NEXT_ASSERTION)
|
||||
return cbor_get_next_assertion(data + 1, len - 1);
|
||||
return CTAP2_ERR_INVALID_CBOR;
|
||||
}
|
||||
|
||||
void cbor_thread() {
|
||||
|
||||
card_init_core1();
|
||||
while (1) {
|
||||
uint32_t m;
|
||||
queue_remove_blocking(&usb_to_card_q, &m);
|
||||
|
||||
if (m == EV_EXIT) {
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
apdu.sw = cbor_parse(cbor_data, cbor_len);
|
||||
|
||||
finished_data_size = res_APDU_size+1;
|
||||
uint32_t flag = EV_EXEC_FINISHED;
|
||||
queue_add_blocking(&card_to_usb_q, &flag);
|
||||
}
|
||||
}
|
||||
|
||||
int cbor_process(const uint8_t *data, size_t len) {
|
||||
cbor_data = data;
|
||||
cbor_len = len;
|
||||
res_APDU = ctap_resp->init.data + 1;
|
||||
res_APDU_size = 0;
|
||||
return 1;
|
||||
}
|
||||
533
src/fido/cbor_client_pin.c
Normal file
533
src/fido/cbor_client_pin.c
Normal file
@@ -0,0 +1,533 @@
|
||||
/*
|
||||
* 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 "common.h"
|
||||
#include "mbedtls/ecp.h"
|
||||
#include "mbedtls/ecdh.h"
|
||||
#include "mbedtls/sha256.h"
|
||||
#include "mbedtls/hkdf.h"
|
||||
#include "cbor.h"
|
||||
#include "ctap.h"
|
||||
#include "ctap2_cbor.h"
|
||||
#include "bsp/board.h"
|
||||
#include "fido.h"
|
||||
#include "files.h"
|
||||
#include "random.h"
|
||||
#include "crypto_utils.h"
|
||||
#include "hsm.h"
|
||||
#include "apdu.h"
|
||||
|
||||
uint8_t permissions_rp_id = 0, permission_set = 0;
|
||||
uint32_t usage_timer = 0, initial_usage_time_limit = 0;
|
||||
uint32_t max_usage_time_period = 600*1000;
|
||||
bool needs_power_cycle = false;
|
||||
mbedtls_ecdh_context hkey;
|
||||
bool hkey_init = false;
|
||||
|
||||
int beginUsingPinUvAuthToken(bool userIsPresent) {
|
||||
paut.user_present = userIsPresent;
|
||||
paut.user_verified = true;
|
||||
initial_usage_time_limit = board_millis();
|
||||
usage_timer = board_millis();
|
||||
paut.in_use = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void clearUserPresentFlag() {
|
||||
if (paut.in_use == true)
|
||||
paut.user_present = false;
|
||||
}
|
||||
|
||||
void clearUserVerifiedFlag() {
|
||||
if (paut.in_use == true)
|
||||
paut.user_verified = false;
|
||||
}
|
||||
|
||||
void clearPinUvAuthTokenPermissionsExceptLbw() {
|
||||
if (paut.in_use == true)
|
||||
paut.permissions = FIDO2_PERMISSION_LBW;
|
||||
}
|
||||
|
||||
void stopUsingPinUvAuthToken() {
|
||||
permissions_rp_id = 0;
|
||||
paut.permissions = 0;
|
||||
usage_timer = 0;
|
||||
paut.in_use = false;
|
||||
memset(paut.rp_id_hash, 0, sizeof(paut.rp_id_hash));
|
||||
initial_usage_time_limit = 0;
|
||||
paut.user_present = paut.user_verified = false;
|
||||
user_present_time_limit = 0;
|
||||
}
|
||||
|
||||
bool getUserPresentFlagValue() {
|
||||
if (paut.in_use != true)
|
||||
paut.user_present = false;
|
||||
return paut.user_present;
|
||||
}
|
||||
|
||||
bool getUserVerifiedFlagValue() {
|
||||
if (paut.in_use != true)
|
||||
paut.user_verified = false;
|
||||
return paut.user_verified;
|
||||
}
|
||||
|
||||
int regenerate() {
|
||||
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);
|
||||
mbedtls_mpi_lset(&hkey.ctx.mbed_ecdh.Qp.Z, 1);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
return ret;
|
||||
if (protocol == 1) {
|
||||
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)
|
||||
return ret;
|
||||
return mbedtls_hkdf(md_info, NULL, 0, buf, sizeof(buf), (uint8_t *)"CTAP2 AES key", 13, sharedSecret+32, 32);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
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);
|
||||
ret = kdf(protocol, &z, sharedSecret);
|
||||
mbedtls_mpi_free(&z);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int resetPinUvAuthToken() {
|
||||
uint8_t t[32];
|
||||
random_gen(NULL, t, sizeof(t));
|
||||
flash_write_data_to_file(ef_authtoken, t, sizeof(t));
|
||||
paut.permissions = 0;
|
||||
paut.data = file_get_data(ef_authtoken);
|
||||
paut.len = file_get_size(ef_authtoken);
|
||||
|
||||
low_flash_available();
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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 -1;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
else if (protocol == 2) {
|
||||
memcpy(out, in, in_len);
|
||||
return aes_encrypt(key+32, out, 32*8, HSM_AES_MODE_CBC, out+IV_SIZE, 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) {
|
||||
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)
|
||||
return ret;
|
||||
if (protocol == 1) {
|
||||
memcpy(sign, hmac, 16);
|
||||
}
|
||||
else if (protocol == 2) {
|
||||
memcpy(sign, hmac, 32);
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int verify(uint8_t protocol, const uint8_t *key, const uint8_t *data, size_t len, uint8_t *sign) {
|
||||
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)
|
||||
return ret;
|
||||
if (protocol == 1)
|
||||
return memcmp(sign, hmac, 16);
|
||||
else if (protocol == 2)
|
||||
return memcmp(sign, hmac, 32);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int initialize() {
|
||||
regenerate();
|
||||
return resetPinUvAuthToken();
|
||||
}
|
||||
|
||||
int getPublicKey() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pinUvAuthTokenUsageTimerObserver() {
|
||||
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())
|
||||
clearUserPresentFlag();
|
||||
if (paut.in_use == true) {
|
||||
if (initial_usage_time_limit == 0 || initial_usage_time_limit+TRANSPORT_TIME_LIMIT < board_millis()) {
|
||||
stopUsingPinUvAuthToken();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
// TO DO: implement a rolling timer
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t new_pin_mismatches = 0;
|
||||
|
||||
int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
size_t resp_size = 0;
|
||||
uint64_t subcommand = 0x0, pinUvAuthProtocol = 0, permissions = 0;
|
||||
int64_t kty = 0, alg = 0, crv = 0;
|
||||
CborParser parser;
|
||||
CborEncoder encoder, mapEncoder;
|
||||
CborValue map;
|
||||
CborError error = CborNoError;
|
||||
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)
|
||||
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)
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
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);
|
||||
}
|
||||
else if (val_u == 0x02) {
|
||||
CBOR_FIELD_GET_UINT(subcommand, 1);
|
||||
}
|
||||
else if (val_u == 0x03) {
|
||||
int64_t key = 0;
|
||||
CBOR_PARSE_MAP_START(_f1, 2) {
|
||||
CBOR_FIELD_GET_INT(key, 2);
|
||||
if (key == 1) {
|
||||
CBOR_FIELD_GET_INT(kty, 2);
|
||||
}
|
||||
else if (key == 3) {
|
||||
CBOR_FIELD_GET_INT(alg, 2);
|
||||
}
|
||||
else if (key == -1) {
|
||||
CBOR_FIELD_GET_INT(crv, 2);
|
||||
}
|
||||
else if (key == -2) {
|
||||
CBOR_FIELD_GET_BYTES(kax, 2);
|
||||
}
|
||||
else if (key == -3) {
|
||||
CBOR_FIELD_GET_BYTES(kay, 2);
|
||||
}
|
||||
else
|
||||
CBOR_ADVANCE(2);
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f1, 2);
|
||||
}
|
||||
else if (val_u == 0x04) {
|
||||
CBOR_FIELD_GET_BYTES(pinUvAuthParam, 1);
|
||||
}
|
||||
else if (val_u == 0x05) {
|
||||
CBOR_FIELD_GET_BYTES(newPinEnc, 1);
|
||||
}
|
||||
else if (val_u == 0x06) {
|
||||
CBOR_FIELD_GET_BYTES(pinHashEnc, 1);
|
||||
}
|
||||
else if (val_u == 0x09) {
|
||||
CBOR_FIELD_GET_UINT(permissions, 1);
|
||||
}
|
||||
else if (val_u == 0x0A) {
|
||||
CBOR_FIELD_GET_TEXT(rpId, 1);
|
||||
}
|
||||
}
|
||||
CBOR_PARSE_MAP_END(map, 1);
|
||||
|
||||
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
|
||||
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)));
|
||||
if (needs_power_cycle) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04));
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, true));
|
||||
}
|
||||
}
|
||||
else if (subcommand == 0x2) { //getKeyAgreement
|
||||
if (pinUvAuthProtocol == 1 || pinUvAuthProtocol == 2) {
|
||||
CborEncoder mapEncoder2;
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
|
||||
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, 5));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 2));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 3));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, -FIDO2_ALG_ECDH_ES_HKDF_256));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, FIDO2_CURVE_P256));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 2));
|
||||
uint8_t pkey[32];
|
||||
mbedtls_mpi_write_binary(&hkey.ctx.mbed_ecdh.Q.X, pkey, 32);
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, pkey, 32));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 3));
|
||||
mbedtls_mpi_write_binary(&hkey.ctx.mbed_ecdh.Q.Y, pkey, 32);
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, pkey, 32));
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
|
||||
}
|
||||
else if (pinUvAuthProtocol == 0)
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
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)
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2)
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
if (file_has_data(ef_pin))
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
if (newPinEnc.len != 64)
|
||||
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);
|
||||
}
|
||||
if (mbedtls_mpi_read_binary(&hkey.ctx.mbed_ecdh.Qp.Y, kay.data, kay.len) != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
uint8_t sharedSecret[64];
|
||||
int ret = ecdh(pinUvAuthProtocol, &hkey.ctx.mbed_ecdh.Qp, sharedSecret);
|
||||
if (ret != 0) {
|
||||
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);
|
||||
}
|
||||
uint8_t paddedNewPin[64];
|
||||
ret = decrypt(pinUvAuthProtocol, sharedSecret, newPinEnc.data, newPinEnc.len, paddedNewPin);
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
if (ret != 0)
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
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))
|
||||
pin_len++;
|
||||
if (pin_len < 4)
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION);
|
||||
uint8_t hsh[33];
|
||||
hsh[0] = MAX_PIN_RETRIES;
|
||||
mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, hsh + 1);
|
||||
flash_write_data_to_file(ef_pin, hsh, 1+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)
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2)
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
if (!file_has_data(ef_pin))
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_NOT_SET);
|
||||
if (*file_get_data(ef_pin) == 0)
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_BLOCKED);
|
||||
if (newPinEnc.len != 64 || pinHashEnc.len != 16)
|
||||
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);
|
||||
}
|
||||
if (mbedtls_mpi_read_binary(&hkey.ctx.mbed_ecdh.Qp.Y, kay.data, kay.len) != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
uint8_t sharedSecret[64];
|
||||
int ret = ecdh(pinUvAuthProtocol, &hkey.ctx.mbed_ecdh.Qp, sharedSecret);
|
||||
if (ret != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
uint8_t tmp[64 + 16];
|
||||
memcpy(tmp, newPinEnc.data, 64);
|
||||
memcpy(tmp + 64, pinHashEnc.data, 16);
|
||||
if (verify(pinUvAuthProtocol, sharedSecret, tmp, sizeof(tmp), pinUvAuthParam.data) != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
uint8_t retries = *file_get_data(ef_pin) - 1;
|
||||
flash_write_data_to_file(ef_pin, &retries, 1);
|
||||
uint8_t paddedNewPin[64];
|
||||
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);
|
||||
}
|
||||
low_flash_available();
|
||||
if (memcmp(paddedNewPin, file_get_data(ef_pin)+1, 16) != 0) {
|
||||
regenerate();
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
if (retries == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_BLOCKED);
|
||||
}
|
||||
if (++new_pin_mismatches == 3) {
|
||||
needs_power_cycle = true;
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_BLOCKED);
|
||||
}
|
||||
else
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_INVALID);
|
||||
}
|
||||
retries = MAX_PIN_RETRIES;
|
||||
new_pin_mismatches = 0;
|
||||
flash_write_data_to_file(ef_pin, &retries, 1);
|
||||
ret = decrypt(pinUvAuthProtocol, sharedSecret, newPinEnc.data, newPinEnc.len, paddedNewPin);
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
if (paddedNewPin[63] != 0)
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
uint8_t pin_len = 0;
|
||||
while (paddedNewPin[pin_len] != 0 && pin_len < sizeof(paddedNewPin))
|
||||
pin_len++;
|
||||
if (pin_len < 4)
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION);
|
||||
uint8_t hsh[33];
|
||||
hsh[0] = MAX_PIN_RETRIES;
|
||||
mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, hsh + 1);
|
||||
flash_write_data_to_file(ef_pin, hsh, 1+16);
|
||||
low_flash_available();
|
||||
resetPinUvAuthToken();
|
||||
goto err; // No return
|
||||
}
|
||||
else if (subcommand == 0x9 || subcommand == 0x5) { //getUVRgetPinUvAuthTokenUsingPinWithPermissionsetries
|
||||
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)
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
if ((subcommand == 0x9 && permissions == 0) || (subcommand == 0x5 && (permissions != 0 || rpId.present == true)))
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
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);
|
||||
}
|
||||
if (mbedtls_mpi_read_binary(&hkey.ctx.mbed_ecdh.Qp.Y, kay.data, kay.len) != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
uint8_t sharedSecret[64];
|
||||
int ret = ecdh(pinUvAuthProtocol, &hkey.ctx.mbed_ecdh.Qp, sharedSecret);
|
||||
if (ret != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
uint8_t retries = *file_get_data(ef_pin) - 1;
|
||||
flash_write_data_to_file(ef_pin, &retries, 1);
|
||||
uint8_t paddedNewPin[64];
|
||||
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);
|
||||
}
|
||||
low_flash_available();
|
||||
if (memcmp(paddedNewPin, file_get_data(ef_pin)+1, 16) != 0) {
|
||||
regenerate();
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
if (retries == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_BLOCKED);
|
||||
}
|
||||
if (++new_pin_mismatches >= 3) {
|
||||
needs_power_cycle = true;
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_BLOCKED);
|
||||
}
|
||||
else
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_INVALID);
|
||||
}
|
||||
retries = MAX_PIN_RETRIES;
|
||||
new_pin_mismatches = 0;
|
||||
flash_write_data_to_file(ef_pin, &retries, 1);
|
||||
low_flash_available();
|
||||
beginUsingPinUvAuthToken(false);
|
||||
paut.permissions = permissions;
|
||||
if (rpId.present == true)
|
||||
memcpy(paut.rp_id_hash, rpId.data, 32);
|
||||
else
|
||||
memset(paut.rp_id_hash, 0, sizeof(paut.rp_id_hash));
|
||||
uint8_t pinUvAuthToken_enc[32];
|
||||
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));
|
||||
}
|
||||
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:
|
||||
CBOR_FREE_BYTE_STRING(pinUvAuthParam);
|
||||
CBOR_FREE_BYTE_STRING(newPinEnc);
|
||||
CBOR_FREE_BYTE_STRING(pinHashEnc);
|
||||
CBOR_FREE_BYTE_STRING(kax);
|
||||
CBOR_FREE_BYTE_STRING(kay);
|
||||
CBOR_FREE_BYTE_STRING(rpId);
|
||||
if (error != CborNoError) {
|
||||
if (error == CborErrorImproperValue)
|
||||
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
||||
return error;
|
||||
}
|
||||
res_APDU_size = resp_size;
|
||||
return 0;
|
||||
}
|
||||
514
src/fido/cbor_get_assertion.c
Normal file
514
src/fido/cbor_get_assertion.c
Normal file
@@ -0,0 +1,514 @@
|
||||
/*
|
||||
* 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 "cbor.h"
|
||||
#include "ctap.h"
|
||||
#include "ctap2_cbor.h"
|
||||
#include "bsp/board.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>
|
||||
|
||||
bool residentx = false;
|
||||
Credential credsx[MAX_CREDENTIAL_COUNT_IN_LIST] = {0};
|
||||
uint8_t credentialCounter = 1;
|
||||
uint8_t numberOfCredentialsx = 0;
|
||||
uint8_t flagsx = 0;
|
||||
uint32_t timerx = 0;
|
||||
uint8_t *datax = NULL;
|
||||
size_t lenx = 0;
|
||||
|
||||
int cbor_get_next_assertion(const uint8_t *data, size_t len) {
|
||||
CborError error = CborNoError;
|
||||
if (credentialCounter >= numberOfCredentialsx)
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
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) {
|
||||
for (int i = 0; i < MAX_CREDENTIAL_COUNT_IN_LIST; i++)
|
||||
credential_free(&credsx[i]);
|
||||
if (datax) {
|
||||
free(datax);
|
||||
datax = NULL;
|
||||
}
|
||||
lenx = 0;
|
||||
residentx = false;
|
||||
timerx = 0;
|
||||
flagsx = 0;
|
||||
credentialCounter = 0;
|
||||
numberOfCredentialsx = 0;
|
||||
if (error == CborErrorImproperValue)
|
||||
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
||||
return error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
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};
|
||||
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};
|
||||
size_t allowList_len = 0, creds_len = 0;
|
||||
uint8_t *aut_data = NULL;
|
||||
bool asserted = false;
|
||||
int64_t kty = 2, alg = 0, crv = 0;
|
||||
CborByteString kax = {0}, kay = {0}, salt_enc = {0}, salt_auth = {0};
|
||||
|
||||
CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map));
|
||||
uint64_t val_c = 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)
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
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);
|
||||
}
|
||||
else if (val_u == 0x02) {
|
||||
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_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_FIELD_GET_TEXT(pc->transports[pc->transports_len], 4);
|
||||
pc->transports_len++;
|
||||
}
|
||||
CBOR_PARSE_ARRAY_END(_f3, 4);
|
||||
}
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f2, 3);
|
||||
allowList_len++;
|
||||
}
|
||||
CBOR_PARSE_ARRAY_END(_f1, 2);
|
||||
}
|
||||
else if (val_u == 0x04) { // extensions
|
||||
extensions.present = true;
|
||||
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_FIELD_GET_UINT(ukey, 3);
|
||||
if (ukey == 0x01) {
|
||||
int64_t kkey = 0;
|
||||
CBOR_PARSE_MAP_START(_f3, 4) {
|
||||
CBOR_FIELD_GET_INT(kkey, 4);
|
||||
if (kkey == 1) {
|
||||
CBOR_FIELD_GET_INT(kty, 4);
|
||||
}
|
||||
else if (kkey == 3) {
|
||||
CBOR_FIELD_GET_INT(alg, 4);
|
||||
}
|
||||
else if (kkey == -1) {
|
||||
CBOR_FIELD_GET_INT(crv, 4);
|
||||
}
|
||||
else if (kkey == -2) {
|
||||
CBOR_FIELD_GET_BYTES(kax, 4);
|
||||
}
|
||||
else if (kkey == -3) {
|
||||
CBOR_FIELD_GET_BYTES(kay, 4);
|
||||
}
|
||||
else
|
||||
CBOR_ADVANCE(4);
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f3, 4);
|
||||
}
|
||||
else if (ukey == 0x02) {
|
||||
CBOR_FIELD_GET_BYTES(salt_enc, 3);
|
||||
}
|
||||
else if (ukey == 0x03) {
|
||||
CBOR_FIELD_GET_BYTES(salt_auth, 3);
|
||||
}
|
||||
else if (ukey == 0x04) {
|
||||
CBOR_FIELD_GET_UINT(hmacSecretPinUvAuthProtocol, 3);
|
||||
}
|
||||
else
|
||||
CBOR_ADVANCE(3);
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f2, 3);
|
||||
continue;
|
||||
}
|
||||
CBOR_FIELD_KEY_TEXT_VAL_UINT(2, "credProtect", extensions.credProtect);
|
||||
CBOR_ADVANCE(2);
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f1, 2);
|
||||
}
|
||||
else if (val_u == 0x05) { // 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_ADVANCE(2);
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f1, 2);
|
||||
}
|
||||
else if (val_u == 0x06) { // pinUvAuthParam
|
||||
CBOR_FIELD_GET_BYTES(pinUvAuthParam, 1);
|
||||
}
|
||||
else if (val_u == 0x07) { // pinUvAuthProtocol
|
||||
CBOR_FIELD_GET_UINT(pinUvAuthProtocol, 1);
|
||||
}
|
||||
}
|
||||
CBOR_PARSE_MAP_END(map, 1);
|
||||
|
||||
uint8_t flags = 0;
|
||||
uint8_t rp_id_hash[32];
|
||||
mbedtls_sha256((uint8_t *)rpId.data, rpId.len, rp_id_hash, 0);
|
||||
|
||||
bool resident = false;
|
||||
uint8_t numberOfCredentials = 0;
|
||||
Credential *selcred = NULL;
|
||||
if (next == false) {
|
||||
if (pinUvAuthParam.present == true) {
|
||||
if (pinUvAuthParam.len == 0 || pinUvAuthParam.data == NULL) {
|
||||
if (check_user_presence() == false)
|
||||
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_AUTH_INVALID);
|
||||
}
|
||||
else {
|
||||
if (pinUvAuthProtocol == 0)
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2)
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
}
|
||||
if (options.present) {
|
||||
if (options.uv == ptrue) { //4.3
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_OPTION);
|
||||
}
|
||||
//if (options.up != NULL) { //4.5
|
||||
// CBOR_ERROR(CTAP2_ERR_INVALID_OPTION);
|
||||
//}
|
||||
if (options.rk != NULL) {
|
||||
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION);
|
||||
}
|
||||
//else if (options.up == NULL) //5.7
|
||||
//rup = ptrue;
|
||||
}
|
||||
|
||||
if (pinUvAuthParam.present == true) { //6.1
|
||||
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)
|
||||
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)
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (salt_enc.len != 32 && salt_enc.len != 64)
|
||||
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)
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
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) {
|
||||
CBOR_FREE_BYTE_STRING(allowList[e].id);
|
||||
credential_free(&creds[creds_len]);
|
||||
}
|
||||
else
|
||||
creds_len++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
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)
|
||||
continue;
|
||||
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
|
||||
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))
|
||||
credential_free(&creds[i]);
|
||||
else if (creds[i].extensions.credProtect == CRED_PROT_UV_OPTIONAL_WITH_LIST && resident == true && !(flags & FIDO2_AUT_FLAG_UV))
|
||||
credential_free(&creds[i]);
|
||||
else
|
||||
creds[numberOfCredentials++] = creds[i];
|
||||
}
|
||||
else
|
||||
creds[numberOfCredentials++] = creds[i];
|
||||
}
|
||||
}
|
||||
if (numberOfCredentials == 0)
|
||||
CBOR_ERROR(CTAP2_ERR_NO_CREDENTIALS);
|
||||
|
||||
for (int i = 0; i < numberOfCredentials; i++) {
|
||||
for (int j = i + 1; j < numberOfCredentials; j++) {
|
||||
if (creds[j].creation > creds[i].creation) {
|
||||
Credential tmp = creds[j];
|
||||
creds[j] = creds[i];
|
||||
creds[i] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (options.up == ptrue || options.present == false || options.up == NULL) { //9.1
|
||||
if (pinUvAuthParam.present == true) {
|
||||
if (getUserPresentFlagValue() == false) {
|
||||
if (check_user_presence() == false)
|
||||
CBOR_ERROR(CTAP2_ERR_OPERATION_DENIED);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!(flags & FIDO2_AUT_FLAG_UP)) {
|
||||
if (check_user_presence() == false)
|
||||
CBOR_ERROR(CTAP2_ERR_OPERATION_DENIED);
|
||||
}
|
||||
}
|
||||
flags |= FIDO2_AUT_FLAG_UP;
|
||||
clearUserPresentFlag();
|
||||
clearUserVerifiedFlag();
|
||||
clearPinUvAuthTokenPermissionsExceptLbw();
|
||||
}
|
||||
|
||||
if (!(flags & FIDO2_AUT_FLAG_UP) && !(flags & FIDO2_AUT_FLAG_UV)) {
|
||||
selcred = &creds[0];
|
||||
}
|
||||
else {
|
||||
selcred = &creds[0];
|
||||
if (numberOfCredentials > 1) {
|
||||
asserted = true;
|
||||
residentx = resident;
|
||||
for (int i = 0; i < MAX_CREDENTIAL_COUNT_IN_LIST; i++)
|
||||
credsx[i] = creds[i];
|
||||
numberOfCredentialsx = numberOfCredentials;
|
||||
datax = (uint8_t *)calloc(1, len);
|
||||
memcpy(datax, data, len);
|
||||
lenx = len;
|
||||
flagsx = flags;
|
||||
timerx = board_millis();
|
||||
credentialCounter = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
resident = residentx;
|
||||
numberOfCredentials = numberOfCredentialsx;
|
||||
flags = flagsx;
|
||||
selcred = &credsx[credentialCounter];
|
||||
}
|
||||
mbedtls_ecdsa_context ekey;
|
||||
mbedtls_ecdsa_init(&ekey);
|
||||
int ret = fido_load_key(selcred->curve, selcred->id.data, &ekey);
|
||||
if (ret != 0) {
|
||||
if (derive_key(rp_id_hash, false, selcred->id.data, MBEDTLS_ECP_DP_SECP256R1, &ekey) != 0) {
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
}
|
||||
}
|
||||
|
||||
size_t ext_len = 0;
|
||||
uint8_t ext [512];
|
||||
if (extensions.present == true) {
|
||||
cbor_encoder_init(&encoder, ext, sizeof(ext), 0);
|
||||
int l = 0;
|
||||
if (options.up == pfalse)
|
||||
extensions.hmac_secret = NULL;
|
||||
if (extensions.hmac_secret != NULL)
|
||||
l++;
|
||||
if (extensions.credProtect != 0)
|
||||
l++;
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, l));
|
||||
if (extensions.credProtect != 0) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "credProtect"));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, extensions.credProtect));
|
||||
}
|
||||
if (extensions.hmac_secret != NULL) {
|
||||
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "hmac-secret"));
|
||||
|
||||
uint8_t sharedSecret[64];
|
||||
mbedtls_ecp_point Qp;
|
||||
mbedtls_ecp_point_init(&Qp);
|
||||
mbedtls_mpi_lset(&Qp.Z, 1);
|
||||
if (mbedtls_mpi_read_binary(&Qp.X, kax.data, kax.len) != 0) {
|
||||
mbedtls_ecp_point_free(&Qp);
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
if (mbedtls_mpi_read_binary(&Qp.Y, kay.data, kay.len) != 0) {
|
||||
mbedtls_ecp_point_free(&Qp);
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
int ret = ecdh(hmacSecretPinUvAuthProtocol, &Qp, sharedSecret);
|
||||
mbedtls_ecp_point_free(&Qp);
|
||||
if (ret != 0) {
|
||||
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) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP2_ERR_EXTENSION_FIRST);
|
||||
}
|
||||
uint8_t salt_dec[64];
|
||||
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);
|
||||
}
|
||||
uint8_t cred_random[64], *crd = NULL;
|
||||
ret = credential_derive_hmac_key(selcred->id.data, selcred->id.len, cred_random);
|
||||
if (ret != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
if (flags & FIDO2_AUT_FLAG_UV)
|
||||
crd = cred_random + 32;
|
||||
else
|
||||
crd = cred_random;
|
||||
uint8_t out1[64], hmac_res[64];
|
||||
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), crd, 32, salt_dec, 32, out1);
|
||||
if (salt_enc.len == 64)
|
||||
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, hmac_res);
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, hmac_res, salt_enc.len));
|
||||
}
|
||||
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
ext_len = cbor_encoder_get_buffer_size(&encoder, ext);
|
||||
flags |= FIDO2_AUT_FLAG_ED;
|
||||
}
|
||||
|
||||
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);
|
||||
uint8_t *pa = aut_data;
|
||||
memcpy(pa, rp_id_hash, 32); pa += 32;
|
||||
*pa++ = flags;
|
||||
*pa++ = ctr >> 24;
|
||||
*pa++ = ctr >> 16;
|
||||
*pa++ = ctr >> 8;
|
||||
*pa++ = ctr & 0xff;
|
||||
memcpy(pa, ext, ext_len); pa += ext_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);
|
||||
size_t olen = 0;
|
||||
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)
|
||||
lfields++;
|
||||
if (numberOfCredentials > 1 && next == false)
|
||||
lfields++;
|
||||
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, lfields));
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, 2));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "id"));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, selcred->id.data, selcred->id.len));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "type"));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "public-key"));
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x02));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, aut_data, aut_data_len));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, sig, olen));
|
||||
|
||||
if (selcred->opts.present == true && selcred->opts.rk == ptrue) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04));
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, 1));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "id"));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, selcred->userId.data, selcred->userId.len));
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
|
||||
}
|
||||
if (numberOfCredentials > 1 && next == false) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x05));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, numberOfCredentials));
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
resp_size = cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1);
|
||||
|
||||
err:
|
||||
if (asserted == false) {
|
||||
CBOR_FREE_BYTE_STRING(clientDataHash);
|
||||
CBOR_FREE_BYTE_STRING(pinUvAuthParam);
|
||||
CBOR_FREE_BYTE_STRING(rpId);
|
||||
}
|
||||
|
||||
for (int m = 0; m < allowList_len; m++) {
|
||||
CBOR_FREE_BYTE_STRING(allowList[m].type);
|
||||
CBOR_FREE_BYTE_STRING(allowList[m].id);
|
||||
for (int n = 0; n < allowList[m].transports_len; n++) {
|
||||
CBOR_FREE_BYTE_STRING(allowList[m].transports[n]);
|
||||
}
|
||||
}
|
||||
if (aut_data)
|
||||
free(aut_data);
|
||||
if (error != CborNoError) {
|
||||
if (error == CborErrorImproperValue)
|
||||
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
||||
return error;
|
||||
}
|
||||
res_APDU_size = resp_size;
|
||||
return 0;
|
||||
}
|
||||
88
src/fido/cbor_get_info.c
Normal file
88
src/fido/cbor_get_info.c
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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 "ctap2_cbor.h"
|
||||
#include "fido.h"
|
||||
#include "ctap.h"
|
||||
#include "files.h"
|
||||
#include "apdu.h"
|
||||
#include "version.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, 9));
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
|
||||
CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 3));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "U2F_V2"));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "FIDO_2_0"));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "FIDO_2_1"));
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &arrayEncoder));
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x02));
|
||||
CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 2));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "credProtect"));
|
||||
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, "credMgmt"));
|
||||
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, "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_encoder_close_container(&mapEncoder, &arrayEncoder));
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x06));
|
||||
CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 2));
|
||||
CBOR_CHECK(cbor_encode_uint(&arrayEncoder, 1)); // PIN protocols
|
||||
CBOR_CHECK(cbor_encode_uint(&arrayEncoder, 2)); // PIN protocols
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &arrayEncoder));
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x07));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, MAX_CREDENTIAL_COUNT_IN_LIST)); // MAX_CRED_COUNT_IN_LIST
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x08));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, MAX_CRED_ID_LENGTH)); // MAX_CRED_ID_MAX_LENGTH
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0D));
|
||||
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
|
||||
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
err:
|
||||
if (error != CborNoError)
|
||||
return -CTAP2_ERR_INVALID_CBOR;
|
||||
res_APDU_size = cbor_encoder_get_buffer_size(&encoder, res_APDU + 1);
|
||||
return 0;
|
||||
}
|
||||
404
src/fido/cbor_make_credential.c
Normal file
404
src/fido/cbor_make_credential.c
Normal file
@@ -0,0 +1,404 @@
|
||||
/*
|
||||
* 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 "common.h"
|
||||
#include "ctap2_cbor.h"
|
||||
#include "cbor_make_credential.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"
|
||||
|
||||
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};
|
||||
size_t pubKeyCredParams_len = 0;
|
||||
PublicKeyCredentialDescriptor excludeList[MAX_CREDENTIAL_COUNT_IN_LIST] = {0};
|
||||
size_t excludeList_len = 0;
|
||||
CredOptions options = {0};
|
||||
uint64_t pinUvAuthProtocol = 0, enterpriseAttestation = 0;
|
||||
uint8_t *aut_data = NULL;
|
||||
size_t resp_size = 0;
|
||||
CredExtensions extensions = {0};
|
||||
//options.present = true;
|
||||
options.up = ptrue;
|
||||
options.uv = pfalse;
|
||||
options.rk = pfalse;
|
||||
|
||||
CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map));
|
||||
uint64_t val_c = 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)
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
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);
|
||||
}
|
||||
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(_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_FIELD_GET_TEXT(pc->transports[pc->transports_len], 4);
|
||||
pc->transports_len++;
|
||||
}
|
||||
CBOR_PARSE_ARRAY_END(_f3, 4);
|
||||
}
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f2, 3);
|
||||
excludeList_len++;
|
||||
}
|
||||
CBOR_PARSE_ARRAY_END(_f1, 2);
|
||||
}
|
||||
else if (val_u == 0x06) { // extensions
|
||||
extensions.present = true;
|
||||
CBOR_PARSE_MAP_START(_f1, 2) {
|
||||
CBOR_FIELD_GET_KEY_TEXT(2);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "hmac-secret", extensions.hmac_secret);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_UINT(2, "credProtect", extensions.credProtect);
|
||||
CBOR_ADVANCE(2);
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f1, 2);
|
||||
}
|
||||
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_ADVANCE(2);
|
||||
}
|
||||
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 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);
|
||||
|
||||
int curve = -1, alg = 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)
|
||||
continue;
|
||||
if (pubKeyCredParams[i].alg == FIDO2_ALG_ES256)
|
||||
curve = FIDO2_CURVE_P256;
|
||||
else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES384)
|
||||
curve = FIDO2_CURVE_P384;
|
||||
else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES512)
|
||||
curve = FIDO2_CURVE_P521;
|
||||
else if (pubKeyCredParams[i].alg == 0) // no present
|
||||
curve = -1;
|
||||
else
|
||||
curve = 0;
|
||||
if (curve > 0) {
|
||||
alg = pubKeyCredParams[i].alg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (curve == 0)
|
||||
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_ALGORITHM);
|
||||
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)
|
||||
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_AUTH_INVALID);
|
||||
}
|
||||
else {
|
||||
if (pinUvAuthProtocol == 0)
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2)
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
}
|
||||
if (options.present) {
|
||||
if (options.uv == ptrue) { //5.3
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_OPTION);
|
||||
}
|
||||
if (options.up != NULL) { //5.6
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_OPTION);
|
||||
}
|
||||
//else if (options.up == NULL) //5.7
|
||||
//rup = ptrue;
|
||||
}
|
||||
if (pinUvAuthParam.present == false && options.uv != ptrue && 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(pinUvAuthProtocol, paut.data, clientDataHash.data, clientDataHash.len, pinUvAuthParam.data);
|
||||
if (ret != CborNoError)
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (getUserVerifiedFlagValue() == false)
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
flags |= FIDO2_AUT_FLAG_UV;
|
||||
// Check pinUvAuthToken permissions. See 6.1.2.11
|
||||
}
|
||||
|
||||
for (int e = 0; e < excludeList_len; e++) { //12.1
|
||||
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)
|
||||
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 (options.up == ptrue) { //14.1
|
||||
if (pinUvAuthParam.present == true) {
|
||||
if (getUserPresentFlagValue() == false) {
|
||||
if (check_user_presence() == false)
|
||||
CBOR_ERROR(CTAP2_ERR_OPERATION_DENIED);
|
||||
}
|
||||
}
|
||||
flags |= FIDO2_AUT_FLAG_UP;
|
||||
clearUserPresentFlag();
|
||||
clearUserVerifiedFlag();
|
||||
clearPinUvAuthTokenPermissionsExceptLbw();
|
||||
}
|
||||
|
||||
const known_app_t *ka = find_app_by_rp_id_hash(rp_id_hash);
|
||||
|
||||
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));
|
||||
|
||||
mbedtls_ecdsa_context ekey;
|
||||
mbedtls_ecdsa_init(&ekey);
|
||||
int ret = fido_load_key(curve, cred_id, &ekey);
|
||||
if (ret != 0) {
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
}
|
||||
|
||||
if (getUserVerifiedFlagValue())
|
||||
flags |= FIDO2_AUT_FLAG_UV;
|
||||
size_t ext_len = 0;
|
||||
uint8_t ext [512];
|
||||
CborEncoder encoder, mapEncoder, mapEncoder2;
|
||||
if (extensions.present == true) {
|
||||
cbor_encoder_init(&encoder, ext, sizeof(ext), 0);
|
||||
int l = 0;
|
||||
if (extensions.hmac_secret != NULL)
|
||||
l++;
|
||||
if (extensions.credProtect != 0)
|
||||
l++;
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, l));
|
||||
if (extensions.credProtect != 0) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "credProtect"));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, extensions.credProtect));
|
||||
}
|
||||
if (extensions.hmac_secret != NULL) {
|
||||
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "hmac-secret"));
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, *extensions.hmac_secret));
|
||||
}
|
||||
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
ext_len = cbor_encoder_get_buffer_size(&encoder, ext);
|
||||
flags |= FIDO2_AUT_FLAG_ED;
|
||||
}
|
||||
uint8_t pkey[66];
|
||||
const mbedtls_ecp_curve_info *cinfo = mbedtls_ecp_curve_info_from_grp_id(ekey.grp.id);
|
||||
if (cinfo == NULL)
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
size_t olen = 0, pkey_len = ceil((float)cinfo->bit_size/8);
|
||||
uint32_t ctr = get_sign_counter();
|
||||
uint8_t cbor_buf[1024];
|
||||
cbor_encoder_init(&encoder, cbor_buf, sizeof(cbor_buf), 0);
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 5));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 2));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 3));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder, -alg));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, curve));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder, 2));
|
||||
mbedtls_mpi_write_binary(&ekey.Q.X, pkey, pkey_len);
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, pkey, pkey_len));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder, 3));
|
||||
mbedtls_mpi_write_binary(&ekey.Q.Y, pkey, pkey_len);
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, pkey, pkey_len));
|
||||
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
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);
|
||||
uint8_t *pa = aut_data;
|
||||
memcpy(pa, rp_id_hash, 32); pa += 32;
|
||||
*pa++ = flags;
|
||||
*pa++ = ctr >> 24;
|
||||
*pa++ = ctr >> 16;
|
||||
*pa++ = ctr >> 8;
|
||||
*pa++ = ctr & 0xff;
|
||||
memcpy(pa, aaguid, 16); pa += 16;
|
||||
*pa++ = cred_id_len >> 8;
|
||||
*pa++ = cred_id_len & 0xff;
|
||||
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)
|
||||
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);
|
||||
|
||||
bool self_attestation = true;
|
||||
if (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);
|
||||
self_attestation = false;
|
||||
}
|
||||
ret = mbedtls_ecdsa_write_signature(&ekey, MBEDTLS_MD_SHA256, hash, 32, sig, sizeof(sig), &olen, random_gen, NULL);
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
|
||||
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 3));
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "packed"));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x02));
|
||||
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_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"));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, sig, olen));
|
||||
if (self_attestation == false) {
|
||||
CborEncoder arrEncoder;
|
||||
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_certdev), file_get_size(ef_certdev)));
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder2, &arrEncoder));
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
|
||||
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
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)
|
||||
CBOR_ERROR(CTAP2_ERR_KEY_STORE_FULL);
|
||||
}
|
||||
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 (aut_data)
|
||||
free(aut_data);
|
||||
if (error != CborNoError) {
|
||||
if (error == CborErrorImproperValue)
|
||||
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
||||
return error;
|
||||
}
|
||||
res_APDU_size = resp_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
60
src/fido/cbor_make_credential.h
Normal file
60
src/fido/cbor_make_credential.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* This file is part of the Pico FIDO distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _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
|
||||
{
|
||||
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;
|
||||
|
||||
|
||||
#endif //_CBOR_MAKE_CREDENTIAL_H_
|
||||
38
src/fido/cbor_reset.c
Normal file
38
src/fido/cbor_reset.c
Normal file
@@ -0,0 +1,38 @@
|
||||
|
||||
/*
|
||||
* 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 "ctap2_cbor.h"
|
||||
#include "file.h"
|
||||
#include "fido.h"
|
||||
#include "apdu.h"
|
||||
#include "ctap.h"
|
||||
#include "bsp/board.h"
|
||||
|
||||
extern void scan_all();
|
||||
|
||||
int cbor_reset() {
|
||||
#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)
|
||||
return CTAP2_ERR_USER_ACTION_TIMEOUT;
|
||||
initialize_flash(true);
|
||||
init_fido(true);
|
||||
return 0;
|
||||
}
|
||||
@@ -18,69 +18,66 @@
|
||||
#include "fido.h"
|
||||
#include "hsm.h"
|
||||
#include "apdu.h"
|
||||
#include "u2f.h"
|
||||
#include "ctap.h"
|
||||
#include "mbedtls/ecdsa.h"
|
||||
#include "random.h"
|
||||
#include "files.h"
|
||||
#include "credential.h"
|
||||
|
||||
int cmd_authenticate() {
|
||||
U2F_AUTHENTICATE_REQ *req = (U2F_AUTHENTICATE_REQ *)apdu.data;
|
||||
U2F_AUTHENTICATE_RESP *resp = (U2F_AUTHENTICATE_RESP *)res_APDU;
|
||||
if (scan_files() != CCID_OK)
|
||||
return SW_EXEC_ERROR();
|
||||
if (req->keyHandleLen != KEY_HANDLE_LEN)
|
||||
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)
|
||||
return SW_WRONG_DATA();
|
||||
if (P1(apdu) == U2F_AUTH_ENFORCE && wait_button_pressed() == true)
|
||||
if (req->keyHandleLen < KEY_HANDLE_LEN)
|
||||
return SW_INCORRECT_PARAMS();
|
||||
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 = derive_key(req->appId, false, req->keyHandle, &key);
|
||||
int ret = 0;
|
||||
uint8_t *tmp_kh = (uint8_t *)calloc(1, req->keyHandleLen);
|
||||
memcpy(tmp_kh, req->keyHandle, req->keyHandleLen);
|
||||
if (credential_verify(tmp_kh, req->keyHandleLen, req->appId) == 0) {
|
||||
DEBUG_DATA(req->keyHandle, req->keyHandleLen);
|
||||
ret = fido_load_key(FIDO2_CURVE_P256, req->keyHandle, &key);
|
||||
}
|
||||
else {
|
||||
ret = derive_key(req->appId, false, req->keyHandle, MBEDTLS_ECP_DP_SECP256R1, &key);
|
||||
}
|
||||
free(tmp_kh);
|
||||
if (ret != CCID_OK) {
|
||||
mbedtls_ecdsa_free(&key);
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
if (P1(apdu) == U2F_AUTH_CHECK_ONLY) {
|
||||
for (int i = 0; i < KEY_PATH_ENTRIES; i++) {
|
||||
uint32_t k = *(uint32_t *)&req->keyHandle[i*sizeof(uint32_t)];
|
||||
if (!(k & 0x80000000)) {
|
||||
mbedtls_ecdsa_free(&key);
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
}
|
||||
uint8_t hmac[32], d[32];
|
||||
ret = mbedtls_ecp_write_key(&key, d, sizeof(d));
|
||||
mbedtls_ecdsa_free(&key);
|
||||
if (ret != 0)
|
||||
return SW_WRONG_DATA();
|
||||
uint8_t key_base[U2F_APPID_SIZE + KEY_PATH_LEN];
|
||||
memcpy(key_base, req->appId, U2F_APPID_SIZE);
|
||||
memcpy(key_base + U2F_APPID_SIZE, req->keyHandle, KEY_PATH_LEN);
|
||||
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));
|
||||
if (memcmp(req->keyHandle + KEY_PATH_LEN, hmac, sizeof(hmac)) != 0)
|
||||
return SW_WRONG_DATA();
|
||||
if (verify_key(req->appId, req->keyHandle, &key) != 0) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
if (P1(apdu) == CTAP_AUTH_CHECK_ONLY) {
|
||||
return SW_CONDITIONS_NOT_SATISFIED();
|
||||
}
|
||||
resp->flags = 0;
|
||||
resp->flags |= P1(apdu) == U2F_AUTH_ENFORCE ? U2F_AUTH_FLAG_TUP : 0x0;
|
||||
uint32_t ctr = *(uint32_t *)file_get_data(ef_counter);
|
||||
resp->flags |= P1(apdu) == CTAP_AUTH_ENFORCE ? CTAP_AUTH_FLAG_TUP : 0x0;
|
||||
uint32_t ctr = get_sign_counter();
|
||||
resp->ctr[0] = ctr >> 24;
|
||||
resp->ctr[1] = ctr >> 16;
|
||||
resp->ctr[2] = ctr >> 8;
|
||||
resp->ctr[3] = ctr & 0xff;
|
||||
uint8_t hash[32], sig_base[U2F_APPID_SIZE + 1 + 4 + U2F_CHAL_SIZE];
|
||||
memcpy(sig_base, req->appId, U2F_APPID_SIZE);
|
||||
memcpy(sig_base+U2F_APPID_SIZE, &resp->flags, sizeof(uint8_t));
|
||||
memcpy(sig_base + U2F_APPID_SIZE + 1, resp->ctr, 4);
|
||||
memcpy(sig_base + U2F_APPID_SIZE + 1 + 4, req->chal, U2F_CHAL_SIZE);
|
||||
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 + 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);
|
||||
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, U2F_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)
|
||||
return SW_EXEC_ERROR();
|
||||
|
||||
@@ -18,43 +18,43 @@
|
||||
#include "fido.h"
|
||||
#include "hsm.h"
|
||||
#include "apdu.h"
|
||||
#include "u2f.h"
|
||||
#include "ctap.h"
|
||||
#include "mbedtls/ecdsa.h"
|
||||
#include "random.h"
|
||||
#include "files.h"
|
||||
|
||||
int cmd_register() {
|
||||
U2F_REGISTER_REQ *req = (U2F_REGISTER_REQ *)apdu.data;
|
||||
U2F_REGISTER_RESP *resp = (U2F_REGISTER_RESP *)res_APDU;
|
||||
resp->registerId = U2F_REGISTER_ID;
|
||||
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() != CCID_OK)
|
||||
return SW_EXEC_ERROR();
|
||||
if (apdu.nc != U2F_APPID_SIZE + U2F_CHAL_SIZE)
|
||||
//if (scan_files(true) != CCID_OK)
|
||||
// return SW_EXEC_ERROR();
|
||||
if (apdu.nc != CTAP_APPID_SIZE + CTAP_CHAL_SIZE)
|
||||
return SW_WRONG_LENGTH();
|
||||
if (wait_button_pressed() == true)
|
||||
return SW_CONDITIONS_NOT_SATISFIED();
|
||||
mbedtls_ecdsa_context key;
|
||||
mbedtls_ecdsa_init(&key);
|
||||
int ret = derive_key(req->appId, true, resp->keyHandleCertSig, &key);
|
||||
int ret = derive_key(req->appId, true, resp->keyHandleCertSig, MBEDTLS_ECP_DP_SECP256R1, &key);
|
||||
if (ret != CCID_OK) {
|
||||
mbedtls_ecdsa_free(&key);
|
||||
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, U2F_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 + U2F_APPID_SIZE + U2F_CHAL_SIZE + KEY_HANDLE_LEN + U2F_EC_POINT_SIZE];
|
||||
sign_base[0] = U2F_REGISTER_HASH_ID;
|
||||
memcpy(sign_base + 1, req->appId, U2F_APPID_SIZE);
|
||||
memcpy(sign_base + 1 + U2F_APPID_SIZE, req->chal, U2F_CHAL_SIZE);
|
||||
memcpy(sign_base + 1 + U2F_APPID_SIZE + U2F_CHAL_SIZE, resp->keyHandleCertSig, KEY_HANDLE_LEN);
|
||||
memcpy(sign_base + 1 + U2F_APPID_SIZE + U2F_CHAL_SIZE + KEY_HANDLE_LEN, (uint8_t *)&resp->pubKey, U2F_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)
|
||||
return SW_EXEC_ERROR();
|
||||
@@ -64,11 +64,10 @@ int cmd_register() {
|
||||
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, U2F_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)
|
||||
return SW_EXEC_ERROR();
|
||||
res_APDU_size = sizeof(U2F_REGISTER_RESP) - sizeof(resp->keyHandleCertSig) + KEY_HANDLE_LEN + ef_certdev_size + olen;
|
||||
DEBUG_PAYLOAD(res_APDU, res_APDU_size);
|
||||
res_APDU_size = sizeof(CTAP_REGISTER_RESP) - sizeof(resp->keyHandleCertSig) + KEY_HANDLE_LEN + ef_certdev_size + olen;
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
272
src/fido/credential.c
Normal file
272
src/fido/credential.c
Normal file
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
* 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 "common.h"
|
||||
#include "mbedtls/chachapoly.h"
|
||||
#include "mbedtls/sha256.h"
|
||||
#include "credential.h"
|
||||
#include "bsp/board.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;
|
||||
memset(key, 0, sizeof(key));
|
||||
credential_derive_chacha_key(key);
|
||||
mbedtls_chachapoly_context chatx;
|
||||
mbedtls_chachapoly_init(&chatx);
|
||||
mbedtls_chachapoly_setkey(&chatx, key);
|
||||
return mbedtls_chachapoly_auth_decrypt(&chatx, cred_id_len - (4 + 12 + 16), iv, rp_id_hash, 32, tag, cipher, cipher);
|
||||
}
|
||||
|
||||
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);
|
||||
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));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, rp_id_hash, 32));
|
||||
CBOR_APPEND_KEY_UINT_VAL_BYTES(mapEncoder, 0x03, *userId);
|
||||
CBOR_APPEND_KEY_UINT_VAL_STRING(mapEncoder, 0x04, *userName);
|
||||
CBOR_APPEND_KEY_UINT_VAL_STRING(mapEncoder, 0x05, *userDisplayName);
|
||||
CBOR_APPEND_KEY_UINT_VAL_UINT(mapEncoder, 0x06, board_millis());
|
||||
if (extensions->present == true) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x07));
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, CborIndefiniteLength));
|
||||
if (extensions->credProtect != 0) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "credProtect"));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, extensions->credProtect));
|
||||
}
|
||||
if (extensions->hmac_secret != NULL) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "hmac-secret"));
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder2, *extensions->hmac_secret));
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
|
||||
}
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x08));
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, use_sign_count));
|
||||
if (alg != FIDO2_ALG_ES256 || curve != FIDO2_CURVE_P256) {
|
||||
CBOR_APPEND_KEY_UINT_VAL_INT(mapEncoder, 0x09, alg);
|
||||
CBOR_APPEND_KEY_UINT_VAL_INT(mapEncoder, 0x0A, curve);
|
||||
}
|
||||
if (opts->present == true) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0B));
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, CborIndefiniteLength));
|
||||
if (opts->rk != NULL) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "rk"));
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder2, opts->rk == ptrue));
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
size_t rs = cbor_encoder_get_buffer_size(&encoder, cred_id);
|
||||
*cred_id_len = 4 + 12 + rs + 16;
|
||||
uint8_t key[32];
|
||||
memset(key, 0, sizeof(key));
|
||||
credential_derive_chacha_key(key);
|
||||
uint8_t iv[12];
|
||||
random_gen(NULL, iv, sizeof(12));
|
||||
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);
|
||||
mbedtls_chachapoly_free(&chatx);
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
}
|
||||
memcpy(cred_id, "\xf1\xd0\x02\x00", 4);
|
||||
memcpy(cred_id + 4, iv, 12);
|
||||
|
||||
err:
|
||||
if (error != CborNoError) {
|
||||
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 ret = 0;
|
||||
CborError error;
|
||||
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)
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_CREDENTIAL);
|
||||
}
|
||||
else {
|
||||
CborParser parser;
|
||||
CborValue map;
|
||||
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) {
|
||||
uint64_t val_u = 0;
|
||||
CBOR_FIELD_GET_UINT(val_u, 1);
|
||||
if (val_u == 0x01) {
|
||||
CBOR_FIELD_GET_TEXT(cred->rpId, 1);
|
||||
}
|
||||
else if (val_u == 0x03) {
|
||||
CBOR_FIELD_GET_BYTES(cred->userId, 1);
|
||||
}
|
||||
else if (val_u == 0x06) {
|
||||
CBOR_FIELD_GET_UINT(cred->creation, 1);
|
||||
}
|
||||
else if (val_u == 0x07) {
|
||||
cred->extensions.present = true;
|
||||
CBOR_PARSE_MAP_START(_f1, 2)
|
||||
{
|
||||
CBOR_FIELD_GET_KEY_TEXT(2);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "hmac-secret", cred->extensions.hmac_secret);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_UINT(2, "credProtect", cred->extensions.credProtect);
|
||||
CBOR_ADVANCE(2);
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f1, 2);
|
||||
}
|
||||
else if (val_u == 0x08) {
|
||||
CBOR_FIELD_GET_BOOL(cred->use_sign_count, 1);
|
||||
}
|
||||
else if (val_u == 0x09) {
|
||||
CBOR_FIELD_GET_INT(cred->alg, 1);
|
||||
}
|
||||
else if (val_u == 0x0A) {
|
||||
CBOR_FIELD_GET_INT(cred->curve, 1);
|
||||
}
|
||||
else if (val_u == 0x0B) {
|
||||
cred->opts.present = true;
|
||||
CBOR_PARSE_MAP_START(_f1, 2)
|
||||
{
|
||||
CBOR_FIELD_GET_KEY_TEXT(2);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "rk", cred->opts.rk);
|
||||
CBOR_ADVANCE(2);
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f1, 2);
|
||||
}
|
||||
else {
|
||||
CBOR_ADVANCE(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
cred->id.present = true;
|
||||
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:
|
||||
free(copy_cred_id);
|
||||
if (error != CborNoError) {
|
||||
if (error == CborErrorImproperValue)
|
||||
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
||||
return error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void credential_free(Credential *cred) {
|
||||
CBOR_FREE_BYTE_STRING(cred->rpId);
|
||||
CBOR_FREE_BYTE_STRING(cred->userId);
|
||||
CBOR_FREE_BYTE_STRING(cred->userName);
|
||||
CBOR_FREE_BYTE_STRING(cred->userDisplayName);
|
||||
CBOR_FREE_BYTE_STRING(cred->id);
|
||||
cred->present = false;
|
||||
cred->extensions.present = false;
|
||||
cred->opts.present = false;
|
||||
}
|
||||
|
||||
int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash) {
|
||||
int sloti = -1;
|
||||
Credential cred = {0};
|
||||
int ret = 0;
|
||||
ret = credential_load(cred_id, cred_id_len, rp_id_hash, &cred);
|
||||
if (ret != 0) {
|
||||
credential_free(&cred);
|
||||
return ret;
|
||||
}
|
||||
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
|
||||
file_t *ef = search_dynamic_file(EF_CRED + i);
|
||||
Credential rcred = {0};
|
||||
if (!file_has_data(ef)) {
|
||||
if (sloti == -1)
|
||||
sloti = i;
|
||||
continue;
|
||||
}
|
||||
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);
|
||||
if (ret != 0) {
|
||||
credential_free(&rcred);
|
||||
continue;
|
||||
}
|
||||
if (memcmp(rcred.userId.data, cred.userId.data, MIN(rcred.userId.len, cred.userId.len)) == 0) {
|
||||
sloti = i;
|
||||
credential_free(&rcred);
|
||||
break;
|
||||
}
|
||||
credential_free(&rcred);
|
||||
}
|
||||
if (sloti == -1)
|
||||
return -1;
|
||||
credential_free(&cred);
|
||||
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);
|
||||
flash_write_data_to_file(ef, data, cred_id_len + 32);
|
||||
low_flash_available();
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
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 *)"\xf1\xd0\x02\x00", 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;
|
||||
}
|
||||
|
||||
int credential_derive_chacha_key(uint8_t *outk) {
|
||||
memset(outk, 0, 64);
|
||||
int r = 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 *)"\xf1\xd0\x02\x00", 4, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *)"Encryption key", 14, outk);
|
||||
return 0;
|
||||
}
|
||||
63
src/fido/credential.h
Normal file
63
src/fido/credential.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* This file is part of the Pico FIDO distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _CREDENTIAL_H_
|
||||
#define _CREDENTIAL_H_
|
||||
|
||||
#include "ctap2_cbor.h"
|
||||
|
||||
typedef struct CredOptions {
|
||||
const bool *rk;
|
||||
const bool *up;
|
||||
const bool *uv;
|
||||
bool present;
|
||||
} CredOptions;
|
||||
|
||||
typedef struct CredExtensions {
|
||||
const bool *hmac_secret;
|
||||
uint64_t credProtect;
|
||||
bool present;
|
||||
} CredExtensions;
|
||||
|
||||
typedef struct Credential
|
||||
{
|
||||
CborCharString rpId;
|
||||
CborByteString userId;
|
||||
CborCharString userName;
|
||||
CborCharString userDisplayName;
|
||||
uint64_t creation;
|
||||
CredExtensions extensions;
|
||||
const bool *use_sign_count;
|
||||
int64_t alg;
|
||||
int64_t curve;
|
||||
CborByteString id;
|
||||
CredOptions opts;
|
||||
bool present;
|
||||
} Credential;
|
||||
|
||||
#define CRED_PROT_UV_OPTIONAL 0x01
|
||||
#define CRED_PROT_UV_OPTIONAL_WITH_LIST 0x02
|
||||
#define CRED_PROT_UV_REQUIRED 0x03
|
||||
|
||||
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 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_derive_hmac_key(const uint8_t *cred_id, size_t cred_id_len, uint8_t *outk);
|
||||
|
||||
#endif // _CREDENTIAL_H_
|
||||
161
src/fido/ctap.h
Normal file
161
src/fido/ctap.h
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* This file is part of the Pico HSM SDK distribution (https://github.com/polhenarejos/pico-hsm-sdk).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _CTAP_H_
|
||||
#define _CTAP_H_
|
||||
|
||||
#ifdef _MSC_VER // Windows
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned long int uint64_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// General constants
|
||||
|
||||
#define CTAP_EC_KEY_SIZE 32 // EC key size in bytes
|
||||
#define CTAP_EC_POINT_SIZE ((CTAP_EC_KEY_SIZE * 2) + 1) // Size of EC point
|
||||
#define CTAP_MAX_KH_SIZE 128 // Max size of key handle
|
||||
#define CTAP_MAX_ATT_CERT_SIZE 2048 // Max size of attestation certificate
|
||||
#define CTAP_MAX_EC_SIG_SIZE 72 // Max size of DER coded EC signature
|
||||
#define CTAP_CTR_SIZE 4 // Size of counter field
|
||||
#define CTAP_APPID_SIZE 32 // Size of application id
|
||||
#define CTAP_CHAL_SIZE 32 // Size of challenge
|
||||
|
||||
#define ENC_SIZE(x) ((x + 7) & 0xfff8)
|
||||
|
||||
// EC (uncompressed) point
|
||||
|
||||
#define CTAP_POINT_UNCOMPRESSED 0x04 // Uncompressed point format
|
||||
|
||||
typedef struct {
|
||||
uint8_t pointFormat; // Point type
|
||||
uint8_t x[CTAP_EC_KEY_SIZE]; // X-value
|
||||
uint8_t y[CTAP_EC_KEY_SIZE]; // Y-value
|
||||
} CTAP_EC_POINT;
|
||||
|
||||
// CTAP MSG commands
|
||||
|
||||
#define CTAP_REGISTER 0x01 // Registration command
|
||||
#define CTAP_AUTHENTICATE 0x02 // Authenticate/sign command
|
||||
#define CTAP_VERSION 0x03 // Read version string command
|
||||
|
||||
#define CTAP_VENDOR_FIRST 0x40 // First vendor defined command
|
||||
#define CTAP_VENDOR_LAST 0xbf // Last vendor defined command
|
||||
|
||||
// CTAP_CMD_REGISTER command defines
|
||||
|
||||
#define CTAP_REGISTER_ID 0x05 // Version 2 registration identifier
|
||||
#define CTAP_REGISTER_HASH_ID 0x00 // Version 2 hash identintifier
|
||||
|
||||
typedef struct {
|
||||
uint8_t chal[CTAP_CHAL_SIZE]; // Challenge
|
||||
uint8_t appId[CTAP_APPID_SIZE]; // Application id
|
||||
} CTAP_REGISTER_REQ;
|
||||
|
||||
typedef struct {
|
||||
uint8_t registerId; // Registration identifier (CTAP_REGISTER_ID_V2)
|
||||
CTAP_EC_POINT pubKey; // Generated public key
|
||||
uint8_t keyHandleLen; // Length of key handle
|
||||
uint8_t keyHandleCertSig[
|
||||
CTAP_MAX_KH_SIZE + // Key handle
|
||||
CTAP_MAX_ATT_CERT_SIZE + // Attestation certificate
|
||||
CTAP_MAX_EC_SIG_SIZE]; // Registration signature
|
||||
} CTAP_REGISTER_RESP;
|
||||
|
||||
// CTAP_CMD_AUTHENTICATE command defines
|
||||
|
||||
// Authentication control byte
|
||||
|
||||
#define CTAP_AUTH_ENFORCE 0x03 // Enforce user presence and sign
|
||||
#define CTAP_AUTH_CHECK_ONLY 0x07 // Check only
|
||||
#define CTAP_AUTH_FLAG_TUP 0x01 // Test of user presence set
|
||||
|
||||
typedef struct {
|
||||
uint8_t chal[CTAP_CHAL_SIZE]; // Challenge
|
||||
uint8_t appId[CTAP_APPID_SIZE]; // Application id
|
||||
uint8_t keyHandleLen; // Length of key handle
|
||||
uint8_t keyHandle[CTAP_MAX_KH_SIZE]; // Key handle
|
||||
} CTAP_AUTHENTICATE_REQ;
|
||||
|
||||
typedef struct {
|
||||
uint8_t flags; // CTAP_AUTH_FLAG_ values
|
||||
uint8_t ctr[CTAP_CTR_SIZE]; // Counter field (big-endian)
|
||||
uint8_t sig[CTAP_MAX_EC_SIG_SIZE]; // Signature
|
||||
} CTAP_AUTHENTICATE_RESP;
|
||||
|
||||
// Command status responses
|
||||
|
||||
#define CTAP_SW_NO_ERROR 0x9000 // SW_NO_ERROR
|
||||
#define CTAP_SW_WRONG_DATA 0x6A80 // SW_WRONG_DATA
|
||||
#define CTAP_SW_CONDITIONS_NOT_SATISFIED 0x6985 // SW_CONDITIONS_NOT_SATISFIED
|
||||
#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
|
||||
|
||||
#endif // _CTAP_H_
|
||||
249
src/fido/ctap2_cbor.h
Normal file
249
src/fido/ctap2_cbor.h
Normal file
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* This file is part of the Pico FIDO distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _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();
|
||||
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 int cbor_client_pin(const uint8_t *data, size_t len);
|
||||
extern int cbor_get_assertion(const uint8_t *data, size_t len, bool next);
|
||||
extern int cbor_get_next_assertion(const uint8_t *data, size_t len);
|
||||
extern const uint8_t aaguid[16];
|
||||
|
||||
extern const bool _btrue, _bfalse;
|
||||
#define ptrue (&_btrue)
|
||||
#define pfalse (&_bfalse)
|
||||
|
||||
#define CBOR_CHECK(f) \
|
||||
do \
|
||||
{ \
|
||||
error = f; \
|
||||
if (error != CborNoError) \
|
||||
{ \
|
||||
printf("Cannot encode CBOR [%s:%d]: %s (%d)\n", __FILE__, __LINE__, #f, error); \
|
||||
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 [%s:%d]: %d\n", __FILE__, __LINE__, e); \
|
||||
goto err; \
|
||||
} while(0)
|
||||
|
||||
#define CBOR_ASSERT(c) \
|
||||
do \
|
||||
{ \
|
||||
if (!c) \
|
||||
{ \
|
||||
error = CborErrorImproperValue; \
|
||||
printf("Cbor ASSERT [%s:%d]: %s\n", __FILE__, __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; \
|
||||
(v).present = false; \
|
||||
} 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; \
|
||||
continue; \
|
||||
}
|
||||
|
||||
#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; \
|
||||
continue; \
|
||||
}
|
||||
|
||||
#define CBOR_FIELD_KEY_TEXT_VAL_INT(_n, _t, _v) \
|
||||
if (strcmp(_fd##_n, _t) == 0) { \
|
||||
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);\
|
||||
continue; \
|
||||
}
|
||||
|
||||
#define CBOR_FIELD_KEY_TEXT_VAL_BOOL(_n, _t, _v) \
|
||||
if (strcmp(_fd##_n, _t) == 0) { \
|
||||
CBOR_FIELD_GET_BOOL(_v, _n);\
|
||||
continue; \
|
||||
}
|
||||
|
||||
#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_BYTES(p, k, v) \
|
||||
do { \
|
||||
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)
|
||||
|
||||
#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)
|
||||
|
||||
|
||||
#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_INT(p, k, v) \
|
||||
do { \
|
||||
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)
|
||||
|
||||
#define CBOR_APPEND_KEY_UINT_VAL_PBOOL(p, k, v) \
|
||||
do { \
|
||||
if (v != NULL) {\
|
||||
CBOR_CHECK(cbor_encode_uint(&(p), (k))); \
|
||||
CBOR_CHECK(cbor_encode_boolean(&(p), v == ptrue ? true : false)); \
|
||||
} } while(0)
|
||||
|
||||
#endif //_CTAP2_CBOR_H_
|
||||
178
src/fido/fido.c
178
src/fido/fido.c
@@ -19,22 +19,26 @@
|
||||
#include "fido.h"
|
||||
#include "hsm.h"
|
||||
#include "apdu.h"
|
||||
#include "u2f.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"
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void init_fido();
|
||||
void init_fido(bool);
|
||||
int fido_process_apdu();
|
||||
int fido_unload();
|
||||
|
||||
pinUvAuthToken_t paut = {0};
|
||||
|
||||
const uint8_t fido_aid[] = {
|
||||
8,
|
||||
0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01
|
||||
@@ -45,7 +49,7 @@ app_t *fido_select(app_t *a) {
|
||||
a->process_apdu = fido_process_apdu;
|
||||
a->unload = fido_unload;
|
||||
current_app = a;
|
||||
init_fido();
|
||||
//init_fido(false);
|
||||
return a;
|
||||
}
|
||||
|
||||
@@ -58,7 +62,35 @@ int fido_unload() {
|
||||
return CCID_OK;
|
||||
}
|
||||
|
||||
int x509_create_cert(mbedtls_ecdsa_context *ecdsa, uint8_t *buffer, size_t buffer_size) {
|
||||
mbedtls_ecp_group_id fido_curve_to_mbedtls(int curve) {
|
||||
if (curve == FIDO2_CURVE_P256)
|
||||
return MBEDTLS_ECP_DP_SECP256R1;
|
||||
else if (curve == FIDO2_CURVE_P384)
|
||||
return MBEDTLS_ECP_DP_SECP384R1;
|
||||
else if (curve == FIDO2_CURVE_P521)
|
||||
return MBEDTLS_ECP_DP_SECP521R1;
|
||||
else if (curve == FIDO2_CURVE_P256K1)
|
||||
return MBEDTLS_ECP_DP_SECP256K1;
|
||||
else if (curve == FIDO2_CURVE_X25519)
|
||||
return MBEDTLS_ECP_DP_CURVE25519;
|
||||
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)
|
||||
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;
|
||||
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) {
|
||||
mbedtls_x509write_cert ctx;
|
||||
mbedtls_x509write_crt_init(&ctx);
|
||||
mbedtls_x509write_crt_set_version(&ctx, MBEDTLS_X509_CRT_VERSION_3);
|
||||
@@ -67,7 +99,7 @@ int x509_create_cert(mbedtls_ecdsa_context *ecdsa, uint8_t *buffer, size_t buffe
|
||||
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, random_gen, NULL);
|
||||
mbedtls_mpi_fill_random(&serial, 32, core1 ? random_gen : random_gen_core0, NULL);
|
||||
mbedtls_x509write_crt_set_serial(&ctx, &serial);
|
||||
mbedtls_pk_context key;
|
||||
mbedtls_pk_init(&key);
|
||||
@@ -80,7 +112,7 @@ int x509_create_cert(mbedtls_ecdsa_context *ecdsa, uint8_t *buffer, size_t buffe
|
||||
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, random_gen, NULL);
|
||||
int ret = mbedtls_x509write_crt_der(&ctx, buffer, buffer_size, core1 ? random_gen : random_gen_core0, NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -92,29 +124,60 @@ int load_keydev(uint8_t *key) {
|
||||
return CCID_OK;
|
||||
}
|
||||
|
||||
int derive_key(const uint8_t *app_id, bool new_key, uint8_t *key_handle, mbedtls_ecdsa_context *key) {
|
||||
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)];
|
||||
if (!(k & 0x80000000)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
mbedtls_ecdsa_context ctx;
|
||||
if (key == NULL) {
|
||||
mbedtls_ecdsa_init(&ctx);
|
||||
key = &ctx;
|
||||
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)
|
||||
mbedtls_ecdsa_free(&ctx);
|
||||
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);
|
||||
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 r = 0;
|
||||
memset(outk, 0, sizeof(outk));
|
||||
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++)
|
||||
{
|
||||
for (int i = 0; i < KEY_PATH_ENTRIES; i++) {
|
||||
if (new_key == true) {
|
||||
uint32_t val = 0x80000000 | *((uint32_t *)random_bytes_get(sizeof(uint32_t)));
|
||||
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));
|
||||
}
|
||||
if ((r = mbedtls_hkdf(md_info, &key_handle[i], sizeof(uint32_t), outk, 32, outk + 32, 32, outk, sizeof(outk))) != 0)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (new_key == true) {
|
||||
uint8_t key_base[U2F_APPID_SIZE + KEY_PATH_LEN];
|
||||
memcpy(key_base, app_id, U2F_APPID_SIZE);
|
||||
memcpy(key_base + U2F_APPID_SIZE, key_handle, KEY_PATH_LEN);
|
||||
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)
|
||||
{
|
||||
mbedtls_platform_zeroize(outk, sizeof(outk));
|
||||
@@ -122,26 +185,29 @@ int derive_key(const uint8_t *app_id, bool new_key, uint8_t *key_handle, mbedtls
|
||||
}
|
||||
}
|
||||
if (key != NULL) {
|
||||
mbedtls_ecp_group_load(&key->grp, MBEDTLS_ECP_DP_SECP256R1);
|
||||
r = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, key, outk, 32);
|
||||
mbedtls_ecp_group_load(&key->grp, curve);
|
||||
const mbedtls_ecp_curve_info *cinfo = mbedtls_ecp_curve_info_from_grp_id(curve);
|
||||
if (cinfo == NULL)
|
||||
return 1;
|
||||
r = mbedtls_ecp_read_key(curve, key, outk, ceil((float)cinfo->bit_size/8));
|
||||
mbedtls_platform_zeroize(outk, sizeof(outk));
|
||||
if (r != 0)
|
||||
return r;
|
||||
return mbedtls_ecp_mul(&key->grp, &key->Q, &key->d, &key->grp.G, random_gen, NULL );
|
||||
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() {
|
||||
int scan_files(bool core1) {
|
||||
ef_keydev = search_by_fid(EF_KEY_DEV, NULL, SPECIFY_EF);
|
||||
if (ef_keydev) {
|
||||
if (!ef_keydev->data) {
|
||||
if (!file_has_data(ef_keydev)) {
|
||||
printf("KEY DEVICE is empty. Generating SECP256R1 curve...");
|
||||
mbedtls_ecdsa_context ecdsa;
|
||||
mbedtls_ecdsa_init(&ecdsa);
|
||||
uint8_t index = 0;
|
||||
int ret = mbedtls_ecdsa_genkey(&ecdsa, MBEDTLS_ECP_DP_SECP256R1, random_gen, &index);
|
||||
int ret = mbedtls_ecdsa_genkey(&ecdsa, MBEDTLS_ECP_DP_SECP256R1, core1 ? random_gen : random_gen_core0, &index);
|
||||
if (ret != 0) {
|
||||
mbedtls_ecdsa_free(&ecdsa);
|
||||
return ret;
|
||||
@@ -151,8 +217,8 @@ int scan_files() {
|
||||
mbedtls_mpi_write_binary(&ecdsa.d, kdata, key_size);
|
||||
ret = flash_write_data_to_file(ef_keydev, kdata, key_size);
|
||||
mbedtls_platform_zeroize(kdata, sizeof(kdata));
|
||||
mbedtls_ecdsa_free(&ecdsa);
|
||||
if (ret != CCID_OK) {
|
||||
mbedtls_ecdsa_free(&ecdsa);
|
||||
return ret;
|
||||
}
|
||||
printf(" done!\n");
|
||||
@@ -163,21 +229,21 @@ int scan_files() {
|
||||
}
|
||||
ef_certdev = search_by_fid(EF_EE_DEV, NULL, SPECIFY_EF);
|
||||
if (ef_certdev) {
|
||||
if (file_get_size(ef_certdev) == 0 || !ef_certdev->data) {
|
||||
if (!file_has_data(ef_certdev)) {
|
||||
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), 32);
|
||||
printf("ret %d\n", ret);
|
||||
int ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &key, file_get_data(ef_keydev), file_get_size(ef_keydev));
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
ret = x509_create_cert(&key, cert, sizeof(cert));
|
||||
ret = mbedtls_ecp_mul(&key.grp, &key.Q, &key.d, &key.grp.G, random_gen, NULL);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
ret = x509_create_cert(&key, cert, sizeof(cert), core1);
|
||||
mbedtls_ecdsa_free(&key);
|
||||
printf("ret %d\n", ret);
|
||||
if (ret <= 0)
|
||||
return ret;
|
||||
flash_write_data_to_file(ef_certdev, cert + sizeof(cert) - ret, ret);
|
||||
DEBUG_PAYLOAD(cert + sizeof(cert) - ret, ret);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -185,7 +251,7 @@ int scan_files() {
|
||||
}
|
||||
ef_counter = search_by_fid(EF_COUNTER, NULL, SPECIFY_EF);
|
||||
if (ef_counter) {
|
||||
if (file_get_size(ef_counter) == 0 || !ef_counter->data) {
|
||||
if (!file_has_data(ef_counter)) {
|
||||
uint32_t v = 0;
|
||||
flash_write_data_to_file(ef_counter, (uint8_t *)&v, sizeof(v));
|
||||
}
|
||||
@@ -193,25 +259,63 @@ 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);
|
||||
ef_authtoken = search_by_fid(EF_AUTHTOKEN, NULL, SPECIFY_EF);
|
||||
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));
|
||||
flash_write_data_to_file(ef_authtoken, t, sizeof(t));
|
||||
}
|
||||
paut.data = file_get_data(ef_authtoken);
|
||||
paut.len = file_get_size(ef_authtoken);
|
||||
}
|
||||
else {
|
||||
printf("FATAL ERROR: Auth Token not found in memory!\r\n");
|
||||
}
|
||||
low_flash_available();
|
||||
return CCID_OK;
|
||||
}
|
||||
|
||||
void scan_all() {
|
||||
void scan_all(bool core1) {
|
||||
scan_flash();
|
||||
scan_files(core1);
|
||||
}
|
||||
|
||||
void init_fido() {
|
||||
scan_all();
|
||||
void init_fido(bool core1) {
|
||||
scan_all(core1);
|
||||
}
|
||||
|
||||
bool wait_button_pressed() {
|
||||
uint32_t val = EV_PRESS_BUTTON;
|
||||
#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);
|
||||
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
|
||||
return false;
|
||||
//user_present_time_limit = board_millis();
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t get_sign_counter() {
|
||||
uint8_t *caddr = file_get_data(ef_counter);
|
||||
return (*caddr) | (*(caddr + 1) << 8) | (*(caddr + 2) << 16) | (*(caddr + 3) << 24);
|
||||
}
|
||||
|
||||
typedef struct cmd
|
||||
@@ -225,13 +329,15 @@ extern int cmd_authenticate();
|
||||
extern int cmd_version();
|
||||
|
||||
static const cmd_t cmds[] = {
|
||||
{ U2F_REGISTER, cmd_register },
|
||||
{ U2F_AUTHENTICATE, cmd_authenticate },
|
||||
{ U2F_VERSION, cmd_version },
|
||||
{ CTAP_REGISTER, cmd_register },
|
||||
{ CTAP_AUTHENTICATE, cmd_authenticate },
|
||||
{ CTAP_VERSION, cmd_version },
|
||||
{ 0x00, 0x0}
|
||||
};
|
||||
|
||||
int fido_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)) {
|
||||
|
||||
@@ -22,15 +22,92 @@
|
||||
#include "pico/stdlib.h"
|
||||
#include "common.h"
|
||||
#include "mbedtls/ecdsa.h"
|
||||
#include "ctap_hid.h"
|
||||
|
||||
#define U2F_PUBKEY_LEN (65)
|
||||
#define CTAP_PUBKEY_LEN (65)
|
||||
#define KEY_PATH_LEN (32)
|
||||
#define KEY_PATH_ENTRIES (KEY_PATH_LEN / sizeof(uint32_t))
|
||||
#define SHA256_DIGEST_LENGTH (32)
|
||||
#define KEY_HANDLE_LEN (KEY_PATH_LEN + SHA256_DIGEST_LENGTH)
|
||||
|
||||
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 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 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 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 ecdh(uint8_t protocol, const mbedtls_ecp_point *Q, uint8_t *sharedSecret);
|
||||
|
||||
#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
|
||||
#define FIDO2_ALG_ECDH_ES_HKDF_256 -25 //ECDH-ES + HKDF-256
|
||||
|
||||
#define FIDO2_CURVE_P256 1
|
||||
#define FIDO2_CURVE_P384 2
|
||||
#define FIDO2_CURVE_P521 3
|
||||
#define FIDO2_CURVE_X25519 4
|
||||
#define FIDO2_CURVE_X448 5
|
||||
#define FIDO2_CURVE_ED25519 6
|
||||
#define FIDO2_CURVE_ED448 7
|
||||
#define FIDO2_CURVE_P256K1 8
|
||||
|
||||
#define FIDO2_AUT_FLAG_UP 0x1
|
||||
#define FIDO2_AUT_FLAG_UV 0x4
|
||||
#define FIDO2_AUT_FLAG_AT 0x40
|
||||
#define FIDO2_AUT_FLAG_ED 0x80
|
||||
|
||||
#define FIDO2_PERMISSION_MC 0x1
|
||||
#define FIDO2_PERMISSION_GA 0x2
|
||||
#define FIDO2_PERMISSION_CM 0x4
|
||||
#define FIDO2_PERMISSION_BE 0x8
|
||||
#define FIDO2_PERMISSION_LBW 0x10
|
||||
#define FIDO2_PERMISSION_ACFG 0x20
|
||||
|
||||
#define MAX_PIN_RETRIES 8
|
||||
extern bool getUserPresentFlagValue();
|
||||
extern bool getUserVerifiedFlagValue();
|
||||
extern void clearUserPresentFlag();
|
||||
extern void clearUserVerifiedFlag();
|
||||
extern void clearPinUvAuthTokenPermissionsExceptLbw();
|
||||
extern void send_keepalive();
|
||||
extern uint32_t get_sign_counter();
|
||||
#define MAX_CREDENTIAL_COUNT_IN_LIST 16
|
||||
#define MAX_CRED_ID_LENGTH 1024
|
||||
#define MAX_RESIDENT_CREDENTIALS 256
|
||||
|
||||
typedef struct known_app {
|
||||
const uint8_t *rp_id_hash;
|
||||
const char *label;
|
||||
const bool *use_sign_count;
|
||||
const bool *use_self_attestation;
|
||||
} known_app_t;
|
||||
|
||||
extern const known_app_t *find_app_by_rp_id_hash(const uint8_t *rp_id_hash);
|
||||
|
||||
#define TRANSPORT_TIME_LIMIT (30*1000) //USB
|
||||
|
||||
bool check_user_presence();
|
||||
|
||||
typedef struct pinUvAuthToken {
|
||||
uint8_t *data;
|
||||
size_t len;
|
||||
bool in_use;
|
||||
uint8_t permissions;
|
||||
uint8_t rp_id_hash[32];
|
||||
bool user_present;
|
||||
bool user_verified;
|
||||
} pinUvAuthToken_t;
|
||||
|
||||
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);
|
||||
|
||||
#endif //_FIDO_H
|
||||
|
||||
@@ -20,9 +20,11 @@
|
||||
|
||||
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 | 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_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_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_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 = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_UNKNOWN, .data = NULL, .ef_structure = 0, .acl = {0} } //end
|
||||
};
|
||||
|
||||
@@ -31,3 +33,5 @@ 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;
|
||||
file_t *ef_authtoken = NULL;
|
||||
|
||||
@@ -23,9 +23,14 @@
|
||||
#define EF_KEY_DEV 0xCC00
|
||||
#define EF_EE_DEV 0xCE00
|
||||
#define EF_COUNTER 0xC000
|
||||
#define EF_PIN 0x1080
|
||||
#define EF_AUTHTOKEN 0x1090
|
||||
#define EF_CRED 0xCF00 // Creds at 0xCF00 - 0xCFFF
|
||||
|
||||
extern file_t *ef_keydev;
|
||||
extern file_t *ef_certdev;
|
||||
extern file_t *ef_counter;
|
||||
extern file_t *ef_pin;
|
||||
extern file_t *ef_authtoken;
|
||||
|
||||
#endif //_FILES_H_
|
||||
|
||||
311
src/fido/known_apps.c
Normal file
311
src/fido/known_apps.c
Normal file
@@ -0,0 +1,311 @@
|
||||
/*
|
||||
* 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 <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",
|
||||
.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",
|
||||
.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",
|
||||
.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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
.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",
|
||||
.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",
|
||||
.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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
//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",
|
||||
//WebAuthn key for demo.yubico.com
|
||||
.label = "demo.yubico.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = NULL,
|
||||
.label = NULL,
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL
|
||||
}
|
||||
};
|
||||
|
||||
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)
|
||||
return ka;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
107
src/fido/u2f.h
107
src/fido/u2f.h
@@ -1,107 +0,0 @@
|
||||
// Common U2F raw message format header - Review Draft
|
||||
// 2014-10-08
|
||||
// Editor: Jakob Ehrensvard, Yubico, jakob@yubico.com
|
||||
|
||||
#ifndef __U2F_H_INCLUDED__
|
||||
#define __U2F_H_INCLUDED__
|
||||
|
||||
#ifdef _MSC_VER // Windows
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned long int uint64_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// General constants
|
||||
|
||||
#define U2F_EC_KEY_SIZE 32 // EC key size in bytes
|
||||
#define U2F_EC_POINT_SIZE ((U2F_EC_KEY_SIZE * 2) + 1) // Size of EC point
|
||||
#define U2F_MAX_KH_SIZE 128 // Max size of key handle
|
||||
#define U2F_MAX_ATT_CERT_SIZE 2048 // Max size of attestation certificate
|
||||
#define U2F_MAX_EC_SIG_SIZE 72 // Max size of DER coded EC signature
|
||||
#define U2F_CTR_SIZE 4 // Size of counter field
|
||||
#define U2F_APPID_SIZE 32 // Size of application id
|
||||
#define U2F_CHAL_SIZE 32 // Size of challenge
|
||||
|
||||
#define ENC_SIZE(x) ((x + 7) & 0xfff8)
|
||||
|
||||
// EC (uncompressed) point
|
||||
|
||||
#define U2F_POINT_UNCOMPRESSED 0x04 // Uncompressed point format
|
||||
|
||||
typedef struct {
|
||||
uint8_t pointFormat; // Point type
|
||||
uint8_t x[U2F_EC_KEY_SIZE]; // X-value
|
||||
uint8_t y[U2F_EC_KEY_SIZE]; // Y-value
|
||||
} U2F_EC_POINT;
|
||||
|
||||
// U2F native commands
|
||||
|
||||
#define U2F_REGISTER 0x01 // Registration command
|
||||
#define U2F_AUTHENTICATE 0x02 // Authenticate/sign command
|
||||
#define U2F_VERSION 0x03 // Read version string command
|
||||
|
||||
#define U2F_VENDOR_FIRST 0x40 // First vendor defined command
|
||||
#define U2F_VENDOR_LAST 0xbf // Last vendor defined command
|
||||
|
||||
// U2F_CMD_REGISTER command defines
|
||||
|
||||
#define U2F_REGISTER_ID 0x05 // Version 2 registration identifier
|
||||
#define U2F_REGISTER_HASH_ID 0x00 // Version 2 hash identintifier
|
||||
|
||||
typedef struct {
|
||||
uint8_t chal[U2F_CHAL_SIZE]; // Challenge
|
||||
uint8_t appId[U2F_APPID_SIZE]; // Application id
|
||||
} U2F_REGISTER_REQ;
|
||||
|
||||
typedef struct {
|
||||
uint8_t registerId; // Registration identifier (U2F_REGISTER_ID_V2)
|
||||
U2F_EC_POINT pubKey; // Generated public key
|
||||
uint8_t keyHandleLen; // Length of key handle
|
||||
uint8_t keyHandleCertSig[
|
||||
U2F_MAX_KH_SIZE + // Key handle
|
||||
U2F_MAX_ATT_CERT_SIZE + // Attestation certificate
|
||||
U2F_MAX_EC_SIG_SIZE]; // Registration signature
|
||||
} U2F_REGISTER_RESP;
|
||||
|
||||
// U2F_CMD_AUTHENTICATE command defines
|
||||
|
||||
// Authentication control byte
|
||||
|
||||
#define U2F_AUTH_ENFORCE 0x03 // Enforce user presence and sign
|
||||
#define U2F_AUTH_CHECK_ONLY 0x07 // Check only
|
||||
#define U2F_AUTH_FLAG_TUP 0x01 // Test of user presence set
|
||||
|
||||
typedef struct {
|
||||
uint8_t chal[U2F_CHAL_SIZE]; // Challenge
|
||||
uint8_t appId[U2F_APPID_SIZE]; // Application id
|
||||
uint8_t keyHandleLen; // Length of key handle
|
||||
uint8_t keyHandle[U2F_MAX_KH_SIZE]; // Key handle
|
||||
} U2F_AUTHENTICATE_REQ;
|
||||
|
||||
typedef struct {
|
||||
uint8_t flags; // U2F_AUTH_FLAG_ values
|
||||
uint8_t ctr[U2F_CTR_SIZE]; // Counter field (big-endian)
|
||||
uint8_t sig[U2F_MAX_EC_SIG_SIZE]; // Signature
|
||||
} U2F_AUTHENTICATE_RESP;
|
||||
|
||||
// Command status responses
|
||||
|
||||
#define U2F_SW_NO_ERROR 0x9000 // SW_NO_ERROR
|
||||
#define U2F_SW_WRONG_DATA 0x6A80 // SW_WRONG_DATA
|
||||
#define U2F_SW_CONDITIONS_NOT_SATISFIED 0x6985 // SW_CONDITIONS_NOT_SATISFIED
|
||||
#define U2F_SW_COMMAND_NOT_ALLOWED 0x6986 // SW_COMMAND_NOT_ALLOWED
|
||||
#define U2F_SW_INS_NOT_SUPPORTED 0x6D00 // SW_INS_NOT_SUPPORTED
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __U2F_H_INCLUDED__
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#ifndef __VERSION_H_
|
||||
#define __VERSION_H_
|
||||
|
||||
#define PICO_FIDO_VERSION 0x0102
|
||||
#define PICO_FIDO_VERSION 0x0200
|
||||
|
||||
#define PICO_FIDO_VERSION_MAJOR ((PICO_FIDO_VERSION >> 8) & 0xff)
|
||||
#define PICO_FIDO_VERSION_MINOR (PICO_FIDO_VERSION & 0xff)
|
||||
|
||||
13
workflows/autobuild.sh
Executable file
13
workflows/autobuild.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
git submodule update --init --recursive
|
||||
sudo apt update
|
||||
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 ..
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DPICO_SDK_PATH=../pico-sdk ..
|
||||
make
|
||||
Reference in New Issue
Block a user