FIDO2 support in RIOT
This commit is contained in:
parent
8ca1520342
commit
e127a4d865
@ -37,6 +37,7 @@ PSEUDOMODULES += evtimer_mbox
|
||||
PSEUDOMODULES += evtimer_on_ztimer
|
||||
PSEUDOMODULES += fmt_%
|
||||
PSEUDOMODULES += gcoap_dtls
|
||||
PSEUDOMODULES += fido2_tests
|
||||
PSEUDOMODULES += gnrc_dhcpv6_%
|
||||
PSEUDOMODULES += gnrc_ipv6_default
|
||||
PSEUDOMODULES += gnrc_ipv6_ext_frag_stats
|
||||
|
||||
9
pkg/fido2_tests/Makefile
Normal file
9
pkg/fido2_tests/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
PKG_NAME=fido2_tests
|
||||
PKG_URL=https://github.com/solokeys/fido2-tests
|
||||
PKG_VERSION=3f7893d8d1a39b009cddad7913d3808ca664d3b7
|
||||
PKG_LICENSE=Apache-2.0 OR MIT
|
||||
|
||||
include $(RIOTBASE)/pkg/pkg.mk
|
||||
|
||||
all:
|
||||
@
|
||||
226
pkg/fido2_tests/patches/0001-Adaptions-for-RIOT-FIDO2-CTAP.patch
Normal file
226
pkg/fido2_tests/patches/0001-Adaptions-for-RIOT-FIDO2-CTAP.patch
Normal file
@ -0,0 +1,226 @@
|
||||
From 445c1fe93f6d0edbd1c59f318703b070c8ee445f Mon Sep 17 00:00:00 2001
|
||||
From: Ollrogge <nils-ollrogge@outlook.de>
|
||||
Date: Tue, 7 Sep 2021 19:12:31 +0200
|
||||
Subject: [PATCH] Adaptions for RIOT FIDO2 CTAP
|
||||
|
||||
---
|
||||
Makefile | 15 ++++++------
|
||||
tests/conftest.py | 2 +-
|
||||
tests/standard/fido2/pin/test_pin.py | 24 ++++++++++++++++---
|
||||
tests/standard/fido2/test_reset.py | 5 ++++
|
||||
tests/standard/fido2/test_resident_key.py | 4 ++--
|
||||
.../fido2/user_presence/test_user_presence.py | 10 +++++++-
|
||||
tests/standard/transport/test_hid.py | 11 +++++++++
|
||||
7 files changed, 57 insertions(+), 14 deletions(-)
|
||||
|
||||
diff --git a/Makefile b/Makefile
|
||||
index 85aa451..c101826 100644
|
||||
--- a/Makefile
|
||||
+++ b/Makefile
|
||||
@@ -1,4 +1,4 @@
|
||||
-.PHONY: standard-tests vendor-tests
|
||||
+.PHONY: standard-tests
|
||||
|
||||
PY_VERSION=$(shell python -c "import sys; print('%d.%d'% sys.version_info[0:2])")
|
||||
VALID=$(shell python -c "print($(PY_VERSION) >= 3.6)")
|
||||
@@ -16,24 +16,21 @@ else
|
||||
endif
|
||||
|
||||
standard-tests: venv
|
||||
- $(BIN)/pytest tests/standard
|
||||
+ $(BIN)/pytest --ignore=tests/standard/fido2v1 --ignore=tests/standard/fido2/extensions --ignore=tests/standard/u2f --ignore tests/standard/fido2/user_presence -k "not test_ctap1_interop and not test_rk_maximum_list_capacity_per_rp_nodisplay and not test_keep_alive" tests/standard/ -s
|
||||
|
||||
-vendor-tests: venv
|
||||
- $(BIN)/pytest tests/vendor
|
||||
+up-tests: venv
|
||||
+ $(BIN)/pytest tests/standard/fido2/user_presence -s
|
||||
|
||||
# setup development environment
|
||||
venv:
|
||||
$(PYTHON) -m venv venv
|
||||
$(BIN)/python -m pip install -U pip
|
||||
$(BIN)/pip install -U -r requirements.txt
|
||||
- $(BIN)/pip install -U -r dev-requirements.txt
|
||||
- $(BIN)/pre-commit install
|
||||
|
||||
# re-run if dependencies change
|
||||
update:
|
||||
$(BIN)/python -m pip install -U pip
|
||||
$(BIN)/pip install -U -r requirements.txt
|
||||
- $(BIN)/pip install -U -r dev-requirements.txt
|
||||
|
||||
# ensure this passes before commiting
|
||||
check:
|
||||
@@ -48,3 +45,7 @@ black:
|
||||
|
||||
isort:
|
||||
$(BIN)/isort -y --recursive tests/
|
||||
+
|
||||
+clean:
|
||||
+ rm -r venv
|
||||
+
|
||||
diff --git a/tests/conftest.py b/tests/conftest.py
|
||||
index 761a684..d13d6dc 100644
|
||||
--- a/tests/conftest.py
|
||||
+++ b/tests/conftest.py
|
||||
@@ -175,7 +175,7 @@ class MoreRobustPcscDevice(CtapPcscDevice):
|
||||
except CtapError:
|
||||
if self._capabilities == 0:
|
||||
raise ValueError("Unsupported device")
|
||||
-
|
||||
+
|
||||
def apdu_exchange(self, apdu, protocol = None):
|
||||
try:
|
||||
return super().apdu_exchange(apdu,protocol)
|
||||
diff --git a/tests/standard/fido2/pin/test_pin.py b/tests/standard/fido2/pin/test_pin.py
|
||||
index 78b09e3..f5ee4e4 100644
|
||||
--- a/tests/standard/fido2/pin/test_pin.py
|
||||
+++ b/tests/standard/fido2/pin/test_pin.py
|
||||
@@ -60,7 +60,11 @@ class TestPin(object):
|
||||
with pytest.raises(CtapError) as e:
|
||||
device.client.pin_protocol.set_pin('1234')
|
||||
|
||||
- assert e.value.code == CtapError.ERR.NOT_ALLOWED
|
||||
+ '''
|
||||
+ CTAP spec states: "If a PIN has already been set, authenticator
|
||||
+ returns CTAP2_ERR_PIN_AUTH_INVALID error."
|
||||
+ '''
|
||||
+ assert e.value.code == CtapError.ERR.PIN_AUTH_INVALID
|
||||
|
||||
|
||||
def test_get_key_agreement_fields(self, CPRes):
|
||||
@@ -99,11 +103,25 @@ class TestPin(object):
|
||||
def test_zero_length_pin_auth(self, device, SetPinRes):
|
||||
with pytest.raises(CtapError) as e:
|
||||
reg = device.sendMC(*FidoRequest(SetPinRes, pin_auth=b"").toMC())
|
||||
- assert e.value.code == CtapError.ERR.PIN_AUTH_INVALID
|
||||
+
|
||||
+ '''
|
||||
+ CTAP spec states: If platform sends zero length pinAuth, authenticator
|
||||
+ needs to wait for user touch and then returns either
|
||||
+ CTAP2_ERR_PIN_NOT_SET if pin is not set or CTAP2_ERR_PIN_INVALID
|
||||
+ if pin has been set. [...]"
|
||||
+ '''
|
||||
+ assert e.value.code == CtapError.ERR.PIN_INVALID
|
||||
|
||||
with pytest.raises(CtapError) as e:
|
||||
reg = device.sendGA(*FidoRequest(SetPinRes, pin_auth=b"").toGA())
|
||||
- assert e.value.code == CtapError.ERR.PIN_AUTH_INVALID
|
||||
+
|
||||
+ '''
|
||||
+ CTAP spec states: If platform sends zero length pinAuth, authenticator
|
||||
+ needs to wait for user touch and then returns either
|
||||
+ CTAP2_ERR_PIN_NOT_SET if pin is not set or CTAP2_ERR_PIN_INVALID
|
||||
+ if pin has been set.
|
||||
+ '''
|
||||
+ assert e.value.code == CtapError.ERR.PIN_INVALID
|
||||
|
||||
def test_make_credential_no_pin(self, device, SetPinRes):
|
||||
with pytest.raises(CtapError) as e:
|
||||
diff --git a/tests/standard/fido2/test_reset.py b/tests/standard/fido2/test_reset.py
|
||||
index 508d755..adb2818 100644
|
||||
--- a/tests/standard/fido2/test_reset.py
|
||||
+++ b/tests/standard/fido2/test_reset.py
|
||||
@@ -9,9 +9,14 @@ import tests
|
||||
def test_reset(device):
|
||||
device.reset()
|
||||
|
||||
+'''
|
||||
+Not mentioned in any spec.
|
||||
+'''
|
||||
+'''
|
||||
def test_reset_window(device):
|
||||
print("Waiting 11s before sending reset...")
|
||||
time.sleep(11)
|
||||
with pytest.raises(CtapError) as e:
|
||||
device.ctap2.reset(on_keepalive=DeviceSelectCredential(1))
|
||||
assert e.value.code == CtapError.ERR.NOT_ALLOWED
|
||||
+'''
|
||||
\ No newline at end of file
|
||||
diff --git a/tests/standard/fido2/test_resident_key.py b/tests/standard/fido2/test_resident_key.py
|
||||
index 2c5bece..32fe534 100644
|
||||
--- a/tests/standard/fido2/test_resident_key.py
|
||||
+++ b/tests/standard/fido2/test_resident_key.py
|
||||
@@ -45,7 +45,7 @@ class TestResidentKeyPersistance(object):
|
||||
@pytest.mark.parametrize("do_reboot", [False, True])
|
||||
def test_user_info_returned_when_using_allowlist(self, device, MC_RK_Res, GA_RK_Res, do_reboot):
|
||||
assert "id" in GA_RK_Res.user.keys()
|
||||
-
|
||||
+
|
||||
allow_list = [
|
||||
{
|
||||
"id": MC_RK_Res.auth_data.credential_data.credential_id[:],
|
||||
@@ -66,7 +66,7 @@ class TestResidentKeyPersistance(object):
|
||||
class TestResidentKeyAfterReset(object):
|
||||
def test_with_allow_list_after_reset(self, device, MC_RK_Res, GA_RK_Res):
|
||||
assert "id" in GA_RK_Res.user.keys()
|
||||
-
|
||||
+
|
||||
allow_list = [
|
||||
{
|
||||
"id": MC_RK_Res.auth_data.credential_data.credential_id[:],
|
||||
diff --git a/tests/standard/fido2/user_presence/test_user_presence.py b/tests/standard/fido2/user_presence/test_user_presence.py
|
||||
index c9904b2..0b74d24 100644
|
||||
--- a/tests/standard/fido2/user_presence/test_user_presence.py
|
||||
+++ b/tests/standard/fido2/user_presence/test_user_presence.py
|
||||
@@ -34,7 +34,10 @@ class TestUserPresence(object):
|
||||
device.sendGA(
|
||||
*FidoRequest(GARes, timeout=event, on_keepalive=None).toGA()
|
||||
)
|
||||
- assert e.value.code == CtapError.ERR.KEEPALIVE_CANCEL
|
||||
+ '''
|
||||
+ The CTAP states that if no UP has been activated, CTAP2_ERR_OPERATION_DENIED should be returned.
|
||||
+ '''
|
||||
+ assert e.value.code == CtapError.ERR.OPERATION_DENIED
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not "trezor" in sys.argv, reason="Only Trezor supports decline."
|
||||
@@ -71,6 +74,10 @@ class TestUserPresence(object):
|
||||
)
|
||||
assert e.value.code == CtapError.ERR.INVALID_OPTION
|
||||
|
||||
+ '''
|
||||
+ This test makes no sense since device.sendGA is blocking
|
||||
+ '''
|
||||
+ '''
|
||||
def test_user_presence_permits_only_one_request(self, device, MCRes, GARes):
|
||||
print("ACTIVATE UP ONCE")
|
||||
device.sendGA(*FidoRequest(GARes).toGA())
|
||||
@@ -81,3 +88,4 @@ class TestUserPresence(object):
|
||||
*FidoRequest(GARes, timeout=event, on_keepalive=None).toGA()
|
||||
)
|
||||
assert e.value.code == CtapError.ERR.KEEPALIVE_CANCEL
|
||||
+ '''
|
||||
\ No newline at end of file
|
||||
diff --git a/tests/standard/transport/test_hid.py b/tests/standard/transport/test_hid.py
|
||||
index c79c933..6203a00 100644
|
||||
--- a/tests/standard/transport/test_hid.py
|
||||
+++ b/tests/standard/transport/test_hid.py
|
||||
@@ -105,6 +105,16 @@ class TestHID(object):
|
||||
device.send_raw("\x01")
|
||||
device.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
|
||||
+ '''
|
||||
+ CTAP spec states: If an application tries to access the device from a
|
||||
+ different channel while the device is busy with a transaction, that request
|
||||
+ will immediately fail with a busy-error message sent to the requesting channel.
|
||||
+
|
||||
+ This test tries to send an init from a different cid while the authenticator
|
||||
+ is busy with the ping. In my understanding, based on the sentence above,
|
||||
+ this should throw an error. Therefore the test does not make sense.
|
||||
+ '''
|
||||
+ '''
|
||||
def test_ping_abort_from_different_cid(self, device, check_timeouts=False):
|
||||
oldcid = device.cid()
|
||||
newcid = "\x11\x22\x33\x44"
|
||||
@@ -123,6 +133,7 @@ class TestHID(object):
|
||||
# print('wait for timeout')
|
||||
cmd, r = device.recv_raw() # timeout response
|
||||
assert cmd == 0xBF
|
||||
+ '''
|
||||
|
||||
def test_timeout(self, device):
|
||||
device.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
--
|
||||
2.33.0
|
||||
|
||||
@ -24,6 +24,7 @@ rsource "embunit/Kconfig"
|
||||
rsource "entropy_source/Kconfig"
|
||||
rsource "eepreg/Kconfig"
|
||||
rsource "event/Kconfig"
|
||||
rsource "fido2/Kconfig"
|
||||
rsource "fmt/Kconfig"
|
||||
rsource "frac/Kconfig"
|
||||
rsource "hashes/Kconfig"
|
||||
|
||||
@ -779,4 +779,35 @@ ifneq (,$(filter dbgpin,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += dbgpin
|
||||
endif
|
||||
|
||||
ifneq (,$(filter fido2_ctap_%,$(USEMODULE)))
|
||||
USEMODULE += fido2_ctap_transport
|
||||
USEMODULE += fido2_ctap
|
||||
ifneq (,$(filter fido2_ctap_transport_hid,$(USEMODULE)))
|
||||
USEMODULE += usbus_hid
|
||||
DISABLE_MODULE += auto_init_usbus
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq (,$(filter fido2_ctap,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_flashpage
|
||||
FEATURES_REQUIRED += periph_gpio_irq
|
||||
|
||||
USEPKG += tiny-asn1
|
||||
USEPKG += tinycbor
|
||||
USEPKG += micro-ecc
|
||||
|
||||
INCLUDE += $(RIOTPKG)/tinycbor
|
||||
|
||||
USEMODULE += mtd_flashpage
|
||||
USEMODULE += mtd_write_page
|
||||
USEMODULE += ztimer_msec
|
||||
USEMODULE += event
|
||||
USEMODULE += event_timeout
|
||||
USEMODULE += prng_sha256prng
|
||||
USEMODULE += cipher_modes
|
||||
USEMODULE += crypto_aes_256
|
||||
USEMODULE += hashes
|
||||
USEMODULE += fido2
|
||||
endif
|
||||
|
||||
include $(RIOTBASE)/sys/test_utils/Makefile.dep
|
||||
|
||||
7
sys/fido2/Kconfig
Normal file
7
sys/fido2/Kconfig
Normal file
@ -0,0 +1,7 @@
|
||||
# Copyright (C) 2021 Freie Universität Berlin
|
||||
#
|
||||
# This file is subject to the terms and conditions of the GNU Lesser
|
||||
# General Public License v2.1. See the file LICENSE in the top level
|
||||
# directory for more details.
|
||||
|
||||
rsource "ctap/Kconfig"
|
||||
5
sys/fido2/Makefile
Normal file
5
sys/fido2/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
ifneq (,$(filter fido2_ctap%,$(USEMODULE)))
|
||||
DIRS += ctap
|
||||
endif
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
||||
96
sys/fido2/ctap/Kconfig
Normal file
96
sys/fido2/ctap/Kconfig
Normal file
@ -0,0 +1,96 @@
|
||||
# Copyright (C) 2021 Freie Universität Berlin
|
||||
#
|
||||
# This file is subject to the terms and conditions of the GNU Lesser
|
||||
# General Public License v2.1. See the file LICENSE in the top level
|
||||
# directory for more details.
|
||||
|
||||
menuconfig KCONFIG_USEMODULE_FIDO2_CTAP
|
||||
bool "FIDO2 CTAP"
|
||||
depends on USEMODULE_FIDO2_CTAP
|
||||
help
|
||||
Configure a FIDO2 CTAP authenticator via KConfig.
|
||||
|
||||
if KCONFIG_USEMODULE_FIDO2_CTAP
|
||||
|
||||
config FIDO2_CTAP_STACK_SIZE
|
||||
int "CTAP thread stack size"
|
||||
default 15000
|
||||
|
||||
config FIDO2_CTAP_DEVICE_AAGUID
|
||||
string "AAGUID of the CTAP authenticator"
|
||||
default "9c295865fa2c36b705a42320af9c8f16"
|
||||
help
|
||||
The AAGUID is identifying the type of the authenticator (e.g manufacturer
|
||||
and model). The AAGUID needs to be 128 bits long. The default value here
|
||||
is a fallback value that was randomly generated.
|
||||
|
||||
config FIDO2_CTAP_DISABLE_UP
|
||||
bool "Disable user presence tests"
|
||||
help
|
||||
When set, the authenticator will not ask for permission before creating
|
||||
a new credential pair or authenticating.
|
||||
|
||||
config FIDO2_CTAP_DISABLE_LED
|
||||
bool "Disable LED animations"
|
||||
help
|
||||
When set, the authenticator will not use LED's.
|
||||
|
||||
config FIDO2_CTAP_UP_TIMEOUT
|
||||
int "Seconds until user presence test times out"
|
||||
default 15
|
||||
|
||||
config FIDO2_CTAP_UP_BUTTON_PORT
|
||||
int "Port of user presence button"
|
||||
depends on !FIDO2_CTAP_DISABLE_UP
|
||||
default -1
|
||||
|
||||
config FIDO2_CTAP_UP_BUTTON_PIN
|
||||
int "Pin of user presence button"
|
||||
depends on !FIDO2_CTAP_DISABLE_UP
|
||||
default -1
|
||||
|
||||
choice
|
||||
bool "User presence button mode"
|
||||
depends on !FIDO2_CTAP_DISABLE_UP
|
||||
default FIDO2_CTAP_UP_BUTTON_MODE_IN_PU
|
||||
|
||||
config FIDO2_CTAP_UP_BUTTON_MODE_IN_PU
|
||||
bool "GPIO_IN_PU"
|
||||
help
|
||||
Configure as input with pull-up resistor
|
||||
|
||||
config FIDO2_CTAP_UP_BUTTON_MODE_IN_PD
|
||||
bool "GPIO_IN_PD"
|
||||
help
|
||||
Configure as input with pull-down resistor
|
||||
|
||||
config FIDO2_CTAP_UP_BUTTON_MODE_IN
|
||||
bool "GPIO_IN"
|
||||
help
|
||||
Configure as input without pull resistor
|
||||
|
||||
endchoice
|
||||
|
||||
choice
|
||||
bool "User presence button pin flank"
|
||||
depends on !FIDO2_CTAP_DISABLE_UP
|
||||
default FIDO2_CTAP_UP_BUTTON_FLANK_FALLING
|
||||
|
||||
config FIDO2_CTAP_UP_BUTTON_FLANK_FALLING
|
||||
bool "GPIO_FALLING"
|
||||
|
||||
config FIDO2_CTAP_UP_BUTTON_FLANK_RISING
|
||||
bool "GPIO_RISING"
|
||||
|
||||
endchoice
|
||||
|
||||
config FIDO2_CTAP_FLASH_START_PAGE
|
||||
int "First flash page to store data in"
|
||||
default -1
|
||||
help
|
||||
Configuring this incorrectly can lead to firmware corruption so make sure
|
||||
the flash page is located after the firmware.
|
||||
|
||||
rsource "transport/Kconfig"
|
||||
|
||||
endif # KCONFIG_USEMODULE_FIDO2_CTAP
|
||||
7
sys/fido2/ctap/Makefile
Normal file
7
sys/fido2/ctap/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
MODULE := fido2_ctap
|
||||
|
||||
ifneq (,$(filter fido2_ctap_%,$(USEMODULE)))
|
||||
DIRS += transport
|
||||
endif
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
||||
1850
sys/fido2/ctap/ctap.c
Normal file
1850
sys/fido2/ctap/ctap.c
Normal file
File diff suppressed because it is too large
Load Diff
1740
sys/fido2/ctap/ctap_cbor.c
Normal file
1740
sys/fido2/ctap/ctap_cbor.c
Normal file
File diff suppressed because it is too large
Load Diff
343
sys/fido2/ctap/ctap_crypto.c
Normal file
343
sys/fido2/ctap/ctap_crypto.c
Normal file
@ -0,0 +1,343 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup fido2_ctap_crypto
|
||||
* @{
|
||||
* @file
|
||||
*
|
||||
* @author Nils Ollrogge <nils.ollrogge@fu-berlin.de>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "assert.h"
|
||||
#include "random.h"
|
||||
|
||||
#include "crypto/ciphers.h"
|
||||
#include "crypto/modes/ccm.h"
|
||||
#include "crypto/modes/cbc.h"
|
||||
|
||||
#include "uECC.h"
|
||||
#include "tiny-asn1.h"
|
||||
|
||||
#include "fido2/ctap/ctap_crypto.h"
|
||||
#include "fido2/ctap.h"
|
||||
#include "fido2/ctap/ctap_utils.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
/**
|
||||
* @brief Parse signature into ASN.1 DER format
|
||||
*/
|
||||
static int _sig_to_der_format(uint8_t *r, uint8_t *s, uint8_t *sig,
|
||||
size_t *sig_len);
|
||||
|
||||
/**
|
||||
* @brief Random number generator
|
||||
*
|
||||
* wrapper for @ref fido2_ctap_crypto_prng
|
||||
*/
|
||||
static int _RNG(uint8_t *dest, unsigned size);
|
||||
|
||||
int fido2_ctap_crypto_init(void)
|
||||
{
|
||||
uECC_set_rng(&_RNG);
|
||||
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
static int _RNG(uint8_t *dest, unsigned size)
|
||||
{
|
||||
fido2_ctap_crypto_prng(dest, (size_t)size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int fido2_ctap_crypto_prng(uint8_t *buf, size_t len)
|
||||
{
|
||||
random_bytes(buf, len);
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
int fido2_ctap_crypto_sha256_init(sha256_context_t *ctx)
|
||||
{
|
||||
sha256_init(ctx);
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
int fido2_ctap_crypto_sha256_update(sha256_context_t *ctx, const void *data, size_t len)
|
||||
{
|
||||
sha256_update(ctx, data, len);
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
int fido2_ctap_crypto_sha256_final(sha256_context_t *ctx, void *digest)
|
||||
{
|
||||
sha256_final(ctx, digest);
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
int fido2_ctap_crypto_sha256(const void *data, size_t len,
|
||||
void *digest)
|
||||
{
|
||||
sha256(data, len, digest);
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
int fido2_ctap_crypto_hmac_sha256_init(hmac_context_t *ctx, const void *key,
|
||||
size_t key_length)
|
||||
{
|
||||
hmac_sha256_init(ctx, key, key_length);
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
int fido2_ctap_crypto_hmac_sha256_update(hmac_context_t *ctx, const void *data, size_t len)
|
||||
{
|
||||
hmac_sha256_update(ctx, data, len);
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
int fido2_ctap_crypto_hmac_sha256_final(hmac_context_t *ctx, void *digest)
|
||||
{
|
||||
hmac_sha256_final(ctx, digest);
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
int fido2_ctap_crypto_hmac_sha256(const void *key,
|
||||
size_t key_length, const void *data, size_t len,
|
||||
void *digest)
|
||||
{
|
||||
hmac_sha256(key, key_length, data, len, digest);
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
int fido2_ctap_crypto_ecdh(uint8_t *out, size_t len,
|
||||
ctap_crypto_pub_key_t *pub_key, uint8_t *priv_key, size_t key_len)
|
||||
{
|
||||
assert(len == CTAP_CRYPTO_KEY_SIZE);
|
||||
assert(key_len == CTAP_CRYPTO_KEY_SIZE);
|
||||
|
||||
int ret;
|
||||
const struct uECC_Curve_t *curve = uECC_secp256r1();
|
||||
|
||||
ret = uECC_shared_secret((uint8_t *)pub_key, priv_key, out, curve);
|
||||
|
||||
if (ret == 0) {
|
||||
return CTAP1_ERR_OTHER;
|
||||
}
|
||||
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
int fido2_ctap_crypto_aes_enc(uint8_t *out, size_t *out_len, uint8_t *in,
|
||||
size_t in_len, const uint8_t *key,
|
||||
size_t key_len)
|
||||
{
|
||||
assert(*out_len >= in_len);
|
||||
int ret;
|
||||
cipher_t cipher;
|
||||
uint8_t iv[16] = { 0 };
|
||||
|
||||
ret = cipher_init(&cipher, CIPHER_AES, key, key_len);
|
||||
|
||||
if (ret < 0) {
|
||||
return CTAP1_ERR_OTHER;
|
||||
}
|
||||
|
||||
ret = cipher_encrypt_cbc(&cipher, iv, in, in_len, out);
|
||||
|
||||
if (ret < 0) {
|
||||
return CTAP1_ERR_OTHER;
|
||||
}
|
||||
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
int fido2_ctap_crypto_aes_dec(uint8_t *out, size_t *out_len, uint8_t *in,
|
||||
size_t in_len, const uint8_t *key,
|
||||
size_t key_len)
|
||||
{
|
||||
assert(*out_len >= in_len);
|
||||
int ret;
|
||||
cipher_t cipher;
|
||||
uint8_t iv[16] = { 0 };
|
||||
|
||||
ret = cipher_init(&cipher, CIPHER_AES, key, key_len);
|
||||
|
||||
if (ret < 0) {
|
||||
return CTAP1_ERR_OTHER;
|
||||
}
|
||||
|
||||
ret = cipher_decrypt_cbc(&cipher, iv, in, in_len, out);
|
||||
|
||||
if (ret < 0) {
|
||||
return CTAP1_ERR_OTHER;
|
||||
}
|
||||
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
int fido2_ctap_crypto_aes_ccm_enc(uint8_t *out, size_t out_len,
|
||||
const uint8_t *in, size_t in_len,
|
||||
uint8_t *auth_data, size_t auth_data_len,
|
||||
uint8_t mac_len, uint8_t length_encoding,
|
||||
const uint8_t *nonce, size_t nonce_len,
|
||||
const uint8_t *key, size_t key_len)
|
||||
{
|
||||
assert(key_len == CTAP_CRED_KEY_LEN);
|
||||
|
||||
cipher_t cipher;
|
||||
int ret;
|
||||
|
||||
ret = cipher_init(&cipher, CIPHER_AES_128, key, key_len);
|
||||
|
||||
if (ret != 1) {
|
||||
return CTAP1_ERR_OTHER;
|
||||
}
|
||||
|
||||
ret = cipher_encrypt_ccm(&cipher, auth_data, auth_data_len, mac_len,
|
||||
length_encoding, nonce, nonce_len,
|
||||
in, in_len, out);
|
||||
|
||||
if (ret < 0) {
|
||||
return CTAP1_ERR_OTHER;
|
||||
}
|
||||
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
int fido2_ctap_crypto_aes_ccm_dec(uint8_t *out, size_t out_len,
|
||||
const uint8_t *in, size_t in_len,
|
||||
uint8_t *auth_data, size_t auth_data_len,
|
||||
uint8_t mac_len, uint8_t length_encoding,
|
||||
const uint8_t *nonce, size_t nonce_len,
|
||||
const uint8_t *key, size_t key_len)
|
||||
{
|
||||
assert(key_len == CTAP_CRED_KEY_LEN);
|
||||
|
||||
cipher_t cipher;
|
||||
int ret, len;
|
||||
|
||||
ret = cipher_init(&cipher, CIPHER_AES, key, key_len);
|
||||
|
||||
if (ret != 1) {
|
||||
return CTAP1_ERR_OTHER;
|
||||
}
|
||||
|
||||
len = cipher_decrypt_ccm(&cipher, auth_data, auth_data_len,
|
||||
mac_len, length_encoding, nonce, nonce_len,
|
||||
in, in_len, out);
|
||||
|
||||
if (len < 0) {
|
||||
return CTAP1_ERR_OTHER;
|
||||
}
|
||||
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
int fido2_ctap_crypto_gen_keypair(ctap_crypto_pub_key_t *pub_key,
|
||||
uint8_t *priv_key, size_t len)
|
||||
{
|
||||
assert(len == CTAP_CRYPTO_KEY_SIZE);
|
||||
|
||||
int ret;
|
||||
const struct uECC_Curve_t *curve = uECC_secp256r1();
|
||||
|
||||
ret = uECC_make_key((uint8_t *)pub_key, priv_key, curve);
|
||||
if (ret == 0) {
|
||||
return CTAP1_ERR_OTHER;
|
||||
}
|
||||
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
int fido2_ctap_crypto_get_sig(uint8_t *hash, size_t hash_len, uint8_t *sig,
|
||||
size_t *sig_len, const uint8_t *key,
|
||||
size_t key_len)
|
||||
{
|
||||
assert(*sig_len >= CTAP_CRYPTO_ES256_DER_MAX_SIZE);
|
||||
assert(key_len == CTAP_CRYPTO_KEY_SIZE);
|
||||
|
||||
/**
|
||||
* +1 to pad with leading zero to prevent integer from being interpreted as
|
||||
* negative (e.g. MSB of r >= 0x80)
|
||||
*/
|
||||
uint8_t r[CTAP_CRYPTO_KEY_SIZE + 1] = { 0 };
|
||||
uint8_t s[CTAP_CRYPTO_KEY_SIZE + 1] = { 0 };
|
||||
int ret;
|
||||
|
||||
const struct uECC_Curve_t *curve = uECC_secp256r1();
|
||||
|
||||
ret = uECC_sign(key, hash, hash_len, sig, curve);
|
||||
|
||||
if (ret == 0) {
|
||||
return CTAP1_ERR_OTHER;
|
||||
}
|
||||
|
||||
memcpy(r + 1, sig, CTAP_CRYPTO_KEY_SIZE);
|
||||
memcpy(s + 1, sig + CTAP_CRYPTO_KEY_SIZE, CTAP_CRYPTO_KEY_SIZE);
|
||||
|
||||
ret = _sig_to_der_format(r, s, sig, sig_len);
|
||||
|
||||
if (ret != CTAP2_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
static int _sig_to_der_format(uint8_t *r, uint8_t *s, uint8_t *sig,
|
||||
size_t *sig_len)
|
||||
{
|
||||
asn1_tree t;
|
||||
asn1_tree c1;
|
||||
asn1_tree c2;
|
||||
uint8_t pad_s, pad_r;
|
||||
int ret;
|
||||
|
||||
/**
|
||||
* if MSB >= 0x80, pad with leading zero byte in order to have number
|
||||
* interpreted as positive.
|
||||
*/
|
||||
pad_r = ((r[1] & 0x80) == 0x80);
|
||||
pad_s = ((s[1] & 0x80) == 0x80);
|
||||
|
||||
memset(sig, 0, *sig_len);
|
||||
|
||||
list_init(&t);
|
||||
list_init(&c1);
|
||||
list_init(&c2);
|
||||
|
||||
t.type = ASN1_TYPE_SEQUENCE;
|
||||
|
||||
c1.type = ASN1_TYPE_INTEGER;
|
||||
c1.length = 0x20 + pad_r;
|
||||
c1.data = pad_r ? r : r + 1;
|
||||
|
||||
ret = add_child(&t, &c1);
|
||||
|
||||
if (ret < 0) {
|
||||
return CTAP1_ERR_OTHER;
|
||||
}
|
||||
|
||||
c2.type = ASN1_TYPE_INTEGER;
|
||||
c2.length = 0x20 + pad_s;
|
||||
c2.data = pad_s ? s : s + 1;
|
||||
|
||||
ret = add_child(&t, &c2);
|
||||
|
||||
if (ret < 0) {
|
||||
return CTAP1_ERR_OTHER;
|
||||
}
|
||||
|
||||
*sig_len = der_encode(&t, sig, *sig_len);
|
||||
|
||||
return CTAP2_OK;
|
||||
}
|
||||
156
sys/fido2/ctap/ctap_mem.c
Normal file
156
sys/fido2/ctap/ctap_mem.c
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup fido2_ctap_mem
|
||||
* @{
|
||||
* @file
|
||||
*
|
||||
* @author Nils Ollrogge <nils.ollrogge@fu-berlin.de>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "mtd.h"
|
||||
#include "mtd_flashpage.h"
|
||||
|
||||
#include "fido2/ctap/ctap_mem.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
/**
|
||||
* @brief MTD device descriptor initialized with flash-page driver
|
||||
*/
|
||||
static mtd_dev_t _mtd_dev = MTD_FLASHPAGE_INIT_VAL(CTAP_FLASH_PAGES_PER_SECTOR);
|
||||
|
||||
/**
|
||||
* @brief Max amount of resident keys that can be stored
|
||||
*/
|
||||
static uint16_t _max_rk_amnt;
|
||||
|
||||
/**
|
||||
* @brief Check if flash region is erased
|
||||
*/
|
||||
static bool _flash_is_erased(int page, int offset, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Get amount of flashpages
|
||||
*/
|
||||
static unsigned _amount_of_flashpages(void);
|
||||
|
||||
int fido2_ctap_mem_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = mtd_init(&_mtd_dev);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (unsigned i = CTAP_FLASH_RK_START_PAGE; i < _amount_of_flashpages(); i++) {
|
||||
_max_rk_amnt += flashpage_size(i) / CTAP_FLASH_RK_SZ;
|
||||
}
|
||||
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
static unsigned _amount_of_flashpages(void)
|
||||
{
|
||||
return _mtd_dev.sector_count * _mtd_dev.pages_per_sector;
|
||||
}
|
||||
|
||||
int fido2_ctap_mem_read(void *buf, uint32_t page, uint32_t offset, uint32_t len)
|
||||
{
|
||||
assert(buf);
|
||||
|
||||
int ret;
|
||||
|
||||
ret = mtd_read_page(&_mtd_dev, buf, page, offset, len);
|
||||
|
||||
if (ret < 0) {
|
||||
return CTAP1_ERR_OTHER;
|
||||
}
|
||||
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
int fido2_ctap_mem_write(const void *buf, uint32_t page, uint32_t offset, uint32_t len)
|
||||
{
|
||||
assert(buf);
|
||||
|
||||
int ret;
|
||||
|
||||
if (!_flash_is_erased(page, offset, len)) {
|
||||
ret = mtd_write_page(&_mtd_dev, buf, page, offset, len);
|
||||
|
||||
if (ret < 0) {
|
||||
return CTAP1_ERR_OTHER;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ret = mtd_write_page_raw(&_mtd_dev, buf, page, offset, len);
|
||||
|
||||
if (ret < 0) {
|
||||
return CTAP1_ERR_OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
static bool _flash_is_erased(int page, int offset, size_t len)
|
||||
{
|
||||
uint8_t *addr = ((uint8_t *)flashpage_addr(page) + offset);
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (addr[i] != FLASHPAGE_ERASE_STATE) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t fido2_ctap_mem_get_max_rk_amount(void)
|
||||
{
|
||||
return _max_rk_amnt;
|
||||
}
|
||||
|
||||
int fido2_ctap_mem_get_flashpage_number_of_rk(uint16_t rk_idx)
|
||||
{
|
||||
uint16_t idx = 0;
|
||||
|
||||
for (unsigned i = CTAP_FLASH_RK_START_PAGE; i < _amount_of_flashpages(); i++) {
|
||||
idx += flashpage_size(i) / CTAP_FLASH_RK_SZ;
|
||||
|
||||
if (idx >= rk_idx) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fido2_ctap_mem_get_offset_of_rk_into_flashpage(uint16_t rk_idx)
|
||||
{
|
||||
uint16_t idx = 0;
|
||||
|
||||
for (unsigned i = CTAP_FLASH_RK_START_PAGE; i < _amount_of_flashpages(); i++) {
|
||||
uint16_t old_idx = idx;
|
||||
idx += flashpage_size(i) / CTAP_FLASH_RK_SZ;
|
||||
|
||||
if (idx >= rk_idx) {
|
||||
return CTAP_FLASH_RK_SZ * (rk_idx - old_idx);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
122
sys/fido2/ctap/ctap_utils.c
Normal file
122
sys/fido2/ctap/ctap_utils.c
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup fido2_ctap_utils
|
||||
* @{
|
||||
* @file
|
||||
*
|
||||
* @author Nils Ollrogge <nils.ollrogge@fu-berlin.de>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "ztimer.h"
|
||||
|
||||
#include "fido2/ctap.h"
|
||||
#include "fido2/ctap/ctap_utils.h"
|
||||
|
||||
#if !IS_ACTIVE(CONFIG_FIDO2_CTAP_DISABLE_UP)
|
||||
#include "periph/gpio.h"
|
||||
#endif
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
#if !IS_ACTIVE(CONFIG_FIDO2_CTAP_DISABLE_UP)
|
||||
|
||||
/**
|
||||
* @brief Flag holding information if user is present or not
|
||||
*/
|
||||
static bool _user_present = false;
|
||||
|
||||
/**
|
||||
* @brief GPIO pin to use for user presence test
|
||||
*/
|
||||
static gpio_t _pin;
|
||||
|
||||
/**
|
||||
* @brief Button callback function
|
||||
*/
|
||||
static void _gpio_cb(void *arg);
|
||||
|
||||
int fido2_ctap_utils_init_gpio_pin(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank)
|
||||
{
|
||||
if (gpio_init_int(pin, mode, flank, _gpio_cb, NULL) < 0) {
|
||||
return CTAP1_ERR_OTHER;
|
||||
}
|
||||
|
||||
_pin = pin;
|
||||
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
int fido2_ctap_utils_user_presence_test(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
gpio_irq_enable(_pin);
|
||||
|
||||
#if !IS_ACTIVE(CONFIG_FIDO2_CTAP_DISABLE_LED)
|
||||
fido2_ctap_utils_led_animation();
|
||||
#endif
|
||||
|
||||
ret = _user_present ? CTAP2_OK : CTAP2_ERR_ACTION_TIMEOUT;
|
||||
|
||||
gpio_irq_disable(_pin);
|
||||
|
||||
_user_present = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void _gpio_cb(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
_user_present = true;
|
||||
}
|
||||
|
||||
#if !IS_ACTIVE(CONFIG_FIDO2_CTAP_DISABLE_LED)
|
||||
void fido2_ctap_utils_led_animation(void)
|
||||
{
|
||||
uint32_t start = ztimer_now(ZTIMER_MSEC);
|
||||
uint32_t diff = 0;
|
||||
uint32_t delay = 500;
|
||||
|
||||
while (!_user_present && diff < CTAP_UP_TIMEOUT) {
|
||||
#ifdef LED0_TOGGLE
|
||||
LED0_TOGGLE;
|
||||
#endif
|
||||
#ifdef LED1_TOGGLE
|
||||
LED1_TOGGLE;
|
||||
#endif
|
||||
#ifdef LED3_TOGGLE
|
||||
LED3_TOGGLE;
|
||||
#endif
|
||||
#ifdef LED2_TOGGLE
|
||||
LED2_TOGGLE;
|
||||
#endif
|
||||
ztimer_sleep(ZTIMER_MSEC, delay);
|
||||
diff = ztimer_now(ZTIMER_MSEC) - start;
|
||||
}
|
||||
|
||||
#ifdef LED0_TOGGLE
|
||||
LED0_OFF;
|
||||
#endif
|
||||
#ifdef LED1_TOGGLE
|
||||
LED1_OFF;
|
||||
#endif
|
||||
#ifdef LED3_TOGGLE
|
||||
LED3_OFF;
|
||||
#endif
|
||||
#ifdef LED2_TOGGLE
|
||||
LED2_OFF;
|
||||
#endif
|
||||
}
|
||||
#endif /* !IS_ACTIVE(CONFIG_FIDO2_CTAP_DISABLE_LED) */
|
||||
#endif /* CONFIG_FIDO2_CTAP_DISABLE_UP */
|
||||
7
sys/fido2/ctap/transport/Kconfig
Normal file
7
sys/fido2/ctap/transport/Kconfig
Normal file
@ -0,0 +1,7 @@
|
||||
# Copyright (C) 2021 Freie Universität Berlin
|
||||
#
|
||||
# This file is subject to the terms and conditions of the GNU Lesser
|
||||
# General Public License v2.1. See the file LICENSE in the top level
|
||||
# directory for more details.
|
||||
|
||||
rsource "hid/Kconfig"
|
||||
7
sys/fido2/ctap/transport/Makefile
Normal file
7
sys/fido2/ctap/transport/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
MODULE := fido2_ctap_transport
|
||||
|
||||
ifneq (,$(filter fido2_ctap_transport_hid,$(USEMODULE)))
|
||||
DIRS += hid
|
||||
endif
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
||||
102
sys/fido2/ctap/transport/ctap_transport.c
Normal file
102
sys/fido2/ctap/transport/ctap_transport.c
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup fido2_ctap_transport
|
||||
* @{
|
||||
* @file
|
||||
*
|
||||
* @author Nils Ollrogge <nils.ollrogge@fu-berlin.de>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "event/timeout.h"
|
||||
|
||||
#include "fido2/ctap/transport/ctap_transport.h"
|
||||
#include "fido2/ctap/ctap.h"
|
||||
|
||||
#if IS_USED(MODULE_FIDO2_CTAP_TRANSPORT_HID)
|
||||
#include "usb/usbus.h"
|
||||
#include "usb/usbus/hid_io.h"
|
||||
#include "fido2/ctap/transport/hid/ctap_hid.h"
|
||||
#endif
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
#if IS_USED(MODULE_FIDO2_CTAP_TRANSPORT_HID)
|
||||
/**
|
||||
* @brief CTAPHID timeout handler
|
||||
*/
|
||||
static void _ctap_hid_timeout_cb(event_t *arg);
|
||||
/**
|
||||
* @brief CTAPHID timeout event
|
||||
*/
|
||||
static event_t _ctap_hid_timeout_event = { .handler = _ctap_hid_timeout_cb };
|
||||
|
||||
/**
|
||||
* @brief CTAPHID event_timeout object
|
||||
*/
|
||||
static event_timeout_t _ctap_hid_event_timeout;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief CTAP stack
|
||||
*/
|
||||
static char _ctap_stack[CTAP_STACKSIZE];
|
||||
|
||||
/**
|
||||
* @brief CTAP transport event queue
|
||||
*/
|
||||
static event_queue_t _queue;
|
||||
|
||||
static void *_event_loop(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
int ret;
|
||||
|
||||
ret = fido2_ctap_init();
|
||||
|
||||
if (ret < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
event_queue_init(&_queue);
|
||||
|
||||
#if IS_USED(MODULE_FIDO2_CTAP_TRANSPORT_HID)
|
||||
event_timeout_init(&_ctap_hid_event_timeout, &_queue, &_ctap_hid_timeout_event);
|
||||
event_timeout_set(&_ctap_hid_event_timeout, CTAP_HID_TRANSACTION_TIMEOUT);
|
||||
#endif
|
||||
|
||||
event_loop(&_queue);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if IS_USED(MODULE_FIDO2_CTAP_TRANSPORT_HID)
|
||||
static void _ctap_hid_timeout_cb(event_t *arg)
|
||||
{
|
||||
(void)arg;
|
||||
fido2_ctap_transport_hid_check_timeouts();
|
||||
event_timeout_set(&_ctap_hid_event_timeout, CTAP_HID_TRANSACTION_TIMEOUT);
|
||||
}
|
||||
#endif
|
||||
|
||||
void fido2_ctap_transport_init(void)
|
||||
{
|
||||
#if IS_USED(MODULE_FIDO2_CTAP_TRANSPORT_HID)
|
||||
fido2_ctap_transport_hid_init(&_queue);
|
||||
#endif
|
||||
|
||||
int ret = thread_create(_ctap_stack, sizeof(_ctap_stack), CTAP_TRANSPORT_PRIO,
|
||||
THREAD_CREATE_STACKTEST, _event_loop, NULL,
|
||||
"fido2_ctap_transport_loop");
|
||||
|
||||
(void)ret;
|
||||
assert(ret > 0);
|
||||
}
|
||||
23
sys/fido2/ctap/transport/hid/Kconfig
Normal file
23
sys/fido2/ctap/transport/hid/Kconfig
Normal file
@ -0,0 +1,23 @@
|
||||
# Copyright (C) 2021 Freie Universität Berlin
|
||||
#
|
||||
# This file is subject to the terms and conditions of the GNU Lesser
|
||||
# General Public License v2.1. See the file LICENSE in the top level
|
||||
# directory for more details.
|
||||
|
||||
menuconfig KCONFIG_USEMODULE_FIDO2_CTAP_TRANSPORT_HID
|
||||
bool "FIDO2 CTAP TRANSPORT HID"
|
||||
depends on USEMODULE_FIDO2_CTAP_TRANSPORT_HID
|
||||
help
|
||||
Configure a FIDO2 CTAP authenticator via KConfig.
|
||||
|
||||
if KCONFIG_USEMODULE_FIDO2_CTAP_TRANSPORT_HID
|
||||
|
||||
config FIDO2_CTAP_TRANSPORT_HID_TRANSACTION_TIMEOUT
|
||||
int "CTAPHID Transaction timeout in milliseconds"
|
||||
default 500
|
||||
help
|
||||
A CTAPHID transaction has to be completed within a specified period
|
||||
of time to prevent the authenticator from being locked by a
|
||||
stalling application.
|
||||
|
||||
endif # KCONFIG_USEMODULE_FIDO2_CTAP_TRANSPORT_HID
|
||||
3
sys/fido2/ctap/transport/hid/Makefile
Normal file
3
sys/fido2/ctap/transport/hid/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
MODULE := fido2_ctap_transport_hid
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
||||
723
sys/fido2/ctap/transport/hid/ctap_hid.c
Normal file
723
sys/fido2/ctap/transport/hid/ctap_hid.c
Normal file
@ -0,0 +1,723 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup fido2_ctap_transport_hid
|
||||
* @{
|
||||
* @file
|
||||
*
|
||||
* @author Nils Ollrogge <nils.ollrogge@fu-berlin.de>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "xtimer.h"
|
||||
#include "usb/usbus.h"
|
||||
#include "usb/usbus/hid_io.h"
|
||||
|
||||
#include "fido2/ctap.h"
|
||||
#include "fido2/ctap/transport/hid/ctap_hid.h"
|
||||
#include "fido2/ctap/ctap_utils.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
/**
|
||||
* @brief CTAP HID report descriptor
|
||||
*
|
||||
* CTAP specification (version 20190130) section 8.1.8.2
|
||||
*/
|
||||
const uint8_t _hid_report_desc[] = {
|
||||
0x06, 0xD0, 0xF1, /**< HID_UsagePage ( FIDO_USAGE_PAGE ) */
|
||||
0x09, 0x01, /**< HID_Usage ( FIDO_USAGE_CTAPHID ) */
|
||||
0xA1, 0x01, /**< HID_Collection ( HID_Application ) */
|
||||
0x09, 0x20, /**< HID_Usage ( FIDO_USAGE_DATA_IN ) */
|
||||
0x15, 0x00, /**< HID_LogicalMin ( 0 ) */
|
||||
0x26, 0xFF, 0x00, /**< HID_LogicalMaxS ( 0xff ) */
|
||||
0x75, 0x08, /**< HID_ReportSize ( 8 ) */
|
||||
0x95, 0x40, /**< HID_ReportCount ( HID_INPUT_REPORT_BYTES ) */
|
||||
0x81, 0x02, /**< HID_Input ( HID_Data | HID_Absolute | HID_Variable ) */
|
||||
0x09, 0x21, /**< HID_Usage ( FIDO_USAGE_DATA_OUT ) */
|
||||
0x15, 0x00, /**< HID_LogicalMin ( 0 ) */
|
||||
0x26, 0xFF, 0x00, /**< HID_LogicalMaxS ( 0xff ) */
|
||||
0x75, 0x08, /**< HID_ReportSize ( 8 ) */
|
||||
0x95, 0x40, /**< HID_ReportCount ( HID_OUTPUT_REPORT_BYTES ) */
|
||||
0x91, 0x02, /**< HID_Output ( HID_Data | HID_Absolute | HID_Variable ) */
|
||||
0xC0, /**< HID_EndCollection */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief CTAP_HID buffer struct
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t cid; /**< channel identifier */
|
||||
uint8_t cmd; /**< CTAP_HID command */
|
||||
uint8_t buffer[CTAP_HID_BUFFER_SIZE]; /**< data buffer */
|
||||
uint16_t offset; /**< current offset into data buffer */
|
||||
int16_t seq; /**< current sequence number */
|
||||
uint16_t bcnt; /**< expected amount of bytes to be received */
|
||||
uint8_t err; /**< error type if error */
|
||||
bool is_locked; /**< buffer is locked by transaction */
|
||||
bool should_cancel; /**< flag if current transaction should be cancelled */
|
||||
} ctap_hid_state_t;
|
||||
|
||||
/**
|
||||
* @brief Serialize data and transmit it via USB HID layer
|
||||
*/
|
||||
static void _ctap_hid_write(uint8_t cmd, uint32_t cid, const void *_data, size_t size);
|
||||
|
||||
/**
|
||||
* @brief CTAPHID_CBOR command
|
||||
*
|
||||
* CTAP specification (version 20190130) section 8.1.9.1.2
|
||||
*/
|
||||
static void _handle_cbor_packet(uint8_t cmd, uint32_t cid, uint8_t *buf, uint16_t bcnt);
|
||||
|
||||
/**
|
||||
* @brief CTAPHID_INIT command
|
||||
*
|
||||
* CTAP specification (version 20190130) section 8.1.9.1.3
|
||||
*/
|
||||
static uint32_t _handle_init_packet(uint32_t cid, uint16_t bcnt,
|
||||
const uint8_t *nonce);
|
||||
|
||||
/**
|
||||
* @brief CTAPHID_WINK command
|
||||
*
|
||||
* CTAP specification (version 20190130) section 8.1.9.2.1
|
||||
*/
|
||||
static void _wink(uint32_t cid, uint8_t cmd);
|
||||
|
||||
/**
|
||||
* @brief Encode response to CTAPHID_INIT command
|
||||
*/
|
||||
static void _send_init_response(uint32_t cid_old, uint32_t cid_new,
|
||||
const uint8_t *nonce);
|
||||
|
||||
/**
|
||||
* @brief Clear the CTAP packet buffer
|
||||
*/
|
||||
static void _clear_ctap_buffer(void);
|
||||
|
||||
/**
|
||||
* @brief Buffer packet belonging to currently processed transaction
|
||||
*/
|
||||
static uint8_t _buffer_pkt(const ctap_hid_pkt_t *pkt);
|
||||
|
||||
/**
|
||||
* @brief Send error code to cid
|
||||
*/
|
||||
static void _send_error_response(uint32_t cid, uint8_t err);
|
||||
|
||||
/**
|
||||
* @brief Refresh the last_used timestamp for this cid
|
||||
*/
|
||||
static int8_t _refresh_cid(uint32_t cid);
|
||||
|
||||
/**
|
||||
* @brief Allocate a new logical channel
|
||||
*/
|
||||
static int8_t _add_cid(uint32_t cid);
|
||||
|
||||
/**
|
||||
* @brief Delete logical channel
|
||||
*/
|
||||
static int8_t _delete_cid(uint32_t cid);
|
||||
|
||||
/**
|
||||
* @brief Check if a logical channel with cid exists
|
||||
*/
|
||||
static bool _cid_exists(uint32_t cid);
|
||||
|
||||
/**
|
||||
* @brief Parse packet length from pkt
|
||||
*/
|
||||
static inline uint16_t _get_packet_len(const ctap_hid_pkt_t *pkt);
|
||||
|
||||
/**
|
||||
* @brief Process CTAPHID transaction
|
||||
*/
|
||||
static void _process_transaction(event_t *arg);
|
||||
|
||||
/**
|
||||
* @brief Check if packet is an initialization packet
|
||||
*/
|
||||
static inline bool _is_init_type_pkt(const ctap_hid_pkt_t *pkt);
|
||||
|
||||
/* usbus functionality */
|
||||
|
||||
/**
|
||||
* @brief USB stack
|
||||
*/
|
||||
static char _usb_stack[USBUS_STACKSIZE];
|
||||
|
||||
/**
|
||||
* @brief USBUS context
|
||||
*/
|
||||
static usbus_t _usbus;
|
||||
|
||||
/**
|
||||
* @brief Indicate if authenticator is busy processing a transactions
|
||||
*
|
||||
* Transactions are atomic, therefore only 1 transaction can be processed at
|
||||
* once
|
||||
*/
|
||||
static bool _is_busy = false;
|
||||
|
||||
/**
|
||||
* @brief State for handling transactions
|
||||
*/
|
||||
static ctap_hid_state_t _state;
|
||||
|
||||
/**
|
||||
* @brief Logical CTAPHID channels
|
||||
*/
|
||||
static ctap_hid_cid_t g_cids[CTAP_HID_CIDS_MAX];
|
||||
|
||||
/**
|
||||
* @brief Incremental channel ids
|
||||
*
|
||||
* channel id 0 is reserved
|
||||
*/
|
||||
static uint32_t _cid = 1;
|
||||
|
||||
/**
|
||||
* @brief CTAP transport layer event queue
|
||||
*/
|
||||
static event_queue_t *_queue;
|
||||
|
||||
/**
|
||||
* @brief CTAPHID event
|
||||
*/
|
||||
static event_t _ctap_hid_event = { .handler = _process_transaction };
|
||||
|
||||
/**
|
||||
* @brief USBUS context
|
||||
*/
|
||||
static usbus_t _usbus;
|
||||
|
||||
static void _usb_cb(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
uint8_t buffer[CONFIG_USBUS_HID_INTERRUPT_EP_SIZE];
|
||||
int read;
|
||||
|
||||
read = usb_hid_io_read(buffer, CONFIG_USBUS_HID_INTERRUPT_EP_SIZE);
|
||||
|
||||
if (read == CONFIG_USBUS_HID_INTERRUPT_EP_SIZE) {
|
||||
fido2_ctap_transport_hid_handle_packet(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void fido2_ctap_transport_hid_init(event_queue_t *queue)
|
||||
{
|
||||
_queue = queue;
|
||||
usbdev_t *usbdev = usbdev_get_ctx(0);
|
||||
|
||||
assert(usbdev);
|
||||
usbus_init(&_usbus, usbdev);
|
||||
usb_hid_io_init(&_usbus, _hid_report_desc, sizeof(_hid_report_desc));
|
||||
usb_hid_io_set_rx_cb(_usb_cb, NULL);
|
||||
usbus_create(_usb_stack, sizeof(_usb_stack), USBUS_PRIO, USBUS_TNAME, &_usbus);
|
||||
}
|
||||
|
||||
void fido2_ctap_transport_hid_handle_packet(void *pkt_raw)
|
||||
{
|
||||
ctap_hid_pkt_t *pkt = (ctap_hid_pkt_t *)pkt_raw;
|
||||
uint32_t cid = pkt->cid;
|
||||
uint8_t status = CTAP_HID_BUFFER_STATUS_BUFFERING;
|
||||
|
||||
if (cid == 0x00) {
|
||||
/* cid = 0x00 always invalid */
|
||||
_send_error_response(cid, CTAP_HID_ERR_INVALID_CHANNEL);
|
||||
return;
|
||||
}
|
||||
else if (_is_busy) {
|
||||
if (_state.cid == cid) {
|
||||
/* CTAP specification (version 20190130) section 8.1.5.3 */
|
||||
if (_is_init_type_pkt(pkt)) {
|
||||
if (pkt->init.cmd == CTAP_HID_COMMAND_INIT) {
|
||||
/* abort */
|
||||
_clear_ctap_buffer();
|
||||
status = _buffer_pkt(pkt);
|
||||
}
|
||||
else if (_state.is_locked && pkt->init.cmd ==
|
||||
CTAP_HID_COMMAND_CANCEL) {
|
||||
_state.should_cancel = true;
|
||||
}
|
||||
/* random init type pkt. invalid sequence of pkts */
|
||||
else {
|
||||
_send_error_response(cid, CTAP_HID_ERR_INVALID_SEQ);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* packet for this cid is currently being worked */
|
||||
else if (_state.is_locked) {
|
||||
_send_error_response(cid, CTAP_HID_ERR_CHANNEL_BUSY);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
/* buffer cont packets */
|
||||
status = _buffer_pkt(pkt);
|
||||
}
|
||||
}
|
||||
/* transactions are atomic. Deny all other cids if busy with one cid */
|
||||
else {
|
||||
_send_error_response(cid, CTAP_HID_ERR_CHANNEL_BUSY);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* first init packet received starts a transaction */
|
||||
if (_is_init_type_pkt(pkt)) {
|
||||
_is_busy = true;
|
||||
status = _buffer_pkt(pkt);
|
||||
}
|
||||
/* ignore rest */
|
||||
}
|
||||
|
||||
if (status == CTAP_HID_BUFFER_STATUS_ERROR) {
|
||||
_send_error_response(cid, _state.err);
|
||||
_delete_cid(cid);
|
||||
_clear_ctap_buffer();
|
||||
_is_busy = false;
|
||||
return;
|
||||
}
|
||||
|
||||
/* pkt->init.bcnt bytes have been received. Transaction can now be processed */
|
||||
if (status == CTAP_HID_BUFFER_STATUS_DONE) {
|
||||
_state.is_locked = 1;
|
||||
event_post(_queue, &_ctap_hid_event);
|
||||
_is_busy = false;
|
||||
}
|
||||
else {
|
||||
/* refresh timestamp of cid that is being buffered */
|
||||
_refresh_cid(_state.cid);
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t _buffer_pkt(const ctap_hid_pkt_t *pkt)
|
||||
{
|
||||
if (_is_init_type_pkt(pkt)) {
|
||||
/**
|
||||
* broadcast cid only allowed for CTAP_HID_COMMAND_INIT
|
||||
*/
|
||||
if (pkt->cid == CTAP_HID_BROADCAST_CID &&
|
||||
pkt->init.cmd != CTAP_HID_COMMAND_INIT) {
|
||||
_send_error_response(pkt->cid, CTAP_HID_ERR_INVALID_CHANNEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* received CTAP_HID_COMMAND_CANCEL while buffering packet.
|
||||
* Cancel request.
|
||||
*/
|
||||
if (pkt->init.cmd == CTAP_HID_COMMAND_CANCEL && !_state.is_locked &&
|
||||
pkt->cid == _state.cid) {
|
||||
|
||||
_state.err = CTAP2_ERR_KEEPALIVE_CANCEL;
|
||||
return CTAP_HID_BUFFER_STATUS_ERROR;
|
||||
}
|
||||
|
||||
_state.bcnt = _get_packet_len(pkt);
|
||||
|
||||
/* check for init transaction size described in CTAP specification
|
||||
(version 20190130) section 8.1.9.1.3 */
|
||||
if (pkt->init.cmd == CTAP_HID_COMMAND_INIT && _state.bcnt != 8) {
|
||||
_state.err = CTAP_HID_ERR_INVALID_LEN;
|
||||
return CTAP_HID_BUFFER_STATUS_ERROR;
|
||||
}
|
||||
|
||||
/* don't allow transactions bigger than max buffer size */
|
||||
if (_state.bcnt > CTAP_HID_BUFFER_SIZE) {
|
||||
_state.err = CTAP_HID_ERR_INVALID_LEN;
|
||||
return CTAP_HID_BUFFER_STATUS_ERROR;
|
||||
}
|
||||
|
||||
uint16_t size = (_state.bcnt < CTAP_HID_INIT_PAYLOAD_SIZE) ?
|
||||
_state.bcnt : CTAP_HID_INIT_PAYLOAD_SIZE;
|
||||
_state.cmd = pkt->init.cmd;
|
||||
_state.cid = pkt->cid;
|
||||
_state.seq = -1;
|
||||
memcpy(_state.buffer, pkt->init.payload, size);
|
||||
_state.offset = size;
|
||||
}
|
||||
else {
|
||||
int left = _state.bcnt - _state.offset;
|
||||
int diff = left - CTAP_HID_CONT_PAYLOAD_SIZE;
|
||||
_state.seq++;
|
||||
|
||||
/* seqs have to increase sequentially */
|
||||
if (pkt->cont.seq != _state.seq) {
|
||||
_state.err = CTAP_HID_ERR_INVALID_SEQ;
|
||||
return CTAP_HID_BUFFER_STATUS_ERROR;
|
||||
}
|
||||
|
||||
/* check for potential buffer overflow */
|
||||
if (_state.offset + CTAP_HID_CONT_PAYLOAD_SIZE > CTAP_HID_BUFFER_SIZE) {
|
||||
_state.err = CTAP_HID_ERR_INVALID_LEN;
|
||||
return CTAP_HID_BUFFER_STATUS_ERROR;
|
||||
}
|
||||
|
||||
if (diff <= 0) {
|
||||
memcpy(_state.buffer + _state.offset, pkt->cont.payload, left);
|
||||
_state.offset += left;
|
||||
}
|
||||
else {
|
||||
memcpy(_state.buffer + _state.offset, pkt->cont.payload,
|
||||
CTAP_HID_CONT_PAYLOAD_SIZE);
|
||||
_state.offset += CTAP_HID_CONT_PAYLOAD_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
return _state.offset == _state.bcnt ?
|
||||
CTAP_HID_BUFFER_STATUS_DONE : CTAP_HID_BUFFER_STATUS_BUFFERING;
|
||||
}
|
||||
|
||||
static void _process_transaction(event_t *arg)
|
||||
{
|
||||
(void)arg;
|
||||
uint8_t *buf = (uint8_t *)&_state.buffer;
|
||||
uint32_t cid = _state.cid;
|
||||
uint16_t bcnt = _state.bcnt;
|
||||
uint8_t cmd = _state.cmd;
|
||||
|
||||
if (cmd == CTAP_HID_COMMAND_INIT) {
|
||||
_handle_init_packet(cid, bcnt, buf);
|
||||
}
|
||||
else {
|
||||
/* readding deleted cid */
|
||||
if (!_cid_exists(cid) && _add_cid(cid) == -1) {
|
||||
_send_error_response(cid, CTAP_HID_ERR_CHANNEL_BUSY);
|
||||
}
|
||||
else {
|
||||
switch (cmd) {
|
||||
case CTAP_HID_COMMAND_MSG:
|
||||
/* not implemented as of now */
|
||||
DEBUG("CTAP_HID: MSG COMMAND \n");
|
||||
_send_error_response(cid, CTAP_HID_ERR_INVALID_CMD);
|
||||
break;
|
||||
case CTAP_HID_COMMAND_CBOR:
|
||||
DEBUG("CTAP_HID: CBOR COMMAND \n");
|
||||
_handle_cbor_packet(cmd, cid, buf, bcnt);
|
||||
break;
|
||||
case CTAP_HID_COMMAND_WINK:
|
||||
DEBUG("CTAP_HID: wink \n");
|
||||
_wink(cid, cmd);
|
||||
break;
|
||||
case CTAP_HID_COMMAND_PING:
|
||||
DEBUG("CTAP_HID: PING \n");
|
||||
_ctap_hid_write(cmd, cid, buf, bcnt);
|
||||
break;
|
||||
case CTAP_HID_COMMAND_CANCEL:
|
||||
/*
|
||||
* no transaction is currently being processed,
|
||||
* no reason to send cancel
|
||||
*/
|
||||
break;
|
||||
default:
|
||||
_send_error_response(cid, CTAP_HID_ERR_INVALID_CMD);
|
||||
DEBUG("Ctaphid: unknown command %u \n", cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* transaction done, cleanup */
|
||||
_clear_ctap_buffer();
|
||||
}
|
||||
|
||||
static uint32_t _handle_init_packet(uint32_t cid, uint16_t bcnt,
|
||||
const uint8_t *nonce)
|
||||
{
|
||||
uint32_t cid_new = 0;
|
||||
|
||||
/* cid 0 is reserved */
|
||||
if (cid == 0) {
|
||||
_send_error_response(cid, CTAP_HID_ERR_INVALID_CHANNEL);
|
||||
return 0;
|
||||
}
|
||||
/* check for len described in standard */
|
||||
if (bcnt != 8) {
|
||||
_send_error_response(cid, CTAP_HID_ERR_INVALID_LEN);
|
||||
return 0;
|
||||
}
|
||||
/* create new channel */
|
||||
if (cid == CTAP_HID_BROADCAST_CID) {
|
||||
cid_new = _cid++;
|
||||
|
||||
if (_add_cid(cid_new) == -1) {
|
||||
_send_error_response(cid, CTAP_HID_ERR_CHANNEL_BUSY);
|
||||
return 0;
|
||||
}
|
||||
_send_init_response(cid, cid_new, nonce);
|
||||
}
|
||||
/* synchronize channel */
|
||||
else {
|
||||
cid_new = cid;
|
||||
if (!_cid_exists(cid)) {
|
||||
if (_add_cid(cid) == -1) {
|
||||
/* reached cid limit */
|
||||
_send_error_response(cid, CTAP_HID_ERR_CHANNEL_BUSY);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
_send_init_response(cid, cid, nonce);
|
||||
}
|
||||
|
||||
return cid_new;
|
||||
}
|
||||
|
||||
static void _handle_cbor_packet(uint8_t cmd, uint32_t cid, uint8_t *buf, uint16_t bcnt)
|
||||
{
|
||||
ctap_resp_t resp;
|
||||
uint8_t err;
|
||||
size_t size;
|
||||
|
||||
if (bcnt == 0) {
|
||||
err = CTAP_HID_ERR_INVALID_LEN;
|
||||
cmd = CTAP_HID_COMMAND_ERROR;
|
||||
_ctap_hid_write(cmd, cid, &err, sizeof(err));
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&resp, 0, sizeof(ctap_resp_t));
|
||||
|
||||
ctap_req_t req;
|
||||
|
||||
req.method = *buf;
|
||||
req.buf = buf + 1;
|
||||
req.len = bcnt - 1;
|
||||
|
||||
size = fido2_ctap_handle_request(&req, &resp);
|
||||
|
||||
/* transaction done, clear should_cancel flag */
|
||||
_state.should_cancel = false;
|
||||
|
||||
if (resp.status == CTAP2_OK && size > 0) {
|
||||
/* status + data */
|
||||
_ctap_hid_write(cmd, cid, &resp, size + sizeof(resp.status));
|
||||
}
|
||||
else {
|
||||
/* status only */
|
||||
_ctap_hid_write(cmd, cid, &resp.status, sizeof(resp.status));
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool _is_init_type_pkt(const ctap_hid_pkt_t *pkt)
|
||||
{
|
||||
return ((pkt->init.cmd & CTAP_HID_INIT_PACKET) == CTAP_HID_INIT_PACKET);
|
||||
}
|
||||
|
||||
static void _clear_ctap_buffer(void)
|
||||
{
|
||||
memset(&_state, 0, sizeof(_state));
|
||||
}
|
||||
|
||||
bool fido2_ctap_transport_hid_should_cancel(void)
|
||||
{
|
||||
return _state.should_cancel;
|
||||
}
|
||||
|
||||
void fido2_ctap_transport_hid_check_timeouts(void)
|
||||
{
|
||||
uint64_t now = xtimer_now_usec64();
|
||||
|
||||
for (uint8_t i = 0; i < CTAP_HID_CIDS_MAX; i++) {
|
||||
/* transaction timed out because cont packets didn't arrive in time */
|
||||
if (_is_busy && g_cids[i].taken &&
|
||||
(now - g_cids[i].last_used) >= CTAP_HID_TRANSACTION_TIMEOUT &&
|
||||
_state.cid == g_cids[i].cid && !_state.is_locked) {
|
||||
|
||||
_send_error_response(g_cids[i].cid, CTAP_HID_ERR_MSG_TIMEOUT);
|
||||
_delete_cid(g_cids[i].cid);
|
||||
_clear_ctap_buffer();
|
||||
|
||||
_is_busy = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int8_t _add_cid(uint32_t cid)
|
||||
{
|
||||
uint64_t oldest = xtimer_now_usec64();
|
||||
int8_t index_oldest = -1;
|
||||
|
||||
for (int i = 0; i < CTAP_HID_CIDS_MAX; i++) {
|
||||
if (!g_cids[i].taken) {
|
||||
g_cids[i].taken = true;
|
||||
g_cids[i].cid = cid;
|
||||
g_cids[i].last_used = xtimer_now_usec64();
|
||||
|
||||
return CTAP_HID_OK;
|
||||
}
|
||||
|
||||
if (g_cids[i].last_used < oldest) {
|
||||
oldest = g_cids[i].last_used;
|
||||
index_oldest = i;
|
||||
}
|
||||
}
|
||||
|
||||
/* remove oldest cid to make place for a new one */
|
||||
if (index_oldest > -1) {
|
||||
g_cids[index_oldest].taken = true;
|
||||
g_cids[index_oldest].cid = cid;
|
||||
g_cids[index_oldest].last_used = xtimer_now_usec64();
|
||||
return CTAP_HID_OK;
|
||||
}
|
||||
|
||||
return CTAP_HID_ERR_OTHER;
|
||||
}
|
||||
|
||||
static int8_t _refresh_cid(uint32_t cid)
|
||||
{
|
||||
for (int i = 0; i < CTAP_HID_CIDS_MAX; i++) {
|
||||
if (g_cids[i].cid == cid) {
|
||||
g_cids[i].last_used = xtimer_now_usec64();
|
||||
return CTAP_HID_OK;
|
||||
}
|
||||
}
|
||||
return CTAP_HID_ERR_OTHER;
|
||||
}
|
||||
|
||||
static int8_t _delete_cid(uint32_t cid)
|
||||
{
|
||||
for (int i = 0; i < CTAP_HID_CIDS_MAX; i++) {
|
||||
if (g_cids[i].cid == cid) {
|
||||
g_cids[i].taken = false;
|
||||
g_cids[i].cid = 0;
|
||||
|
||||
return CTAP_HID_OK;
|
||||
}
|
||||
}
|
||||
return CTAP_HID_ERR_OTHER;
|
||||
}
|
||||
|
||||
static bool _cid_exists(uint32_t cid)
|
||||
{
|
||||
for (int i = 0; i < CTAP_HID_CIDS_MAX; i++) {
|
||||
if (g_cids[i].cid == cid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline uint16_t _get_packet_len(const ctap_hid_pkt_t *pkt)
|
||||
{
|
||||
return (uint16_t)((pkt->init.bcnth << 8) | pkt->init.bcntl);
|
||||
}
|
||||
|
||||
static void _wink(uint32_t cid, uint8_t cmd)
|
||||
{
|
||||
#if !IS_ACTIVE(CONFIG_FIDO2_CTAP_DISABLE_LED)
|
||||
uint32_t delay = CTAP_HID_WINK_DELAY;
|
||||
for (int i = 1; i <= 8; i++) {
|
||||
#ifdef LED0_TOGGLE
|
||||
LED0_TOGGLE;
|
||||
xtimer_msleep(delay);
|
||||
#endif
|
||||
#ifdef LED1_TOGGLE
|
||||
LED1_TOGGLE;
|
||||
xtimer_msleep(delay);
|
||||
#endif
|
||||
#ifdef LED2_TOGGLE
|
||||
LED2_TOGGLE;
|
||||
xtimer_msleep(delay);
|
||||
#endif
|
||||
#ifdef LED3_TOGGLE
|
||||
LED3_TOGGLE;
|
||||
xtimer_msleep(delay);
|
||||
#endif
|
||||
delay /= 2;
|
||||
}
|
||||
#endif /* CONFIG_FIDO2_CTAP_DISABLE_LED */
|
||||
|
||||
_ctap_hid_write(cmd, cid, NULL, 0);
|
||||
}
|
||||
|
||||
static void _send_error_response(uint32_t cid, uint8_t err)
|
||||
{
|
||||
DEBUG("ctap_trans_hid err resp: %02x \n", err);
|
||||
_ctap_hid_write(CTAP_HID_COMMAND_ERROR, cid, &err, sizeof(err));
|
||||
}
|
||||
|
||||
static void _send_init_response(uint32_t cid_old, uint32_t cid_new,
|
||||
const uint8_t *nonce)
|
||||
{
|
||||
ctap_hid_init_resp_t resp;
|
||||
|
||||
memset(&resp, 0, sizeof(ctap_hid_init_resp_t));
|
||||
|
||||
resp.cid = cid_new;
|
||||
resp.protocol_version = CTAP_HID_PROTOCOL_VERSION;
|
||||
resp.version_major = 0;
|
||||
resp.version_minor = 0;
|
||||
resp.build_version = 0;
|
||||
memcpy(resp.nonce, nonce, sizeof(resp.nonce));
|
||||
|
||||
uint8_t cmd = (CTAP_HID_INIT_PACKET | CTAP_HID_COMMAND_INIT);
|
||||
|
||||
resp.capabilities = CTAP_HID_CAPABILITY_CBOR | CTAP_HID_CAPABILITY_WINK
|
||||
| CTAP_HID_CAPABILITY_NMSG;
|
||||
|
||||
_ctap_hid_write(cmd, cid_old, &resp, sizeof(ctap_hid_init_resp_t));
|
||||
}
|
||||
|
||||
void _ctap_hid_write(uint8_t cmd, uint32_t cid, const void *_data, size_t len)
|
||||
{
|
||||
const uint8_t *data = (uint8_t *)_data;
|
||||
uint8_t buf[CONFIG_USBUS_HID_INTERRUPT_EP_SIZE] = { 0 };
|
||||
uint16_t bytes_written = 0;
|
||||
uint8_t seq = 0;
|
||||
uint8_t offset = 0;
|
||||
|
||||
memcpy(buf, &cid, sizeof(cid));
|
||||
offset += sizeof(cid);
|
||||
buf[offset++] = cmd;
|
||||
/* high part of payload length first */
|
||||
buf[offset++] = (len & 0xff00) >> 8;
|
||||
buf[offset++] = (len & 0xff) >> 0;
|
||||
|
||||
if (data == NULL) {
|
||||
usb_hid_io_write(buf, CONFIG_USBUS_HID_INTERRUPT_EP_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (offset == 0) {
|
||||
memcpy(buf, &cid, sizeof(cid));
|
||||
offset += sizeof(cid);
|
||||
|
||||
/* initialization packet */
|
||||
if (bytes_written == 0) {
|
||||
buf[offset++] = cmd;
|
||||
buf[offset++] = (len & 0xff00) >> 8;
|
||||
buf[offset++] = (len & 0xff) >> 0;
|
||||
}
|
||||
/* continuation packet */
|
||||
else {
|
||||
buf[offset++] = seq++;
|
||||
}
|
||||
}
|
||||
|
||||
buf[offset++] = data[i];
|
||||
bytes_written++;
|
||||
|
||||
if (offset == CONFIG_USBUS_HID_INTERRUPT_EP_SIZE) {
|
||||
usb_hid_io_write(buf, CONFIG_USBUS_HID_INTERRUPT_EP_SIZE);
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (offset > 0) {
|
||||
memset(buf + offset, 0, CONFIG_USBUS_HID_INTERRUPT_EP_SIZE - offset);
|
||||
usb_hid_io_write(buf, CONFIG_USBUS_HID_INTERRUPT_EP_SIZE);
|
||||
}
|
||||
}
|
||||
218
sys/fido2/doc.txt
Normal file
218
sys/fido2/doc.txt
Normal file
@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup fido2 FIDO2 - Fast Identity Online 2
|
||||
* @ingroup sys
|
||||
* @brief Description of the FIDO2 CTAP implementation in RIOT
|
||||
* @author Nils Ollrogge <nils.ollrogge@fu-berlin.de>
|
||||
*
|
||||
* @warning This feature is experimental!
|
||||
* This API is experimental and in an early state - expect changes.
|
||||
*
|
||||
* @warning The FIDO2 implementation currently stores private keys in plain text inside flash memory.
|
||||
*
|
||||
* FIDO2 is an authentication standard that seeks to solve the password problem
|
||||
* by enabling passwordless authentication. Instead of using passwords to
|
||||
* authenticate to web services, FIDO2 enables users to use common devices
|
||||
* (authenticators) to create cryptographic credentials which are then used
|
||||
* for authentication. FIDO2 consists of the [W3C Web Authentication
|
||||
* specification (WebAuthn)](https://www.w3.org/TR/2019/REC-webauthn-1-20190304/)
|
||||
* and the [Client to Authenticator Protocol (CTAP)](https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html).
|
||||
*
|
||||
* **This code implements the FIDO2 CTAP protocol.**
|
||||
*
|
||||
* ### General
|
||||
*
|
||||
* Following is an overview of the entities of this implementation and their relationships:
|
||||
*
|
||||
*
|
||||
* +-----------+ +-----------+ +-----------+ +-----------+
|
||||
* | | | | | | | |
|
||||
* | ctap_cbor | |ctap_crypto| | ctap_mem | |ctap_utils |
|
||||
* | | | | | | | |
|
||||
* +-----------+ +-----------+ +-----------+ +-----|-----+
|
||||
* | | | |
|
||||
* | | | |
|
||||
* | +---------------------------+ |
|
||||
* | | | |
|
||||
* |---------| ctap |--------|
|
||||
* | |
|
||||
* +---------------------------+
|
||||
* |
|
||||
* +-------------|-------------+
|
||||
* | |
|
||||
* | ctap_transport |
|
||||
* | |
|
||||
* +---------------------------+
|
||||
* |
|
||||
* |
|
||||
* +-----------+
|
||||
* | |
|
||||
* | ctap_hid |
|
||||
* | |
|
||||
* +-----------+
|
||||
*
|
||||
*
|
||||
*
|
||||
* **ctap_hid**
|
||||
*
|
||||
* USB Human Interface Device (USB HID) transport binding for CTAP (CTAPHID).
|
||||
*
|
||||
* Initializes the USBUS HID interface.
|
||||
*
|
||||
* Communicates with the USBUS USB HID interface through the USBUS HID IO interface.
|
||||
*
|
||||
* **ctap_transport**
|
||||
*
|
||||
* Initializes CTAP layer.
|
||||
*
|
||||
* Initializes CTAP event queue.
|
||||
*
|
||||
* Manages CTAP transport bindings (currently only CTAPHID).
|
||||
*
|
||||
* **ctap**
|
||||
*
|
||||
* Contains the main CTAP logic.
|
||||
*
|
||||
* Makes use of helpers for flash access, cryptographic operations and CBOR operations.
|
||||
*
|
||||
* **ctap_cbor**
|
||||
*
|
||||
* Helper containing functionality to parse and encode CBOR messages. Uses the [tinyCBOR](https://doc.riot-os.org/group__pkg__tinycbor.html) pkg.
|
||||
*
|
||||
* **ctap_crypto**
|
||||
*
|
||||
* Helper containing functionality for cryptographic operations.
|
||||
*
|
||||
* Abstraction for cryptographic operations (SHA256, HMAC-SHA-256, AES CCM).
|
||||
*
|
||||
* @note This abstraction exposes error return values which are currently not implemented in all cases by the RIOT crypto API.
|
||||
*
|
||||
* Abstraction for Elliptic curve cryptography (ECC) operations. Uses the [micro-ecc](https://api.riot-os.org/group__pkg__micro__ecc.html) pkg.
|
||||
*
|
||||
* Parsing of cryptographic signatures into ASN.1 DER format. Uses the [tiny-asn1](http://doc.riot-os.org/group__pkg__tiny-asn1.html) pkg.
|
||||
*
|
||||
* **ctap_mem**
|
||||
*
|
||||
* Abstraction for flash operations. Uses the RIOT [Flashpage MTD driver](http://api.riot-os.org/group__drivers__mtd__flashpage.html).
|
||||
*
|
||||
* Adds additional functionality to speedup flash accesses (e.g. by checking if a flash page is erased to avoid unnecessary erasures of flash pages).
|
||||
*
|
||||
* **ctap_utils**
|
||||
*
|
||||
* Abstraction for GPIO functionality and LED animations.
|
||||
*
|
||||
* ### Implemented features
|
||||
* **FIDO2 CTAP methods**
|
||||
*
|
||||
* All methods defined in the FIDO2 CTAP specification are implemented. Specifically
|
||||
* these are:
|
||||
* * MakeCredential
|
||||
* * GetAssertion
|
||||
* * GetNextAssertion
|
||||
* * GetInfo
|
||||
* * ClientPIN
|
||||
* * Reset
|
||||
*
|
||||
* For information about the FIDO2 CTAP methods refer to the [CTAP specification](https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#authenticator-api).
|
||||
*
|
||||
* **Transport bindings**
|
||||
*
|
||||
* The USB Human Interface Device (USB HID) transport binding is fully implemented.
|
||||
*
|
||||
* For more information about the available transport bindings refer to the [CTAP specification](https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#transport-specific-bindings).
|
||||
*
|
||||
* **Credentials**
|
||||
*
|
||||
* Both types of credentials are supported. Resident and non resident.
|
||||
*
|
||||
* * Resident Credentials
|
||||
* * Resident credentials are credentials stored on the authenticator.
|
||||
* * This implementation stores resident keys in flash memory.
|
||||
* @warning As of now the credentials (containing a private key) are stored
|
||||
* in plain text inside flash memory
|
||||
*
|
||||
* * Non-resident credentials
|
||||
* * Non-resident credentials are credentials that are stored by the relying
|
||||
* party in encrypted form.
|
||||
* * To encrypt the credentials, this implementation uses the RIOT [AES-CCM 128
|
||||
* CCM implementation](https://api.riot-os.org/ccm_8h.html).
|
||||
*
|
||||
* For more information about the two types of credential refer to the [WebAuthn specification](https://www.w3.org/TR/2019/REC-webauthn-1-20190304/#sctn-credential-storage-modality)
|
||||
*
|
||||
* **Attestation types**
|
||||
*
|
||||
* Currently only self attestation is supported.
|
||||
*
|
||||
* For more information about available attestation types refer to the [WebAuthn specification](https://www.w3.org/TR/2019/REC-webauthn-1-20190304/#sctn-attestation-types).
|
||||
*
|
||||
* ### Unimplemented features
|
||||
*
|
||||
* **Backward compatibility with FIDO1**
|
||||
*
|
||||
* For more information about the backward compatibility of FIDO2 to FIDO1
|
||||
* refer to the [CTAP specification](https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#u2f-interoperability).
|
||||
*
|
||||
* **Support of further attestation types**
|
||||
*
|
||||
* Specifically these are:
|
||||
* * Basic Attestation
|
||||
* * Attestation Certificate Authority
|
||||
* * Elliptic Curve based Direct Anonymous Attestation
|
||||
*
|
||||
* For more information about available attestation types refer to the [WebAuthn specification](https://www.w3.org/TR/2019/REC-webauthn-1-20190304/#sctn-attestation-types).
|
||||
*
|
||||
* **Support of further transport bindings**
|
||||
*
|
||||
* Specifically these are:
|
||||
* * Near Field Communication (NFC)
|
||||
* * Bluetooth Low Energy (BLE)
|
||||
*
|
||||
* For information about the available transport bindings refer to the
|
||||
* [CTAP specification](https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#transport-specific-bindings).
|
||||
*
|
||||
* **Extensions**
|
||||
*
|
||||
* For information about CTAP extensions refer to the [CTAP specification](https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#sctn-defined-extensions)
|
||||
*
|
||||
* **CTAP 2.1 support**
|
||||
*
|
||||
* None of the additions from the [CTAP 2.1 specification](https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html) are implemented.
|
||||
*
|
||||
* ### Testing
|
||||
*
|
||||
* Testing is done with the help of the fido2-tests package based on the [solokeys fido2-tests](https://github.com/solokeys/fido2-tests).
|
||||
*
|
||||
* For for more information about testing the FIDO2 CTAP implementation refer to the README of the test application (`/tests/sys_fido2_ctap`).
|
||||
*
|
||||
* **Todo**
|
||||
*
|
||||
* * The expected return codes of some tests were changed due to different opinions of how to interpret the CTAP2 specification. Refer to the [issue](https://github.com/solokeys/fido2-tests/issues/55) for more information. As of writing this the issue is still open.
|
||||
*
|
||||
* ### Configuration
|
||||
*
|
||||
* There are two CFLAGS which can be used to change the behavior of the FIDO2 CTAP implementation:
|
||||
*
|
||||
* * **FIDO2_CTAP_DISABLE_UP**: Disables the user presence test. User presence will always be set to true.
|
||||
* This is helpful when running the fido2-tests as one doesn't have to click the button many times, as well as other use cases
|
||||
* where no user presence test is wanted.
|
||||
* * **FIDO2_CTAP_DISABLE_LED**: Disables LED animations which are used to indicate that user action is needed.
|
||||
*
|
||||
* The CFLAGS can either be set in the Makefile or configured via KConfig.
|
||||
*
|
||||
* ### Future work
|
||||
*
|
||||
* Future improvements / extensions to the FIDO2 CTAP implementation that should be implemented are:
|
||||
*
|
||||
* * Usage of secure elements if available to safely store private keys of FIDO2 credentials.
|
||||
* * Usage of an extra cryptographic processor (e.g. [ARM CryptoCell](https://infocenter.nordicsemi.com/index.jsp?topic=%2Fps_nrf9160%2Fcryptocell.html)) to improve efficiency and drop dependency for ECC cryptography.
|
||||
* * Support of further attestation types.
|
||||
* * Support of further CTAP transport bindings.
|
||||
* * Support of CTAP 2.1.
|
||||
*/
|
||||
219
sys/include/fido2/ctap.h
Normal file
219
sys/include/fido2/ctap.h
Normal file
@ -0,0 +1,219 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup fido2_ctap CTAP
|
||||
* @ingroup fido2
|
||||
* @brief FIDO2 CTAP
|
||||
*
|
||||
* The Client-to-Authenticator Protocol (CTAP) is an application layer protocol
|
||||
* for the communication between an authenticator and a host.
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Public FIDO2 CTAP defines, structures and function declarations
|
||||
*
|
||||
* @author Nils Ollrogge <nils.ollrogge@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef FIDO2_CTAP_H
|
||||
#define FIDO2_CTAP_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief CTAP max message size
|
||||
*
|
||||
* CTAP specification (version 20190130) section 6
|
||||
*/
|
||||
#define CTAP_MAX_MSG_SIZE 0x400
|
||||
|
||||
/**
|
||||
* @brief CTAP status codes
|
||||
*
|
||||
* CTAP specification (version 20190130) section 6.3
|
||||
* @{
|
||||
*/
|
||||
typedef enum {
|
||||
CTAP2_OK = 0x00,
|
||||
CTAP1_ERR_INVALID_COMMAND = 0x01,
|
||||
CTAP1_ERR_INVALID_PARAMETER = 0x02,
|
||||
CTAP1_ERR_INVALID_LENGTH = 0x03,
|
||||
CTAP1_ERR_INVALID_SEQ = 0x04,
|
||||
CTAP1_ERR_TIMEOUT = 0x05,
|
||||
CTAP1_ERR_CHANNEL_BUSY = 0x06,
|
||||
CTAP1_ERR_LOCK_REQUIRED = 0x0A,
|
||||
CTAP1_ERR_INVALID_CHANNEL = 0x0B,
|
||||
CTAP2_ERR_CBOR_PARSING = 0x10,
|
||||
CTAP2_ERR_CBOR_UNEXPECTED_TYPE = 0x11,
|
||||
CTAP2_ERR_INVALID_CBOR = 0x12,
|
||||
CTAP2_ERR_INVALID_CBOR_TYPE = 0x13,
|
||||
CTAP2_ERR_MISSING_PARAMETER = 0x14,
|
||||
CTAP2_ERR_LIMIT_EXCEEDED = 0x15,
|
||||
CTAP2_ERR_UNSUPPORTED_EXTENSION = 0x16,
|
||||
CTAP2_ERR_TOO_MANY_ELEMENTS = 0x17,
|
||||
CTAP2_ERR_EXTENSION_NOT_SUPPORTED = 0x18,
|
||||
CTAP2_ERR_CREDENTIAL_EXCLUDED = 0x19,
|
||||
CTAP2_ERR_CREDENTIAL_NOT_VALID = 0x20,
|
||||
CTAP2_ERR_PROCESSING = 0x21,
|
||||
CTAP2_ERR_INVALID_CREDENTIAL = 0x22,
|
||||
CTAP2_ERR_USER_ACTION_PENDING = 0x23,
|
||||
CTAP2_ERR_OPERATION_PENDING = 0x24,
|
||||
CTAP2_ERR_NO_OPERATIONS = 0x25,
|
||||
CTAP2_ERR_UNSUPPORTED_ALGORITHM = 0x26,
|
||||
CTAP2_ERR_OPERATION_DENIED = 0x27,
|
||||
CTAP2_ERR_KEY_STORE_FULL = 0x28,
|
||||
CTAP2_ERR_NOT_BUSY = 0x29,
|
||||
CTAP2_ERR_NO_OPERATION_PENDING = 0x2A,
|
||||
CTAP2_ERR_UNSUPPORTED_OPTION = 0x2B,
|
||||
CTAP2_ERR_INVALID_OPTION = 0x2C,
|
||||
CTAP2_ERR_KEEPALIVE_CANCEL = 0x2D,
|
||||
CTAP2_ERR_NO_CREDENTIALS = 0x2E,
|
||||
CTAP2_ERR_USER_ACTION_TIMEOUT = 0x2F,
|
||||
CTAP2_ERR_NOT_ALLOWED = 0x30,
|
||||
CTAP2_ERR_PIN_INVALID = 0x31,
|
||||
CTAP2_ERR_PIN_BLOCKED = 0x32,
|
||||
CTAP2_ERR_PIN_AUTH_INVALID = 0x33,
|
||||
CTAP2_ERR_PIN_AUTH_BLOCKED = 0x34,
|
||||
CTAP2_ERR_PIN_NOT_SET = 0x35,
|
||||
CTAP2_ERR_PIN_REQUIRED = 0x36,
|
||||
CTAP2_ERR_PIN_POLICY_VIOLATION = 0x37,
|
||||
CTAP2_ERR_PIN_TOKEN_EXPIRED = 0x38,
|
||||
CTAP2_ERR_REQUEST_TOO_LARGE = 0x39,
|
||||
CTAP2_ERR_ACTION_TIMEOUT = 0x3A,
|
||||
CTAP2_ERR_UP_REQUIRED = 0x3B,
|
||||
CTAP1_ERR_OTHER = 0x7F,
|
||||
CTAP2_ERR_SPEC_LAST = 0xDF,
|
||||
CTAP2_ERR_EXTENSION_FIRST = 0xE0,
|
||||
CTAP2_ERR_EXTENSION_LAST = 0xEF,
|
||||
CTAP2_ERR_VENDOR_FIRST = 0xF0,
|
||||
CTAP2_ERR_VENDOR_LAST = 0xFF
|
||||
} ctap_status_codes_t;
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief CTAP request struct
|
||||
*
|
||||
* CTAP specification (version 20190130) section 6.1
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t *buf; /**< Buffer holding CBOR encoded data */
|
||||
size_t len; /**< Length of buf */
|
||||
uint8_t method; /**< CTAP method identitifer */
|
||||
} ctap_req_t;
|
||||
|
||||
/**
|
||||
* @brief CTAP response struct
|
||||
*
|
||||
* CTAP specification (version 20190130) section 6.2
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t status; /**< response status */
|
||||
uint8_t data[CTAP_MAX_MSG_SIZE]; /**< response data */
|
||||
} ctap_resp_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize ctap
|
||||
*
|
||||
* @return 0 for success
|
||||
* @return negative error code otherwise
|
||||
*/
|
||||
int fido2_ctap_init(void);
|
||||
|
||||
/**
|
||||
* @brief Handle CBOR encoded ctap request.
|
||||
*
|
||||
* This is a convenience function that checks @p req->method and calls the
|
||||
* appropriate CTAP method handler function
|
||||
*
|
||||
* @param[in] req request struct
|
||||
* @param[in] resp response struct
|
||||
*
|
||||
* @return Length of @p resp->data
|
||||
*/
|
||||
size_t fido2_ctap_handle_request(ctap_req_t *req, ctap_resp_t *resp);
|
||||
|
||||
/**
|
||||
* @brief MakeCredential method
|
||||
*
|
||||
* CTAP specification (version 20190130) section 5.1
|
||||
*
|
||||
* @param[in] req CTAP request
|
||||
* @param[in, out] resp CTAP response
|
||||
*
|
||||
* @return Length of @p resp->data
|
||||
*/
|
||||
size_t fido2_ctap_make_credential(ctap_req_t *req, ctap_resp_t *resp);
|
||||
|
||||
/**
|
||||
* @brief GetAssertion method
|
||||
*
|
||||
* CTAP specification (version 20190130) section 5.2
|
||||
*
|
||||
* @param[in] req CTAP request
|
||||
* @param[in, out] resp CTAP response
|
||||
*
|
||||
* @return Length of @p resp->data
|
||||
*/
|
||||
size_t fido2_ctap_get_assertion(ctap_req_t *req, ctap_resp_t *resp);
|
||||
|
||||
/**
|
||||
* @brief GetNextAssertion method
|
||||
*
|
||||
* CTAP specification (version 20190130) section 5.3
|
||||
*
|
||||
* @param[in, out] resp CTAP response
|
||||
*
|
||||
* @return Length of @p resp->data
|
||||
*/
|
||||
size_t fido2_ctap_get_next_assertion(ctap_resp_t *resp);
|
||||
|
||||
/**
|
||||
* @brief GetInfo method
|
||||
*
|
||||
* CTAP specification (version 20190130) section 5.4
|
||||
*
|
||||
* @param[in, out] resp CTAP response
|
||||
*
|
||||
* @return Length of @p resp->data
|
||||
*/
|
||||
size_t fido2_ctap_get_info(ctap_resp_t *resp);
|
||||
|
||||
/**
|
||||
* @brief ClientPIN method
|
||||
*
|
||||
* CTAP specification (version 20190130) section 5.5
|
||||
*
|
||||
* @param[in] req CTAP request
|
||||
* @param[in, out] resp CTAP response
|
||||
*
|
||||
* @return Length of @p resp->data
|
||||
*/
|
||||
size_t fido2_ctap_client_pin(ctap_req_t *req, ctap_resp_t *resp);
|
||||
|
||||
/**
|
||||
* @brief Reset method
|
||||
*
|
||||
* CTAP specification (version 20190130) section 5.6
|
||||
*
|
||||
* @param[in, out] resp CTAP response
|
||||
*
|
||||
* @return Length of @p resp->data
|
||||
*/
|
||||
size_t fido2_ctap_reset(ctap_resp_t *resp);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* FIDO2_CTAP_H */
|
||||
/** @} */
|
||||
674
sys/include/fido2/ctap/ctap.h
Normal file
674
sys/include/fido2/ctap/ctap.h
Normal file
@ -0,0 +1,674 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup fido2_ctap_ctap FIDO2 CTAP
|
||||
* @ingroup fido2_ctap
|
||||
* @brief FIDO2 CTAP
|
||||
*
|
||||
* The Client-to-Authenticator Protocol (CTAP) is an application layer protocol
|
||||
* for the communication between an authenticator and a host.
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Internal FIDO2 CTAP defines, structures and function declarations
|
||||
*
|
||||
* @author Nils Ollrogge <nils.ollrogge@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef FIDO2_CTAP_CTAP_H
|
||||
#define FIDO2_CTAP_CTAP_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "mutex.h"
|
||||
#include "cbor.h"
|
||||
#include "assert.h"
|
||||
#include "crypto/modes/ccm.h"
|
||||
#include "timex.h"
|
||||
#include "board.h"
|
||||
|
||||
#include "fido2/ctap.h"
|
||||
#include "fido2/ctap/ctap_crypto.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Size of pin auth
|
||||
*
|
||||
* First 16 bytes of a HMAC-256.
|
||||
*
|
||||
* CTAP specification (version 20190130) section 5.5.8.2.
|
||||
*/
|
||||
#define CTAP_PIN_AUTH_SZ 16
|
||||
|
||||
/**
|
||||
* @name CTAP methods
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#define CTAP_MAKE_CREDENTIAL 0x01 /**< authenticatorMakeCredential method */
|
||||
#define CTAP_GET_ASSERTION 0x02 /**< authenticatorGetAssertion method */
|
||||
#define CTAP_GET_INFO 0x04 /**< authenticatorGetInfo method */
|
||||
#define CTAP_CLIENT_PIN 0x06 /**< authenticatorClientPIN method */
|
||||
#define CTAP_RESET 0x07 /**< authenticatorReset method */
|
||||
#define CTAP_GET_NEXT_ASSERTION 0x08 /**< authenticatorGetNextAssertion method */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name CTAP authenticator data option flags
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#define CTAP_AUTH_DATA_FLAG_UP (1 << 0) /**< user present */
|
||||
#define CTAP_AUTH_DATA_FLAG_UV (1 << 2) /**< user verified */
|
||||
#define CTAP_AUTH_DATA_FLAG_AT (1 << 6) /**< attested credential data included */
|
||||
#define CTAP_AUTH_DATA_FLAG_ED (1 << 7) /**< extension data included */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name CTAP version flags
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#define CTAP_VERSION_FLAG_FIDO_PRE 0x01 /**< FIDO 2.1 flag */
|
||||
#define CTAP_VERSION_FLAG_FIDO 0x02 /**< FIDO 2 flag */
|
||||
#define CTAP_VERSION_FLAG_U2F_V2 0x04 /**< U2F V2 flag */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name CTAP get info response options map CBOR key values
|
||||
*
|
||||
* All options are in the form key-value pairs with string IDs and
|
||||
* boolean values
|
||||
* @{
|
||||
*/
|
||||
#define CTAP_GET_INFO_RESP_OPTIONS_ID_PLAT "plat" /**< platform device string */
|
||||
#define CTAP_GET_INFO_RESP_OPTIONS_ID_RK "rk" /**< resident key string */
|
||||
#define CTAP_GET_INFO_RESP_OPTIONS_ID_CLIENT_PIN "clientPin" /**< client PIN string */
|
||||
#define CTAP_GET_INFO_RESP_OPTIONS_ID_UP "up" /**< user presence string */
|
||||
#define CTAP_GET_INFO_RESP_OPTIONS_ID_UV "uv" /**< user verification string */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name CTAP get info options flags
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#define CTAP_INFO_OPTIONS_FLAG_PLAT (1 << 0) /**< platform device flag */
|
||||
#define CTAP_INFO_OPTIONS_FLAG_RK (1 << 1) /**< resident key flag */
|
||||
#define CTAP_INFO_OPTIONS_FLAG_CLIENT_PIN (1 << 2) /**< clientPIN flag */
|
||||
#define CTAP_INFO_OPTIONS_FLAG_UP (1 << 3) /**< user presence flag */
|
||||
#define CTAP_INFO_OPTIONS_FLAG_UV (1 << 4) /**< user verification flag */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name CTAP Client PIN request subCommand CBOR key values
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#define CTAP_CP_REQ_SUB_COMMAND_GET_RETRIES 0x01 /**< getRetries subCommand */
|
||||
#define CTAP_CP_REQ_SUB_COMMAND_GET_KEY_AGREEMENT 0x02 /**< getKeyAgreement subCommand */
|
||||
#define CTAP_CP_REQ_SUB_COMMAND_SET_PIN 0x03 /**< setPIN subCommand */
|
||||
#define CTAP_CP_REQ_SUB_COMMAND_CHANGE_PIN 0x04 /**< changePIN subCommand */
|
||||
#define CTAP_CP_REQ_SUB_COMMAND_GET_PIN_TOKEN 0x05 /**< getPinToken subCommand */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief CTAP thread stack size
|
||||
*/
|
||||
#ifdef CONFIG_FIDO2_CTAP_STACK_SIZE
|
||||
#define CTAP_STACKSIZE CONFIG_FIDO2_CTAP_STACK_SIZE
|
||||
#else
|
||||
#define CTAP_STACKSIZE 15000
|
||||
#endif
|
||||
|
||||
#if !IS_ACTIVE(CONFIG_FIDO2_CTAP_DISABLE_UP)
|
||||
/**
|
||||
* @brief CTAP user presence button
|
||||
*/
|
||||
#if defined(CONFIG_FIDO2_CTAP_UP_BUTTON_PORT) && defined(CONFIG_FIDO2_CTAP_UP_BUTTON_PIN) && \
|
||||
(CONFIG_FIDO2_CTAP_UP_BUTTON_PORT >= 0) && (CONFIG_FIDO2_CTAP_UP_BUTTON_PIN >= 0)
|
||||
#define CTAP_UP_BUTTON GPIO_PIN(CONFIG_FIDO2_CTAP_UP_BUTTON_PORT, CONFIG_FIDO2_CTAP_UP_BUTTON_PIN)
|
||||
#else
|
||||
/* set default button if no button is configured */
|
||||
#ifdef BTN0_PIN
|
||||
#define CTAP_UP_BUTTON BTN0_PIN
|
||||
/* if no button available disable UP test */
|
||||
#else
|
||||
#define CONFIG_FIDO2_CTAP_DISABLE_UP 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief CTAP user presence button mode
|
||||
*/
|
||||
#if IS_ACTIVE(CONFIG_FIDO2_CTAP_UP_BUTTON_MODE_IN_PU)
|
||||
#define CTAP_UP_BUTTON_MODE GPIO_IN_PU
|
||||
#elif IS_ACTIVE(CONFIG_FIDO2_CTAP_UP_BUTTON_MODE_IN_PD)
|
||||
#define CTAP_UP_BUTTON_MODE GPIO_IN_PD
|
||||
#elif IS_ACTIVE(CONFIG_FIDO2_CTAP_UP_BUTTON_MODE_IN)
|
||||
#define CTAP_UP_BUTTON_MODE GPIO_IN
|
||||
#else
|
||||
#define CTAP_UP_BUTTON_MODE GPIO_IN_PU
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief CTAP user presence button flank
|
||||
*/
|
||||
#if IS_ACTIVE(CONFIG_FIDO2_CTAP_UP_BUTTON_FLANK_FALLING)
|
||||
#define CTAP_UP_BUTTON_FLANK GPIO_FALLING
|
||||
#elif IS_ACTIVE(CONFIG_FIDO2_CTAP_UP_BUTTON_FLANK_RISING)
|
||||
#define CTAP_UP_BUTTON_FLANK GPIO_RISING
|
||||
#elif IS_ACTIVE(CONFIG_FIDO2_CTAP_UP_BUTTON_FLANK_BOTH)
|
||||
#define CTAP_UP_BUTTON_FLANK GPIO_BOTH
|
||||
#else
|
||||
#define CTAP_UP_BUTTON_FLANK GPIO_FALLING
|
||||
#endif
|
||||
|
||||
#endif /* !IS_ACTIVE(CONFIG_FIDO2_CTAP_DISABLE_UP) */
|
||||
|
||||
/**
|
||||
* @brief Max size of relying party name
|
||||
*/
|
||||
#define CTAP_RP_MAX_NAME_SIZE 32
|
||||
|
||||
/**
|
||||
* @brief Max size of username including null character
|
||||
*/
|
||||
#define CTAP_USER_MAX_NAME_SIZE 64 + 1
|
||||
|
||||
/**
|
||||
* @brief Max size of user id
|
||||
*/
|
||||
#define CTAP_USER_ID_MAX_SIZE 64
|
||||
|
||||
/**
|
||||
* @brief Max size of a domain name including null character
|
||||
*/
|
||||
#define CTAP_DOMAIN_NAME_MAX_SIZE 253 + 1
|
||||
|
||||
/**
|
||||
* @brief Max size of icon including null character
|
||||
*/
|
||||
#define CTAP_ICON_MAX_SIZE 128 + 1
|
||||
|
||||
/**
|
||||
* @brief PIN min size
|
||||
*/
|
||||
#define CTAP_PIN_MIN_SIZE 4
|
||||
|
||||
/**
|
||||
* @brief Encrypted newPin min size
|
||||
*
|
||||
* Encrypted PIN is padded with trailing 0x00 bytes to a minimum length
|
||||
* of 64 in order to prevent leak of PIN length.
|
||||
*/
|
||||
#define CTAP_PIN_ENC_MIN_SIZE 64
|
||||
|
||||
/**
|
||||
* @brief Encrypted newPin max size
|
||||
*
|
||||
*/
|
||||
#define CTAP_PIN_ENC_MAX_SIZE 256
|
||||
|
||||
/**
|
||||
* @brief PIN max size
|
||||
*/
|
||||
#define CTAP_PIN_MAX_SIZE 64
|
||||
|
||||
/**
|
||||
* @brief Max total consecutive incorrect PIN attempts
|
||||
*/
|
||||
#define CTAP_PIN_MAX_ATTS 8
|
||||
|
||||
/**
|
||||
* @brief Max consecutive incorrect PIN attempts for 1 boot cycle
|
||||
*/
|
||||
#define CTAP_PIN_MAX_ATTS_BOOT 3
|
||||
|
||||
/**
|
||||
* @brief PIN protocol version
|
||||
*/
|
||||
#define CTAP_PIN_PROT_VER 1
|
||||
|
||||
/**
|
||||
* @brief Total number of supported PIN protocol versions
|
||||
*/
|
||||
#define CTAP_AMT_SUP_PIN_VER 1
|
||||
|
||||
/**
|
||||
* @brief Size of pin token
|
||||
*
|
||||
* Needs to be a multiple of 16 bytes (AES block length).
|
||||
*/
|
||||
#define CTAP_PIN_TOKEN_SZ 16
|
||||
|
||||
/**
|
||||
* @brief Size of key used to encrypt credential
|
||||
*
|
||||
* Needed if authenticator is unable to store resident keys.
|
||||
* See webauthn specification (version 20190304) section 4 (Credential ID)
|
||||
* for details.
|
||||
*/
|
||||
#define CTAP_CRED_KEY_LEN 16
|
||||
|
||||
/**
|
||||
* @brief AES_CCM_L parameter
|
||||
*
|
||||
* L has to be between 2 and 8. Value of 2 means that message has to be
|
||||
* in the range 0 <= l(m) < 2^(16) = 65536.
|
||||
* This should always be sufficient to send an encrypted resident key.
|
||||
*/
|
||||
#define CTAP_AES_CCM_L 2
|
||||
|
||||
/**
|
||||
* @brief AES CCM nonce size
|
||||
*/
|
||||
#define CTAP_AES_CCM_NONCE_SIZE (15 - CTAP_AES_CCM_L)
|
||||
|
||||
/**
|
||||
* @brief Total size of AES CCM credential id
|
||||
*
|
||||
* Size of encrypted resident key = resident key - cred id - has_nonce
|
||||
*/
|
||||
#define CTAP_CREDENTIAL_ID_ENC_SIZE (sizeof(struct ctap_resident_key) - \
|
||||
sizeof(((struct ctap_resident_key *)0)-> \
|
||||
cred_desc.cred_id) - \
|
||||
sizeof(((struct ctap_resident_key *)0)-> \
|
||||
cred_desc.has_nonce))
|
||||
|
||||
/**
|
||||
* @brief Timeout for user presence test
|
||||
*/
|
||||
#ifdef CONFIG_FIDO2_CTAP_UP_TIMEOUT
|
||||
#define CTAP_UP_TIMEOUT (CONFIG_FIDO2_CTAP_UP_TIMEOUT * MS_PER_SEC)
|
||||
#else
|
||||
#define CTAP_UP_TIMEOUT (15 * MS_PER_SEC)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Max time between call to get_assertion or get_next_assertion until
|
||||
* error is returned
|
||||
*/
|
||||
#define CTAP_GET_NEXT_ASSERTION_TIMEOUT (30 * MS_PER_SEC)
|
||||
|
||||
/**
|
||||
* 128 bit identifier of authenticator
|
||||
*/
|
||||
#ifdef CONFIG_FIDO2_CTAP_DEVICE_AAGUID
|
||||
#define CTAP_AAGUID CONFIG_FIDO2_CTAP_DEVICE_AAGUID
|
||||
#else
|
||||
/* randomly generated fallback value */
|
||||
#define CTAP_AAGUID "9c295865fa2c36b705a42320af9c8f16"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @name CTAP credential types
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#define CTAP_PUB_KEY_CRED_PUB_KEY 0x01 /**< public key credential type */
|
||||
#define CTAP_PUB_KEY_CRED_UNKNOWN 0x02 /**< unknown credential type */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name CTAP COSE key CBOR map key values
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#define CTAP_COSE_KEY_LABEL_KTY 1 /**< key type identifier */
|
||||
#define CTAP_COSE_KEY_LABEL_ALG 3 /**< algorithm identifier */
|
||||
#define CTAP_COSE_KEY_LABEL_CRV -1 /**< elliptic curve identifier */
|
||||
#define CTAP_COSE_KEY_LABEL_X -2 /**< x coordinate */
|
||||
#define CTAP_COSE_KEY_LABEL_Y -3 /**< y coordinate */
|
||||
#define CTAP_COSE_KEY_KTY_EC2 2 /**< 2 coordinate elliptic curve key identifier */
|
||||
#define CTAP_COSE_KEY_CRV_P256 1 /**< secp256r1 elliptic curve key identifier */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief CTAP size of authenticator AAGUID in bytes
|
||||
*/
|
||||
#define CTAP_AAGUID_SIZE 16
|
||||
|
||||
/**
|
||||
* @brief CTAP COSE Algorithms registry identifier for ES256
|
||||
*/
|
||||
#define CTAP_COSE_ALG_ES256 -7
|
||||
|
||||
/**
|
||||
* @brief CTAP COSE Algorithms registry identifier for ECDH ES HKDF 256
|
||||
*/
|
||||
#define CTAP_COSE_ALG_ECDH_ES_HKDF_256 -25
|
||||
|
||||
/**
|
||||
* @brief CTAP size of credential id
|
||||
*
|
||||
*/
|
||||
#define CTAP_CREDENTIAL_ID_SIZE 16U
|
||||
|
||||
/**
|
||||
* @brief CTAP state initialized marker
|
||||
*
|
||||
* Used to check if authenticator state has already been initialized when
|
||||
* reading data from flash.
|
||||
*/
|
||||
#define CTAP_INITIALIZED_MARKER 0x4e
|
||||
|
||||
/**
|
||||
* @brief Max size of allow list
|
||||
*/
|
||||
#define CTAP_MAX_EXCLUDE_LIST_SIZE 0x10
|
||||
|
||||
/**
|
||||
* @brief CTAP cred struct forward declaration
|
||||
*/
|
||||
typedef struct ctap_cred_desc ctap_cred_desc_t;
|
||||
|
||||
/**
|
||||
* @brief Alternative CTAP cred struct forward declaration
|
||||
*/
|
||||
typedef struct ctap_cred_desc_alt ctap_cred_desc_alt_t;
|
||||
|
||||
/**
|
||||
* @brief CTAP resident key credential forward declaration
|
||||
*/
|
||||
typedef struct ctap_resident_key ctap_resident_key_t;
|
||||
|
||||
/**
|
||||
* @brief CTAP authenticator config struct
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t aaguid[CTAP_AAGUID_SIZE]; /**< AAGUID of device */
|
||||
uint8_t options; /**< options */
|
||||
} ctap_config_t;
|
||||
|
||||
/**
|
||||
* @brief CTAP state struct
|
||||
*
|
||||
* state of authenticator. Stored in flash memory
|
||||
*/
|
||||
typedef struct {
|
||||
ctap_config_t config; /**< configuration of authenticator */
|
||||
ctap_crypto_key_agreement_key_t ag_key; /**< Platform key agreement key */
|
||||
int rem_pin_att; /**< remaining PIN tries */
|
||||
uint16_t rk_amount_stored; /**< total number of resident keys stored on device */
|
||||
uint8_t initialized_marker; /**< CTAP initialized marker */
|
||||
uint8_t pin_hash[SHA256_DIGEST_LENGTH / 2]; /**< LEFT(SHA-256(pin), 16) */
|
||||
uint8_t cred_key[CTAP_CRED_KEY_LEN]; /**< AES CCM encryption key for cred */
|
||||
bool cred_key_is_initialized; /**< AES CCM key initialized flag */
|
||||
bool pin_is_set; /**< PIN is set or not */
|
||||
} ctap_state_t;
|
||||
|
||||
/**
|
||||
* @brief CTAP options struct
|
||||
*/
|
||||
typedef struct {
|
||||
int rk; /**< resident key */
|
||||
int uv; /**< user verification */
|
||||
int up; /**< user presence */
|
||||
} ctap_options_t;
|
||||
|
||||
/**
|
||||
* @brief CTAP user entity struct
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t id[CTAP_USER_ID_MAX_SIZE]; /**< RP-specific user account id */
|
||||
uint8_t id_len; /**< actual length of user id */
|
||||
uint8_t name[CTAP_USER_MAX_NAME_SIZE]; /**< user name */
|
||||
uint8_t display_name[CTAP_USER_MAX_NAME_SIZE]; /**< user display name */
|
||||
uint8_t icon[CTAP_DOMAIN_NAME_MAX_SIZE]; /**< URL referencing user icon image */
|
||||
} ctap_user_ent_t;
|
||||
|
||||
/**
|
||||
* @brief CTAP relying party entity struct
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t id[CTAP_DOMAIN_NAME_MAX_SIZE + 1]; /**< relying party identifier */
|
||||
uint8_t id_len; /**< actual length of
|
||||
relying party identifier */
|
||||
uint8_t name[CTAP_RP_MAX_NAME_SIZE + 1]; /**< human friendly relying
|
||||
party name */
|
||||
uint8_t icon[CTAP_DOMAIN_NAME_MAX_SIZE + 1]; /**< URL referencing relying
|
||||
party icon image */
|
||||
} ctap_rp_ent_t;
|
||||
|
||||
/**
|
||||
* @brief CTAP cose key struct
|
||||
*
|
||||
* https://www.iana.org/assignments/cose/cose.xhtml
|
||||
*/
|
||||
typedef struct {
|
||||
ctap_crypto_pub_key_t pubkey; /**< public key */
|
||||
int kty; /**< identification of key type */
|
||||
int crv; /**< EC identifier */
|
||||
int32_t alg_type; /**< COSEAlgorithmIdentifier */
|
||||
uint8_t cred_type; /**< type of credential */
|
||||
} ctap_public_key_cose_t;
|
||||
|
||||
/**
|
||||
* @brief CTAP credential description struct
|
||||
*
|
||||
* Webauthn specification (version 20190304) section 5.8.3
|
||||
*
|
||||
* @warning reordering this struct will break the AES CCM encryption of
|
||||
* resident keys.
|
||||
*/
|
||||
struct ctap_cred_desc {
|
||||
uint8_t cred_type; /**< type of credential */
|
||||
union {
|
||||
uint8_t cred_id[CTAP_CREDENTIAL_ID_SIZE]; /**< credential identifier */
|
||||
uint8_t nonce[CTAP_AES_CCM_NONCE_SIZE]; /**< CTAP AES CCM nonce */
|
||||
};
|
||||
bool has_nonce; /**< Indicate if nonce or
|
||||
cred_id */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief CTAP resident key struct
|
||||
*
|
||||
* A resident key is a fido2 credential that is being stored on the
|
||||
* authenticator.
|
||||
*/
|
||||
struct __attribute__((packed)) ctap_resident_key {
|
||||
uint8_t rp_id_hash[SHA256_DIGEST_LENGTH]; /**< hash of rp domain string */
|
||||
uint8_t user_id[CTAP_USER_ID_MAX_SIZE]; /**< id of user */
|
||||
uint8_t user_id_len; /**< length of the user id */
|
||||
uint8_t priv_key[CTAP_CRYPTO_KEY_SIZE]; /**< private key */
|
||||
uint32_t sign_count; /**< signature counter.
|
||||
See webauthn specification
|
||||
(version 20190304) section 6.1.1
|
||||
for details. */
|
||||
uint32_t creation_time; /**< timestamp for when credential
|
||||
was created */
|
||||
ctap_cred_desc_t cred_desc; /**< credential descriptor */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief CTAP credential ID
|
||||
*
|
||||
* Credential ID can either be 16 random bytes or the encrypted resident
|
||||
* key. (AES CCM cipher + mac + nonce used)
|
||||
*/
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t id[CTAP_CREDENTIAL_ID_ENC_SIZE]; /**< id */
|
||||
uint8_t mac[CCM_MAC_MAX_LEN]; /**< AES CCM MAC */
|
||||
uint8_t nonce[CTAP_AES_CCM_NONCE_SIZE]; /**< AES CCM nonce */
|
||||
} ctap_cred_id_t;
|
||||
|
||||
/**
|
||||
* @brief CTAP credential description alternative struct
|
||||
*
|
||||
* This struct is used when parsing an allow or exclude list.
|
||||
*/
|
||||
struct ctap_cred_desc_alt {
|
||||
uint8_t cred_type; /**< type of credential */
|
||||
ctap_cred_id_t cred_id; /**< credential id */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief CTAP make credential request struct
|
||||
*/
|
||||
typedef struct {
|
||||
ctap_cred_desc_alt_t exclude_list[CTAP_MAX_EXCLUDE_LIST_SIZE]; /**< exclude list */
|
||||
size_t exclude_list_len; /**< length of CBOR exclude list array */
|
||||
ctap_rp_ent_t rp; /**< relying party */
|
||||
ctap_user_ent_t user; /**< user */
|
||||
ctap_options_t options; /**< parameters to influence authenticator operation */
|
||||
uint8_t client_data_hash[SHA256_DIGEST_LENGTH]; /**< SHA-256 hash of JSON serialized client data */
|
||||
uint8_t pin_auth[CTAP_PIN_AUTH_SZ]; /**< pin_auth if PIN is set */
|
||||
size_t pin_auth_len; /**< pin_auth len */
|
||||
int32_t alg_type; /**< cryptographic algorithm identifier */
|
||||
bool pin_auth_present; /**< pin_auth present */
|
||||
uint8_t pin_protocol; /**< PIN protocol version */
|
||||
uint8_t cred_type; /**< type of credential */
|
||||
} ctap_make_credential_req_t;
|
||||
|
||||
/**
|
||||
* @brief CTAP get assertion request struct
|
||||
*/
|
||||
typedef struct {
|
||||
ctap_options_t options; /**< parameters to influence authenticator operation */
|
||||
ctap_cred_desc_alt_t allow_list[CTAP_MAX_EXCLUDE_LIST_SIZE]; /**< allow list */
|
||||
uint8_t client_data_hash[SHA256_DIGEST_LENGTH]; /**< SHA-256 hash of JSON serialized client data */
|
||||
uint8_t rp_id[CTAP_DOMAIN_NAME_MAX_SIZE + 1]; /**< Relying Party Identifier */
|
||||
uint8_t rp_id_len; /**< Actual Length of Relying Party Identifier */
|
||||
uint8_t allow_list_len; /**< length of CBOR allow list array */
|
||||
uint8_t pin_auth[CTAP_PIN_AUTH_SZ]; /**< pin_auth if PIN is set */
|
||||
size_t pin_auth_len; /**< pin_auth length */
|
||||
uint8_t pin_protocol; /**< PIN protocol version */
|
||||
bool pin_auth_present; /**< indicate if pin_auth present */
|
||||
} ctap_get_assertion_req_t;
|
||||
|
||||
/**
|
||||
* @brief CTAP client pin request struct
|
||||
*/
|
||||
typedef struct {
|
||||
ctap_public_key_cose_t key_agreement; /**< public key of platform_key_agreement_key*/
|
||||
uint16_t new_pin_enc_size; /**< size of encrypted new pin */
|
||||
uint8_t pin_auth[CTAP_PIN_AUTH_SZ]; /**< first 16 bytes of HMAC-SHA-256 of encrypted contents */
|
||||
uint8_t new_pin_enc[CTAP_PIN_ENC_MAX_SIZE]; /**< Encrypted new PIN using sharedSecret. */
|
||||
uint8_t pin_hash_enc[SHA256_DIGEST_LENGTH / 2]; /**< Encrypted first 16 bytes of SHA-256 of PIN using sharedSecret. */
|
||||
uint8_t sub_command; /**< authenticator Client PIN sub command */
|
||||
uint8_t pin_protocol; /**< PIN protocol version chosen by the client */
|
||||
bool pin_hash_enc_present; /**< indicate pin_hash_enc is present */
|
||||
bool pin_auth_present; /**< indicate if pin_auth present */
|
||||
bool key_agreement_present; /**< indicate if key_agreement present */
|
||||
} ctap_client_pin_req_t;
|
||||
|
||||
/**
|
||||
* @brief CTAP attested credential data header struct
|
||||
*
|
||||
* Defined for easier serialization
|
||||
*/
|
||||
typedef struct __attribute__((packed)){
|
||||
uint8_t aaguid[CTAP_AAGUID_SIZE]; /**< authenticator aaguid */
|
||||
uint8_t cred_len_h; /**< higher byte of credential length */
|
||||
uint8_t cred_len_l; /**< lower byte of credential length */
|
||||
ctap_cred_id_t cred_id; /**< credential id */
|
||||
} ctap_attested_cred_data_header_t;
|
||||
|
||||
/**
|
||||
* @brief CTAP attested credential data struct
|
||||
*/
|
||||
typedef struct {
|
||||
ctap_attested_cred_data_header_t header; /**< attested credential data header */
|
||||
ctap_public_key_cose_t key; /**< cose key */
|
||||
} ctap_attested_cred_data_t;
|
||||
|
||||
/**
|
||||
* @brief CTAP authenticator data header struct
|
||||
*
|
||||
* Defined for easier serialization
|
||||
*/
|
||||
typedef struct __attribute__((packed)){
|
||||
uint8_t rp_id_hash[SHA256_DIGEST_LENGTH]; /**< hash of relying party id */
|
||||
uint8_t flags; /**< flags indicating result of user verification */
|
||||
uint32_t sign_count; /**< sign count of credential */
|
||||
} ctap_auth_data_header_t;
|
||||
|
||||
/**
|
||||
* @brief CTAP authenticator data struct
|
||||
*/
|
||||
typedef struct {
|
||||
ctap_auth_data_header_t header; /**< auth data header */
|
||||
ctap_attested_cred_data_t attested_cred_data; /**< attested credential data */
|
||||
} ctap_auth_data_t;
|
||||
|
||||
/**
|
||||
* @brief CTAP info struct
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t max_msg_size; /**< max message size */
|
||||
uint8_t aaguid[CTAP_AAGUID_SIZE]; /**< AAGUID */
|
||||
uint8_t versions; /**< supported versions of FIDO */
|
||||
uint8_t options; /**< supported options */
|
||||
uint8_t pin_protocol; /**< supported PIN protocol versions */
|
||||
bool pin_is_set; /**< PIN is set or not */
|
||||
} ctap_info_t;
|
||||
|
||||
/**
|
||||
* @brief Create signature from authenticator data
|
||||
*
|
||||
* Used for attestation and assertion statement.
|
||||
*
|
||||
* @param[in] auth_data authenticator data
|
||||
* @param[in] auth_data_len length of @p auth_data
|
||||
* @param[in] client_data_hash hash of client data sent by relying party in request
|
||||
* @param[in] rk resident key used to sign the data
|
||||
* @param[in] sig signature buffer
|
||||
* @param[in] sig_len length of @p sig
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_get_sig(const uint8_t *auth_data, size_t auth_data_len,
|
||||
const uint8_t *client_data_hash,
|
||||
const ctap_resident_key_t *rk,
|
||||
uint8_t *sig, size_t *sig_len);
|
||||
|
||||
/**
|
||||
* @brief Check if requested algorithm is supported
|
||||
*
|
||||
* @param[in] cred_type type of credential
|
||||
* @param[in] alg_type cryptographic algorithm identifier
|
||||
*
|
||||
* @return true if algorithm is supported
|
||||
* @return false otherwise
|
||||
*/
|
||||
bool fido2_ctap_cred_params_supported(uint8_t cred_type, int32_t alg_type);
|
||||
|
||||
/**
|
||||
* @brief Encrypt resident key with AES CCM
|
||||
*
|
||||
* @param[in] rk type of credential
|
||||
* @param[in] nonce CCM nonce
|
||||
* @param[in] nonce_len length of @p nonce
|
||||
* @param[in] id credential id struct storing encrypted resident key
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_encrypt_rk(ctap_resident_key_t *rk, uint8_t *nonce,
|
||||
size_t nonce_len, ctap_cred_id_t *id);
|
||||
|
||||
/**
|
||||
* @brief Check if PIN has been set on authenticator
|
||||
*
|
||||
* @return true if PIN has been set
|
||||
* @return false otherwise
|
||||
*/
|
||||
bool fido2_ctap_pin_is_set(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* FIDO2_CTAP_CTAP_H */
|
||||
/** @} */
|
||||
338
sys/include/fido2/ctap/ctap_cbor.h
Normal file
338
sys/include/fido2/ctap/ctap_cbor.h
Normal file
@ -0,0 +1,338 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup fido2_ctap_cbor FIDO2 CTAP CBOR
|
||||
* @ingroup fido2_ctap
|
||||
* @brief FIDO2 CTAP CBOR helper
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief CTAP CBOR helper function declarations
|
||||
*
|
||||
* @author Nils Ollrogge <nils.ollrogge@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef FIDO2_CTAP_CTAP_CBOR_H
|
||||
#define FIDO2_CTAP_CTAP_CBOR_H
|
||||
|
||||
#include "fido2/ctap/ctap.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief CBOR map size of encoded getInfo response
|
||||
*/
|
||||
#define CTAP_CBOR_INFO_MAP_SZ 0x06
|
||||
|
||||
/**
|
||||
* @brief CBOR map size of encoded attestation
|
||||
*/
|
||||
#define CTAP_CBOR_ATTESTATION_MAP_SZ 0x03
|
||||
|
||||
/**
|
||||
* @brief CBOR map size of encoded attestation statement
|
||||
*/
|
||||
#define CTAP_CBOR_ATTESTATION_STMT_MAP_SZ 0x02
|
||||
|
||||
/**
|
||||
* @brief CBOR map size of encoded credential description
|
||||
*/
|
||||
#define CTAP_CBOR_CRED_DESC_MAP_SZ 0x02
|
||||
|
||||
/**
|
||||
* @brief CBOR map size of encoded pinToken
|
||||
*/
|
||||
#define CTAP_CBOR_PIN_TOKEN_MAP_SZ 0x01
|
||||
|
||||
/**
|
||||
* @brief CBOR map size of encoded user entity
|
||||
*/
|
||||
#define CTAP_CBOR_USER_ENTITY_MAP_SZ 0x01
|
||||
|
||||
/**
|
||||
* @brief CBOR map size of public key encoded in COSE format
|
||||
*/
|
||||
#define CTAP_CBOR_COSE_KEY_MAP_SZ 0x05
|
||||
|
||||
/**
|
||||
* @brief CBOR map size of encoded clientPIN keyAgreement
|
||||
*/
|
||||
#define CTAP_CBOR_KEY_AGREEMENT_MAP_SZ 0x01
|
||||
|
||||
/**
|
||||
* @brief CBOR map size of encoded clientPIN retries
|
||||
*/
|
||||
#define CTAP_CBOR_RETRIES_MAP_SZ 0x01
|
||||
|
||||
/**
|
||||
* @brief Attestation statement data buffer size
|
||||
*/
|
||||
#define CTAP_CBOR_ATT_STMT_AUTH_DATA_SZ 0x134
|
||||
|
||||
/**
|
||||
* @brief Max length of string key in CBOR map
|
||||
*/
|
||||
#define CTAP_CBOR_MAP_MAX_KEY_LEN 0x10
|
||||
|
||||
/**
|
||||
* @brief Max length of PublicKeyCredentialType string
|
||||
*/
|
||||
#define CTAP_CBOR_MAX_CREDENTIAL_TYPE_LEN 0x10
|
||||
|
||||
/**
|
||||
* @name CTAP CBOR map key string values
|
||||
* @{
|
||||
*/
|
||||
#define CTAP_CBOR_STR_PACKED "packed" /**< packed key string */
|
||||
#define CTAP_CBOR_STR_ALG "alg" /**< algorithm key string */
|
||||
#define CTAP_CBOR_STR_SIG "sig" /**< signature key string */
|
||||
#define CTAP_CBOR_STR_ID "id" /**< id key string */
|
||||
#define CTAP_CBOR_STR_TYPE "type" /**< type key string */
|
||||
#define CTAP_CBOR_STR_PUBLIC_KEY "public-key" /**< public-key key string */
|
||||
#define CTAP_CBOR_STR_USER_VERIFIED "uv" /**< user verification key string */
|
||||
#define CTAP_CBOR_STR_USER_PRESENT "up" /**< user presence key string */
|
||||
#define CTAP_CBOR_STR_RESIDENT_KEY "rk" /**< resident key key string */
|
||||
#define CTAP_CBOR_STR_NAME "name" /**< name key string */
|
||||
#define CTAP_CBOR_STR_ICON "icon" /**< icon key string */
|
||||
#define CTAP_CBOR_DISPLAY_NAME "displayName" /**< displayName key string */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name CTAP Client PIN response CBOR map key values
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#define CTAP_CBOR_CP_RESP_KEY_AGREEMENT 0x01 /**< KeyAgreement key value */
|
||||
#define CTAP_CBOR_CP_PIN_TOKEN_RESP 0x02 /**< pinToken key value */
|
||||
#define CTAP_CBOR_CP_RETRIES_RESP 0x03 /**< retries key value */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name CTAP make credential request CBOR key values
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#define CTAP_CBOR_MC_REQ_CLIENT_DATA_HASH 0x01 /**< clientDataHash key value */
|
||||
#define CTAP_CBOR_MC_REQ_RP 0x02 /**< relying party key value */
|
||||
#define CTAP_CBOR_MC_REQ_USER 0x03 /**< user key value */
|
||||
#define CTAP_CBOR_MC_REQ_PUB_KEY_CRED_PARAMS 0x04 /**< pubKeyCredParams key value */
|
||||
#define CTAP_CBOR_MC_REQ_EXCLUDE_LIST 0x05 /**< excludeList key value */
|
||||
#define CTAP_CBOR_MC_REQ_EXTENSIONS 0x06 /**< extensions key value */
|
||||
#define CTAP_CBOR_MC_REQ_OPTIONS 0x07 /**< options key value */
|
||||
#define CTAP_CBOR_MC_REQ_PIN_AUTH 0x08 /**< pinAuth key value */
|
||||
#define CTAP_CBOR_MC_REQ_PIN_PROTOCOL 0x09 /**< pinProtocol key value */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name CTAP get info response CBOR key values
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#define CTAP_CBOR_GET_INFO_RESP_VERSIONS 0x01 /**< versions key value */
|
||||
#define CTAP_CBOR_GET_INFO_RESP_EXTENSIONS 0x02 /**< extensions key value */
|
||||
#define CTAP_CBOR_GET_INFO_RESP_AAGUID 0x03 /**< AAGUID key value */
|
||||
#define CTAP_CBOR_GET_INFO_RESP_OPTIONS 0x04 /**< options key value */
|
||||
#define CTAP_CBOR_GET_INFO_RESP_MAX_MSG_SIZE 0x05 /**< maxMsgSize key value */
|
||||
#define CTAP_CBOR_GET_INFO_RESP_PIN_PROTOCOLS 0x06 /**< pinProtocol key value */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name CTAP version strings
|
||||
* @{
|
||||
*/
|
||||
#define CTAP_CBOR_VERSION_STRING_FIDO_PRE "FIDO_2_1_PRE" /**< FIDO 2.1 flag */
|
||||
#define CTAP_CBOR_VERSION_STRING_FIDO "FIDO_2_0" /**< FIDO 2 flag */
|
||||
#define CTAP_CBOR_VERSION_STRING_U2F_V2 "U2F_V2" /**< U2F V2 flag */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name CTAP make credential response CBOR key values
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#define CTAP_CBOR_MC_RESP_FMT 0x01 /**< attestation statement format identifier key value */
|
||||
#define CTAP_CBOR_MC_RESP_AUTH_DATA 0x02 /**< authData key value */
|
||||
#define CTAP_CBOR_MC_RESP_ATT_STMT 0x03 /**< attestation statement key value */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name CTAP get assertion request CBOR key values
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#define CTAP_CBOR_GA_REQ_RP_ID 0x01 /**< relying party identifier key value */
|
||||
#define CTAP_CBOR_GA_REQ_CLIENT_DATA_HASH 0x02 /**< clientDataHash key value */
|
||||
#define CTAP_CBOR_GA_REQ_ALLOW_LIST 0x03 /**< allowList key value */
|
||||
#define CTAP_CBOR_GA_REQ_EXTENSIONS 0x04 /**< extensions key value */
|
||||
#define CTAP_CBOR_GA_REQ_OPTIONS 0x05 /**< options key value */
|
||||
#define CTAP_CBOR_GA_REQ_PIN_AUTH 0x06 /**< pinAuth key value */
|
||||
#define CTAP_CBOR_GA_REQ_PIN_PROTOCOL 0x07 /**< pinProtocol key value */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name CTAP get assertion response CBOR key values
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#define CTAP_CBOR_GA_RESP_CREDENTIAL 0x01 /**< credential key value */
|
||||
#define CTAP_CBOR_GA_RESP_AUTH_DATA 0x02 /**< authData key value */
|
||||
#define CTAP_CBOR_GA_RESP_SIGNATURE 0x03 /**< signature key value */
|
||||
#define CTAP_CBOR_GA_RESP_USER 0x04 /**< user key value */
|
||||
#define CTAP_CBOR_GA_RESP_NUMBER_OF_CREDENTIALS 0x05 /**< numberOfCredentials key value */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name CTAP Client PIN request CBOR key values
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#define CTAP_CBOR_CP_REQ_PIN_PROTOCOL 0x01 /**< pinProtocol key value */
|
||||
#define CTAP_CBOR_CP_REQ_SUB_COMMAND 0x02 /**< subCommand key value */
|
||||
#define CTAP_CBOR_CP_REQ_KEY_AGREEMENT 0x03 /**< keyAgreement key value */
|
||||
#define CTAP_CBOR_CP_REQ_PIN_AUTH 0x04 /**< pinAuth key value */
|
||||
#define CTAP_CBOR_CP_REQ_NEW_PIN_ENC 0x05 /**< newPinEnc key value */
|
||||
#define CTAP_CBOR_CP_REQ_PIN_HASH_ENC 0x06 /**< pinHashEnc key value */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Parse MakeCredential method
|
||||
*
|
||||
* CTAP specification (version 20190130) section 5.1
|
||||
*
|
||||
* @param[in] req struct to parse into
|
||||
* @param[in] req_raw raw request
|
||||
* @param[in] len length of @p req_raw
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_cbor_parse_make_credential_req(ctap_make_credential_req_t *req,
|
||||
const uint8_t *req_raw, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Parse GetAssertion method
|
||||
*
|
||||
* CTAP specification (version 20190130) section 5.2
|
||||
*
|
||||
* @param[in] req struct to parse into
|
||||
* @param[in] req_raw raw request
|
||||
* @param[in] len length of @p req_raw
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_cbor_parse_get_assertion_req(ctap_get_assertion_req_t *req,
|
||||
const uint8_t *req_raw, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Encode CBOR info map
|
||||
*
|
||||
* CTAP specification (version 20190130) section 5.4
|
||||
*
|
||||
* @param[in] info information about capabilities
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
|
||||
int fido2_ctap_cbor_encode_info(const ctap_info_t *info);
|
||||
/**
|
||||
* @brief Parse ClientPIN method
|
||||
*
|
||||
* CTAP specification (version 20190130) section 5.5
|
||||
*
|
||||
* @param[in] req struct to parse into
|
||||
* @param[in] req_raw raw request
|
||||
* @param[in] len length of @p req_raw
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_cbor_parse_client_pin_req(ctap_client_pin_req_t *req,
|
||||
const uint8_t *req_raw, size_t len);
|
||||
/**
|
||||
* @brief Encode attestation object
|
||||
*
|
||||
* Webauthn specification (version 20190304) section 6.5
|
||||
*
|
||||
* @param[in] auth_data authenticator data
|
||||
* @param[in] client_data_hash SHA-256 hash of JSON serialized client data
|
||||
* @param[in] rk resident key
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_cbor_encode_attestation_object(const ctap_auth_data_t *auth_data,
|
||||
const uint8_t *client_data_hash,
|
||||
ctap_resident_key_t *rk);
|
||||
|
||||
/**
|
||||
* @brief Encode assertion object
|
||||
*
|
||||
* CTAP specification (version 20190130) section 5.2
|
||||
*
|
||||
* @param[in] auth_data authenticator data header
|
||||
* @param[in] client_data_hash SHA-256 hash of JSON serialized client data
|
||||
* @param[in] rk resident key
|
||||
* @param[in] valid_cred_count amount of valid credentials found in allow list
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_cbor_encode_assertion_object(const ctap_auth_data_header_t *auth_data,
|
||||
const uint8_t *client_data_hash,
|
||||
ctap_resident_key_t *rk,
|
||||
uint8_t valid_cred_count);
|
||||
/**
|
||||
* @brief Encode key agreement
|
||||
*
|
||||
* @param[in] key Public key in COSE format
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_cbor_encode_key_agreement(const ctap_public_key_cose_t *key);
|
||||
|
||||
/**
|
||||
* @brief Encode encrypted pin token
|
||||
*
|
||||
* @param[in] token encrypted pin token
|
||||
* @param[in] len length of @p token
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_cbor_encode_pin_token(uint8_t *token, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Encode PIN tries left
|
||||
*
|
||||
* @param[in] tries_left amount of tries left
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_cbor_encode_retries(uint8_t tries_left);
|
||||
|
||||
/**
|
||||
* @brief Get size of CBOR encoded data
|
||||
*
|
||||
* @param[in] buf Buffer holding the data
|
||||
*
|
||||
* @return size of CBOR encoded data
|
||||
*/
|
||||
size_t fido2_ctap_cbor_get_buffer_size(const uint8_t *buf);
|
||||
|
||||
/**
|
||||
* @brief Initialize CBOR encoder
|
||||
*
|
||||
* @param[in] buf Buffer to hold CBOR encoded data
|
||||
* @param[in] len Length of @p buf
|
||||
*/
|
||||
void fido2_ctap_cbor_init_encoder(uint8_t *buf, size_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* FIDO2_CTAP_CTAP_CBOR_H */
|
||||
/** @} */
|
||||
301
sys/include/fido2/ctap/ctap_crypto.h
Normal file
301
sys/include/fido2/ctap/ctap_crypto.h
Normal file
@ -0,0 +1,301 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup fido2_ctap_crypto FIDO2 CTAP crypto
|
||||
* @ingroup fido2_ctap
|
||||
* @brief FIDO2 CTAP crypto helper
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief FIDO2 CTAP crypto helper defines, structures and function
|
||||
* declarations.
|
||||
*
|
||||
* @author Nils Ollrogge <nils.ollrogge@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef FIDO2_CTAP_CTAP_CRYPTO_H
|
||||
#define FIDO2_CTAP_CTAP_CRYPTO_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hashes/sha256.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Size in bytes of cryptographic keys used
|
||||
*/
|
||||
#define CTAP_CRYPTO_KEY_SIZE 32
|
||||
|
||||
/**
|
||||
* @brief Max size of ES256 signature in ASN.1 DER format
|
||||
*/
|
||||
#define CTAP_CRYPTO_ES256_DER_MAX_SIZE 72
|
||||
|
||||
/**
|
||||
* @brief Elliptic curve public key
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t x[CTAP_CRYPTO_KEY_SIZE]; /**< x coordinate of curve point */
|
||||
uint8_t y[CTAP_CRYPTO_KEY_SIZE]; /**< y coordinate of curve point */
|
||||
} ctap_crypto_pub_key_t;
|
||||
|
||||
/**
|
||||
* @brief Key agreement key
|
||||
*
|
||||
* CTAP specification (version 20190130) section 5.5.4
|
||||
*/
|
||||
typedef struct {
|
||||
ctap_crypto_pub_key_t pub; /**< public key */
|
||||
uint8_t priv[CTAP_CRYPTO_KEY_SIZE]; /**< private key */
|
||||
} ctap_crypto_key_agreement_key_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize crypto helper
|
||||
*
|
||||
* Initializes crypto libs and creates key_agreement key pair
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_crypto_init(void);
|
||||
|
||||
/**
|
||||
* @brief Wrapper function for @ref random_bytes
|
||||
*
|
||||
* @param[in] buf buffer to hold random bytes
|
||||
* @param[in] len length of @p buf
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_crypto_prng(uint8_t *buf, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Wrapper function for @ref sha256_init
|
||||
*
|
||||
* @param ctx sha256_context_t handle to init
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_crypto_sha256_init(sha256_context_t *ctx);
|
||||
|
||||
/**
|
||||
* @brief Wrapper function for @ref sha256_update
|
||||
*
|
||||
* @param ctx sha256_context_t handle to use
|
||||
* @param[in] data Input data
|
||||
* @param[in] len Length of @p data
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_crypto_sha256_update(sha256_context_t *ctx, const void *data, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Wrapper for @ref sha256_final
|
||||
*
|
||||
* @param ctx sha256_context_t handle to use
|
||||
* @param digest resulting digest, this is the hash of all the bytes
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_crypto_sha256_final(sha256_context_t *ctx, void *digest);
|
||||
|
||||
/**
|
||||
* @brief Wrapper function for @ref sha256
|
||||
*
|
||||
* @param[in] data pointer to the buffer to generate hash from
|
||||
* @param[in] len length of @p data
|
||||
* @param[out] digest optional pointer to an array for the result, length must
|
||||
* be SHA256_DIGEST_LENGTH
|
||||
*
|
||||
* @note discards the pointer returned by @ref sha256
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_crypto_sha256(const void *data, size_t len,
|
||||
void *digest);
|
||||
|
||||
/**
|
||||
* @brief Wrapper function for @ref hmac_sha256_init
|
||||
*
|
||||
* @param[in] ctx hmac_context_t handle to use
|
||||
* @param[in] key key used in the hmac-sha256 computation
|
||||
* @param[in] key_length length of @p key
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_crypto_hmac_sha256_init(hmac_context_t *ctx, const void *key,
|
||||
size_t key_length);
|
||||
|
||||
/**
|
||||
* @brief Wrapper function for @ref hmac_sha256_update
|
||||
*
|
||||
* @param[in] ctx hmac_context_t handle to use
|
||||
* @param[in] data pointer to the buffer to generate hash from
|
||||
* @param[in] len length of @p data
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_crypto_hmac_sha256_update(hmac_context_t *ctx, const void *data, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Wrapper function for @ref hmac_sha256_final
|
||||
*
|
||||
* @param[in] ctx hmac_context_t handle to use
|
||||
* @param[out] digest the computed hmac-sha256,
|
||||
* length MUST be SHA256_DIGEST_LENGTH
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_crypto_hmac_sha256_final(hmac_context_t *ctx, void *digest);
|
||||
|
||||
/**
|
||||
* @brief Wrapper function for @ref hmac_sha256
|
||||
*
|
||||
* @param[in] key key used in the hmac-sha256 computation
|
||||
* @param[in] key_length length of @p key
|
||||
* @param[in] data pointer to the buffer to generate the hmac-sha256
|
||||
* @param[in] len length of @p data
|
||||
* @param[out] digest the computed hmac-sha256,
|
||||
* length MUST be SHA256_DIGEST_LENGTH
|
||||
*
|
||||
* @note discards the pointer returned by @ref hmac_sha256
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_crypto_hmac_sha256(const void *key,
|
||||
size_t key_length, const void *data, size_t len,
|
||||
void *digest);
|
||||
|
||||
/**
|
||||
* @brief Generate cryptographic key pair
|
||||
*
|
||||
* @param[in] pub_key public key buffer
|
||||
* @param[in] priv_key private key buffer
|
||||
* @param[in] len length of @p priv_key
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_crypto_gen_keypair(ctap_crypto_pub_key_t *pub_key, uint8_t *priv_key, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Elliptic-curve Diffie-Hellmann
|
||||
*
|
||||
* @param[in] out shared secret buffer
|
||||
* @param[in] len length of @p out
|
||||
* @param[in] pub_key public key of other party
|
||||
* @param[in] priv_key private key
|
||||
* @param[in] key_len length of @p priv_key
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_crypto_ecdh(uint8_t *out, size_t len,
|
||||
ctap_crypto_pub_key_t *pub_key, uint8_t *priv_key, size_t key_len);
|
||||
|
||||
/**
|
||||
* @brief Create cryptographic signature
|
||||
*
|
||||
* @param[in] hash Hash to be signed
|
||||
* @param[in] hash_len length of @p hash
|
||||
* @param[in] sig signature buffer
|
||||
* @param[in] sig_len length of @p sig
|
||||
* @param[in] key private key to use for signature
|
||||
* @param[in] key_len length of @p key
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_crypto_get_sig(uint8_t *hash, size_t hash_len, uint8_t *sig,
|
||||
size_t *sig_len, const uint8_t *key, size_t key_len);
|
||||
|
||||
/**
|
||||
* @brief Encrypt data using AES-256-CBC
|
||||
*
|
||||
* @param[in] out encrypted data
|
||||
* @param[in] out_len length of @p out
|
||||
* @param[in] in data to be encrypted
|
||||
* @param[in] in_len length of @p in
|
||||
* @param[in] key symmetric key to use for encryption
|
||||
* @param[in] key_len length of @p key
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_crypto_aes_enc(uint8_t *out, size_t *out_len, uint8_t * in,
|
||||
size_t in_len, const uint8_t * key, size_t key_len);
|
||||
|
||||
/**
|
||||
* @brief Decrypt data using AES-256-CBC
|
||||
*
|
||||
* @param[in] out decrypted data
|
||||
* @param[in] out_len length of @p out
|
||||
* @param[in] in encrypted data
|
||||
* @param[in] in_len len of @p in
|
||||
* @param[in] key symmetric key to use for decryption
|
||||
* @param[in] key_len length of @p key
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_crypto_aes_dec(uint8_t *out, size_t *out_len, uint8_t * in,
|
||||
size_t in_len, const uint8_t * key, size_t key_len);
|
||||
|
||||
/**
|
||||
* @brief Encrypt data using AES-128-CCM
|
||||
*
|
||||
* @param[in] out encrypted data
|
||||
* @param[in] out_len length of @p out
|
||||
* @param[in] in data to be encrypted
|
||||
* @param[in] in_len length of @p in
|
||||
* @param[in] auth_data additional data to authenticate in MAC
|
||||
* @param[in] auth_data_len length of @p auth_data
|
||||
* @param[in] mac_len length of appended MAC
|
||||
* @param[in] length_encoding max supported length of plaintext
|
||||
* @param[in] nonce nonce for ctr mode encryption
|
||||
* @param[in] nonce_len length of @p nonce
|
||||
* @param[in] key symmetric key to use for encryption
|
||||
* @param[in] key_len length of @p key
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_crypto_aes_ccm_enc(uint8_t *out, size_t out_len,
|
||||
const uint8_t *in, size_t in_len,
|
||||
uint8_t *auth_data, size_t auth_data_len,
|
||||
uint8_t mac_len, uint8_t length_encoding,
|
||||
const uint8_t *nonce, size_t nonce_len,
|
||||
const uint8_t *key, size_t key_len);
|
||||
|
||||
/**
|
||||
* @brief Encrypt data using AES-128-CCM
|
||||
*
|
||||
* @param[in] out encrypted data
|
||||
* @param[in] out_len length of @p out
|
||||
* @param[in] in data to be encrypted
|
||||
* @param[in] in_len length of @p in
|
||||
* @param[in] auth_data additional data to authenticate in MAC
|
||||
* @param[in] auth_data_len length of @p auth_data
|
||||
* @param[in] mac_len length of appended MAC
|
||||
* @param[in] length_encoding max supported length of plaintext
|
||||
* @param[in] nonce nonce for ctr mode encryption
|
||||
* @param[in] nonce_len length of @p nonce
|
||||
* @param[in] key symmetric key to use for encryption
|
||||
* @param[in] key_len length of @p key
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_crypto_aes_ccm_dec(uint8_t *out, size_t out_len,
|
||||
const uint8_t *in, size_t in_len,
|
||||
uint8_t *auth_data, size_t auth_data_len,
|
||||
uint8_t mac_len, uint8_t length_encoding,
|
||||
const uint8_t *nonce, size_t nonce_len,
|
||||
const uint8_t *key, size_t key_len);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* FIDO2_CTAP_CTAP_CRYPTO_H */
|
||||
/** @} */
|
||||
162
sys/include/fido2/ctap/ctap_mem.h
Normal file
162
sys/include/fido2/ctap/ctap_mem.h
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup fido2_ctap_mem FIDO2 CTAP flash
|
||||
* @ingroup fido2_ctap
|
||||
* @brief FIDO2 CTAP flash memory helper
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Definitions for CTAP flash memory helper functions
|
||||
*
|
||||
* @author Nils Ollrogge <nils.ollrogge@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef FIDO2_CTAP_CTAP_MEM_H
|
||||
#define FIDO2_CTAP_CTAP_MEM_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "fido2/ctap/ctap.h"
|
||||
#include "periph/flashpage.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief MAX function for internal use
|
||||
* @{
|
||||
*/
|
||||
#ifndef _MAX
|
||||
#define _MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief First flash page to store data in
|
||||
*
|
||||
* @note This can corrupt firmware if CTAP_FLASH_START_PAGE is set to a
|
||||
* flash page containing firmware. Therefore make sure that CTAP_FLASH_START_PAGE
|
||||
* is located after the firmware.
|
||||
*/
|
||||
#if defined(CONFIG_FIDO2_CTAP_FLASH_START_PAGE) && \
|
||||
(CONFIG_FIDO2_CTAP_FLASH_START_PAGE >= 0)
|
||||
#define CTAP_FLASH_START_PAGE CONFIG_FIDO2_CTAP_FLASH_START_PAGE
|
||||
#else
|
||||
#define CTAP_FLASH_START_PAGE (FLASHPAGE_NUMOF - 4)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Start page for storing resident keys
|
||||
*/
|
||||
#define CTAP_FLASH_RK_START_PAGE CTAP_FLASH_START_PAGE
|
||||
|
||||
/**
|
||||
* @brief Page for storing authenticator state information
|
||||
*/
|
||||
#define CTAP_FLASH_STATE_PAGE CTAP_FLASH_RK_START_PAGE - 1
|
||||
|
||||
/**
|
||||
* @brief Calculate padding needed to align struct size for saving to flash
|
||||
*/
|
||||
#define CTAP_FLASH_ALIGN_PAD(x) (sizeof(x) % FLASHPAGE_WRITE_BLOCK_SIZE == \
|
||||
0 ? \
|
||||
0 : FLASHPAGE_WRITE_BLOCK_SIZE - \
|
||||
sizeof(x) % FLASHPAGE_WRITE_BLOCK_SIZE)
|
||||
|
||||
/**
|
||||
* @brief Resident key size with alignment padding
|
||||
*/
|
||||
#define CTAP_FLASH_RK_SZ (sizeof(ctap_resident_key_t) + \
|
||||
CTAP_FLASH_ALIGN_PAD(ctap_resident_key_t))
|
||||
|
||||
/**
|
||||
* @brief State struct size with alignment padding
|
||||
*/
|
||||
#define CTAP_FLASH_STATE_SZ (sizeof(ctap_state_t) + \
|
||||
CTAP_FLASH_ALIGN_PAD(ctap_state_t))
|
||||
|
||||
/**
|
||||
* @brief Minimum flash sector size needed to hold CTAP related data
|
||||
*
|
||||
* This is needed to ensure that the MTD work_area buffer is big enough
|
||||
*/
|
||||
#define CTAP_FLASH_MIN_SECTOR_SZ _MAX(CTAP_FLASH_STATE_SZ, CTAP_FLASH_RK_SZ)
|
||||
|
||||
/**
|
||||
* @brief Pages per sector needed
|
||||
*/
|
||||
#define CTAP_FLASH_PAGES_PER_SECTOR ((CTAP_FLASH_MIN_SECTOR_SZ / FLASHPAGE_SIZE) + 1)
|
||||
|
||||
/**
|
||||
* @brief Initialize memory helper
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_mem_init(void);
|
||||
|
||||
/**
|
||||
* @brief Write to flash memory
|
||||
*
|
||||
* @param[in] buf buffer to write
|
||||
* @param[in] page page to write to
|
||||
* @param[in] offset offset from the start of the page (in bytes)
|
||||
* @param[in] len number of bytes to write
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_mem_write(const void *buf, uint32_t page, uint32_t offset, uint32_t len);
|
||||
|
||||
/**
|
||||
* @brief Read from flash memory
|
||||
*
|
||||
* @param[out] buf buffer to fil in
|
||||
* @param[in] page page to read from
|
||||
* @param[in] offset offset from the start of the page (in bytes)
|
||||
* @param[in] len number of bytes to write
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_mem_read(void *buf, uint32_t page, uint32_t offset, uint32_t len);
|
||||
|
||||
/**
|
||||
* @brief Get maximum amount of resident credentials that can be stored
|
||||
*
|
||||
* @return maximum amount that can be stored
|
||||
*/
|
||||
uint16_t fido2_ctap_mem_get_max_rk_amount(void);
|
||||
|
||||
/**
|
||||
* @brief Get flashpage number resident key with index @p rk_idx.
|
||||
*
|
||||
* @param[in] rk_idx index of resident key
|
||||
*
|
||||
* @return page number if no error
|
||||
* @return -1 if @p rk_idx is invalid
|
||||
*/
|
||||
int fido2_ctap_mem_get_flashpage_number_of_rk(uint16_t rk_idx);
|
||||
|
||||
/**
|
||||
* @brief Get offset of resident key into flashpage where flashpage =
|
||||
* fido2_ctap_mem_get_flashpage_number_of_r(i)
|
||||
*
|
||||
* @param[in] rk_idx index of resident key
|
||||
*
|
||||
* @return page number if no error
|
||||
* @return -1 if @p rk_idx is invalid
|
||||
*/
|
||||
int fido2_ctap_mem_get_offset_of_rk_into_flashpage(uint16_t rk_idx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* FIDO2_CTAP_CTAP_MEM_H */
|
||||
/** @} */
|
||||
91
sys/include/fido2/ctap/ctap_utils.h
Normal file
91
sys/include/fido2/ctap/ctap_utils.h
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup fido2_ctap_utils FIDO2 CTAP utils
|
||||
* @ingroup fido2_ctap
|
||||
* @brief FIDO2 CTAP utility helper
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Definition for CTAP utility functions
|
||||
*
|
||||
* @author Nils Ollrogge <nils.ollrogge@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef FIDO2_CTAP_CTAP_UTILS_H
|
||||
#define FIDO2_CTAP_CTAP_UTILS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "fido2/ctap/ctap.h"
|
||||
#include "periph/gpio.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief LED animation to indicate that user action is required
|
||||
*/
|
||||
void fido2_ctap_utils_led_animation(void);
|
||||
|
||||
/**
|
||||
* @brief Initialize button to be used for user presence test
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_utils_init_gpio_pin(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank);
|
||||
|
||||
/**
|
||||
* @brief Test user presence
|
||||
*
|
||||
* Successful if user clicks button in less than @ref CTAP_UP_TIMEOUT
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_utils_user_presence_test(void);
|
||||
|
||||
/**
|
||||
* @brief Compare fido2 credentials based on creation time
|
||||
*
|
||||
* @param[in] k1 first resident key
|
||||
* @param[in] k2 second resident key
|
||||
*
|
||||
* @return <0 if k2 has a bigger sign_count
|
||||
* @return 0 if equal k1 and k2 have equal sign_count
|
||||
* @return >0 if k1 has a bigger sign_count
|
||||
*/
|
||||
static inline int fido2_ctap_utils_cred_cmp(const void *k1, const void *k2)
|
||||
{
|
||||
ctap_resident_key_t *_k1 = (ctap_resident_key_t *)k1;
|
||||
ctap_resident_key_t *_k2 = (ctap_resident_key_t *)k2;
|
||||
|
||||
return _k2->creation_time - _k1->creation_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check equality of resident keys based on rp_id_hash and user_id
|
||||
*
|
||||
* @param[in] k1 first resident key
|
||||
* @param[in] k2 second resident key
|
||||
*
|
||||
* @return true if equal false otherwise
|
||||
*/
|
||||
static inline bool fido2_ctap_utils_ks_equal(const ctap_resident_key_t *k1,
|
||||
const ctap_resident_key_t *k2)
|
||||
{
|
||||
return memcmp(k1->rp_id_hash, k2->rp_id_hash, sizeof(k1->rp_id_hash)) == 0 &&
|
||||
memcmp(k1->user_id, k2->user_id, sizeof(k1->user_id)) == 0;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* FIDO2_CTAP_CTAP_UTILS_H */
|
||||
/** @} */
|
||||
48
sys/include/fido2/ctap/transport/ctap_transport.h
Normal file
48
sys/include/fido2/ctap/transport/ctap_transport.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup fido2_ctap_transport FIDO2 CTAP transport
|
||||
* @ingroup fido2_ctap
|
||||
* @brief CTAP transport layer
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief CTAP transport layer defines and function declarations
|
||||
*
|
||||
* @author Nils Ollrogge <nils.ollrogge@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef FIDO2_CTAP_TRANSPORT_CTAP_TRANSPORT_H
|
||||
#define FIDO2_CTAP_TRANSPORT_CTAP_TRANSPORT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "mutex.h"
|
||||
#include "timex.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief CTAP transport thread priority
|
||||
*/
|
||||
#ifndef CTAP_TRANSPORT_PRIO
|
||||
#define CTAP_TRANSPORT_PRIO (THREAD_PRIORITY_MAIN - 5)
|
||||
#endif
|
||||
/**
|
||||
* @brief Initialize ctap_transport layer and fido2_ctap
|
||||
*/
|
||||
void fido2_ctap_transport_init(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* FIDO2_CTAP_TRANSPORT_CTAP_TRANSPORT_H */
|
||||
/** @} */
|
||||
256
sys/include/fido2/ctap/transport/hid/ctap_hid.h
Normal file
256
sys/include/fido2/ctap/transport/hid/ctap_hid.h
Normal file
@ -0,0 +1,256 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup fido2_ctap_transport_hid FIDO2 CTAPHID
|
||||
* @ingroup fido2_ctap_transport
|
||||
* @brief FIDO2 CTAP USB_HID transport binding
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Definition for CTAPHID helper functions
|
||||
*
|
||||
* @author Nils Ollrogge <nils.ollrogge@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef FIDO2_CTAP_TRANSPORT_HID_CTAP_HID_H
|
||||
#define FIDO2_CTAP_TRANSPORT_HID_CTAP_HID_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "usb/usbus/hid.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @name CTAP_HID packet type payload sizes
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#define CTAP_HID_INIT_PAYLOAD_SIZE (CONFIG_USBUS_HID_INTERRUPT_EP_SIZE - 7) /**< endpoint size - init packet metadata */
|
||||
#define CTAP_HID_CONT_PAYLOAD_SIZE (CONFIG_USBUS_HID_INTERRUPT_EP_SIZE - 5) /**< endpoint size - cont packet metadata */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief CTAP_HID protocol version
|
||||
*/
|
||||
#define CTAP_HID_PROTOCOL_VERSION 0x02
|
||||
|
||||
/**
|
||||
* @name CTAP_HID packet type identifiers
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#define CTAP_HID_INIT_PACKET 0x80 /**< initialization packet identifier */
|
||||
#define CTAP_HID_CONT_PACKET 0x00 /**< continuation packet identifier */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief CTAP_HID size of nonce for init request
|
||||
*/
|
||||
#define CTAP_HID_INIT_NONCE_SIZE 8
|
||||
|
||||
/**
|
||||
* @brief CTAP_HID transaction timeout in microseconds
|
||||
*/
|
||||
#ifdef CONFIG_FIDO2_CTAP_TRANSPORT_HID_TRANSACTION_TIMEOUT
|
||||
#define CTAP_HID_TRANSACTION_TIMEOUT (CONFIG_FIDO2_CTAP_TRANSPORT_HID_TRANSACTION_TIMEOUT * \
|
||||
US_PER_MS)
|
||||
#else
|
||||
#define CTAP_HID_TRANSACTION_TIMEOUT (500 * US_PER_MS)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief CTAP_HID max message payload size
|
||||
*
|
||||
* CTAP specification (version 20190130) section 8.2.4.
|
||||
*/
|
||||
#define CTAP_HID_BUFFER_SIZE 7609
|
||||
|
||||
/**
|
||||
* @name CTAP_HID commands
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#define CTAP_HID_COMMAND_PING (0x01 | CTAP_HID_INIT_PACKET) /**< CTAPHID_PING command */
|
||||
#define CTAP_HID_COMMAND_MSG (0x03 | CTAP_HID_INIT_PACKET) /**< CTAPHID_MSG command */
|
||||
#define CTAP_HID_COMMAND_LOCK (0x04 | CTAP_HID_INIT_PACKET) /**< CTAPHID_LOCK command */
|
||||
#define CTAP_HID_COMMAND_INIT (0x06 | CTAP_HID_INIT_PACKET) /**< CTAPHID_INIT command */
|
||||
#define CTAP_HID_COMMAND_WINK (0x08 | CTAP_HID_INIT_PACKET) /**< CTAPHID_WINK command */
|
||||
#define CTAP_HID_COMMAND_CBOR (0x10 | CTAP_HID_INIT_PACKET) /**< CTAPHID_CBOR command */
|
||||
#define CTAP_HID_COMMAND_CANCEL (0x11 | CTAP_HID_INIT_PACKET) /**< CTAPHID_CANCEL command */
|
||||
#define CTAP_HID_COMMAND_KEEPALIVE (0x3b | CTAP_HID_INIT_PACKET) /**< CTAPHID_KEEPALIVE command */
|
||||
#define CTAP_HID_COMMAND_ERROR (0x3f | CTAP_HID_INIT_PACKET) /**< CTAPHID_ERROR command */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name CTAP_HID capability flags
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#define CTAP_HID_CAPABILITY_WINK 0x01 /**< If set, authenticator implements CTAPHID_WINK function */
|
||||
#define CTAP_HID_CAPABILITY_CBOR 0x04 /**< If set, authenticator implements CTAPHID_CBOR function */
|
||||
#define CTAP_HID_CAPABILITY_NMSG 0x08 /**< If set, authenticator DOES NOT implement CTAPHID_MSG function (CTAP1 / U2F) */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name CTAP_HID error codes
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#define CTAP_HID_OK 0x00 /**< Success */
|
||||
#define CTAP_HID_ERR_INVALID_CMD 0x01 /**< The command in the request is invalid */
|
||||
#define CTAP_HID_ERR_INVALID_PAR 0x02 /**< The parameter(s) in the request is invalid */
|
||||
#define CTAP_HID_ERR_INVALID_LEN 0x03 /**< The length field (BCNT) is invalid for the request */
|
||||
#define CTAP_HID_ERR_INVALID_SEQ 0x04 /**< The sequence does not match expected value */
|
||||
#define CTAP_HID_ERR_MSG_TIMEOUT 0x05 /**< The message has timed out */
|
||||
#define CTAP_HID_ERR_CHANNEL_BUSY 0x06 /**< The device is busy for the requesting channel */
|
||||
#define CTAP_HID_ERR_LOCK_REQUIRED 0x0a /**< Command requires channel lock */
|
||||
#define CTAP_HID_ERR_INVALID_CHANNEL 0x0b /**< CID is not valid. */
|
||||
#define CTAP_HID_ERR_OTHER 0x7f /**< Unspecified error */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name CTAP_HID status codes
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#define CTAP_HID_STATUS_PROCESSING 0x01 /**< processing status code */
|
||||
#define CTAP_HID_STATUS_UPNEEDED 0x02 /**< user presence needed status code */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief CTAP_HID max number of channels
|
||||
*
|
||||
*/
|
||||
#define CTAP_HID_CIDS_MAX 0x08
|
||||
|
||||
/**
|
||||
* @brief CTAP_HID animation delay in milliseconds for wink command
|
||||
*/
|
||||
#define CTAP_HID_WINK_DELAY 400
|
||||
|
||||
/**
|
||||
* @brief CTAP_HID broadcast channel identifier
|
||||
*
|
||||
*/
|
||||
#define CTAP_HID_BROADCAST_CID 0xffffffff
|
||||
|
||||
/**
|
||||
* @name CTAP_HID buffer status
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#define CTAP_HID_BUFFER_STATUS_BUFFERING 0x00 /**< packets are being buffered */
|
||||
#define CTAP_HID_BUFFER_STATUS_DONE 0x01 /**< packet processing done */
|
||||
#define CTAP_HID_BUFFER_STATUS_ERROR 0x02 /**< error occurred processing packets */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief CTAP_HID initialization packet struct
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t cmd; /**< CTAP_HID command */
|
||||
uint8_t bcnth; /**< higher byte */
|
||||
uint8_t bcntl; /**< lower byte */
|
||||
uint8_t payload[CTAP_HID_INIT_PAYLOAD_SIZE]; /**< packet payload */
|
||||
} ctap_hid_init_pkt_t;
|
||||
|
||||
/**
|
||||
* @brief CTAP_HID continuation packet struct
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t seq; /**< packet sequence number */
|
||||
uint8_t payload[CTAP_HID_CONT_PAYLOAD_SIZE]; /**< packet payload */
|
||||
} ctap_hid_cont_pkt_t;
|
||||
|
||||
/**
|
||||
* @brief CTAP_HID packet struct
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t cid; /**< channel identifier */
|
||||
union {
|
||||
ctap_hid_init_pkt_t init; /**< initialization packet */
|
||||
ctap_hid_cont_pkt_t cont; /**< continuation packet */
|
||||
};
|
||||
} ctap_hid_pkt_t;
|
||||
|
||||
/**
|
||||
* @brief CTAP_HID initialization response struct
|
||||
*
|
||||
* CTAP specification (version 20190130) 8.1.9.1.3
|
||||
*/
|
||||
typedef struct __attribute__((packed)){
|
||||
uint8_t nonce[CTAP_HID_INIT_NONCE_SIZE]; /**< nonce */
|
||||
uint32_t cid; /**< channel identifier */
|
||||
uint8_t protocol_version; /**< CTAP_HID protocol version */
|
||||
uint8_t version_major; /**< major device version */
|
||||
uint8_t version_minor; /**< minor device version */
|
||||
uint8_t build_version; /**< build device version */
|
||||
uint8_t capabilities; /**< capabilities flags */
|
||||
} ctap_hid_init_resp_t;
|
||||
|
||||
/**
|
||||
* @brief CTAP_HID channel identifier struct
|
||||
*
|
||||
* Used to keep state information about logical channels
|
||||
*/
|
||||
typedef struct {
|
||||
bool taken; /**< is cid taken? */
|
||||
uint32_t cid; /**< channel identifier */
|
||||
uint64_t last_used; /**< timestamp of last usage */
|
||||
} ctap_hid_cid_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize CTAPHID
|
||||
*
|
||||
* @param[in] queue CTAP transport layer event queue
|
||||
*/
|
||||
void fido2_ctap_transport_hid_init(event_queue_t *queue);
|
||||
|
||||
/**
|
||||
* @brief Handle CTAP_HID packet
|
||||
*
|
||||
* @param[in] pkt_raw raw CTAP_HID packet
|
||||
*/
|
||||
void fido2_ctap_transport_hid_handle_packet(void *pkt_raw);
|
||||
|
||||
/**
|
||||
* @brief Check logical channels for timeouts
|
||||
*
|
||||
* This function is used to prevent one channel from locking the authenticator.
|
||||
* E.g. if a device starts a transaction that does not fit in one packet and
|
||||
* sends a CTAPHID initialization packet but not continuation packet the
|
||||
* authenticator will keep waiting. This function will prevent this by
|
||||
* cancelling a transaction if it takes longer than
|
||||
*
|
||||
* CTAP specification (version 20190130) section 5.6
|
||||
*
|
||||
* @ref CTAP_HID_TRANSACTION_TIMEOUT
|
||||
*/
|
||||
void fido2_ctap_transport_hid_check_timeouts(void);
|
||||
|
||||
/**
|
||||
* @brief Check if CTAPHID layer has received CANCEL command
|
||||
*
|
||||
* @return true if CANCEL command has been received
|
||||
* @return false otherwise
|
||||
*/
|
||||
bool fido2_ctap_transport_hid_should_cancel(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* FIDO2_CTAP_TRANSPORT_HID_CTAP_HID_H */
|
||||
/** @} */
|
||||
37
tests/sys_fido2_ctap/Makefile
Normal file
37
tests/sys_fido2_ctap/Makefile
Normal file
@ -0,0 +1,37 @@
|
||||
BOARD ?= nrf52840dk
|
||||
#BOARD ?= nrf52840dongle
|
||||
|
||||
include ../Makefile.tests_common
|
||||
|
||||
USEMODULE += fido2_ctap_transport_hid
|
||||
USEPKG += fido2_tests
|
||||
|
||||
USB_VID ?= $(USB_VID_TESTING)
|
||||
USB_PID ?= $(USB_PID_TESTING)
|
||||
|
||||
# Disable user presence tests
|
||||
# Should be used when running fido2-test to make them run quicker
|
||||
#CFLAGS += -DCONFIG_FIDO2_CTAP_DISABLE_UP=1
|
||||
|
||||
# Disable user LED animation
|
||||
#CFLAGS += -DCONFIG_FIDO2_CTAP_DISABLE_LED=1
|
||||
|
||||
# FIDO2 tests except for the ones requiring user presence
|
||||
#
|
||||
# Use env -i because fido2-test has a depedency (pyscard) that needs to be
|
||||
# compiled natively (x86-64). Therefore we need to clear the flags set by e.g.
|
||||
# BOARD = nrf52840dk
|
||||
fido2-test:
|
||||
env -i PATH=$(PATH) $(MAKE) -C $(RIOTBASE)/build/pkg/fido2_tests
|
||||
|
||||
# FIDO2 user presence tests.
|
||||
#
|
||||
# Make sure to enable user presence tests by uncommenting CFLAGS += -DCONFIG_FIDO2_CTAP_DISABLE_UP=1
|
||||
#
|
||||
# Use env -i because fido2-test has a depedency (pyscard) that needs to be
|
||||
# compiled natively (x86-64). Therefore we need to clear the flags set by e.g.
|
||||
# BOARD = nrf52840dk
|
||||
fido2-test-up:
|
||||
env -i PATH=$(PATH) $(MAKE) -C $(RIOTBASE)/build/pkg/fido2_tests up-tests
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
||||
53
tests/sys_fido2_ctap/README.md
Normal file
53
tests/sys_fido2_ctap/README.md
Normal file
@ -0,0 +1,53 @@
|
||||
# Test Application for FIDO2 CTAP
|
||||
|
||||
This test aims to test the FIDO2 CTAP implementation by creating a FIDO2
|
||||
authenticator which uses CTAPHID as communication protocol.
|
||||
|
||||
Note:
|
||||
* This test application has only been tested on an nrf52840 DK.
|
||||
|
||||
The test application requires at least 16536 bytes of stack memory which are
|
||||
divided as follows:
|
||||
* 512 bytes isr_stack
|
||||
* 1024 usbus
|
||||
* 15000 bytes FIDO2 CTAP
|
||||
|
||||
## Usage
|
||||
The FIDO2 authenticator can be tested in two ways:
|
||||
|
||||
### Functional testing
|
||||
1. Flash the device with `make flash`.
|
||||
2. Test the authenticator on a website like [Webauthn.io](https://webauthn.io/).
|
||||
|
||||
Note:
|
||||
* Due to limited support of FIDO2 CTAP in browsers as of now, make sure to use the
|
||||
Chromium or Google Chrome browser when testing on [Webauthn.io](https://webauthn.io/).
|
||||
* When registering and authenticating on [Webauthn.io](https://webauthn.io/) you
|
||||
will need to push button 1 on your device in order to show user presence.
|
||||
|
||||
### Unit testing
|
||||
Unit testing is based on the `fido2_tests` package.
|
||||
|
||||
There are two test targets (fido2-test, fido2-test-up). The former requires no user
|
||||
interaction the latter does.
|
||||
|
||||
Note:
|
||||
* The tests require python 3.6+.
|
||||
* The tests require [swig](http://www.swig.org/) to be installed on your host computer.
|
||||
* Running the tests for the first time will setup a virtual python environment (venv) and install python dependencies of the tests. To check the dependencies please refer to the `requirements.txt` of the [fido2-tests repository](https://github.com/solokeys/fido2-tests).
|
||||
* The unit tests will require you to reboot the authenticator multiple times. Be patient before continuing as it takes a few seconds for the connection between OS and authenticator to be re-established.
|
||||
* If you keep getting errors while trying to run the tests try changing to another git branch and back e.g. `git checkout branch1 && git checkout -` in order to remove build artifacts. Then re-flash the device with `make flash term` and try to run the tests again with `make fido2-test` or `make fido2-test-up`.
|
||||
|
||||
fido2-test
|
||||
|
||||
1. To make benchmarking faster disable user presence tests by enabling the CFLAG
|
||||
`CONFIG_FIDO2_CTAP_DISABLE_UP` in the Makefile or through KConfig.
|
||||
2. Flash the device with `make flash`.
|
||||
3. Run the unit tests by running `make fido2-test`.
|
||||
|
||||
fido2-test-up
|
||||
|
||||
1. Make sure that the CFLAG `CONFIG_FIDO2_CTAP_DISABLE_UP` is disabled as this test target
|
||||
requires user interaction.
|
||||
2. Flash the device with `make flash`.
|
||||
3. Run the unit tests by running `make fido2-test-up` and follow the instructions. E.g. when `.ACTIVATE UP ONCE` is displayed, press the configured UP button (default button 1) once.
|
||||
36
tests/sys_fido2_ctap/main.c
Normal file
36
tests/sys_fido2_ctap/main.c
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup tests
|
||||
* @{
|
||||
* @file
|
||||
* @brief FIDO2 CTAP test application that creates an authenticator
|
||||
* which uses CTAPHID as underlying communication protocol
|
||||
*
|
||||
* @author Nils Ollrogge <nils-ollrogge@outlook.de>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
#include "xtimer.h"
|
||||
|
||||
#include "fido2/ctap.h"
|
||||
#include "fido2/ctap/transport/ctap_transport.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* sleep in order to see early DEBUG outputs */
|
||||
xtimer_sleep(3);
|
||||
fido2_ctap_transport_init();
|
||||
}
|
||||
@ -26,7 +26,7 @@
|
||||
|
||||
/*
|
||||
this descriptor is used, because the basic usb_hid interface was developed in
|
||||
conjunction with FIDO2. Descriptor is taken from CTAP2 specification
|
||||
conjunction with FIDO2. Descriptor is taken from CTAP specification
|
||||
(version 20190130) section 8.1.8.2
|
||||
*/
|
||||
static const uint8_t report_desc_ctap[] = {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user