Compare commits
412 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cfe1321d62 | ||
|
|
2cbea57c86 | ||
|
|
b6bf2e6c66 | ||
|
|
3212f95915 | ||
|
|
21b12a7bff | ||
|
|
c8dbc213a0 | ||
|
|
0a2ee6523f | ||
|
|
c3ea413592 | ||
|
|
64f371e6e5 | ||
|
|
fdd4afb993 | ||
|
|
fef46dc1c5 | ||
|
|
23a45ac297 | ||
|
|
b152ff15a8 | ||
|
|
751fcf0538 | ||
|
|
4e4c28a479 | ||
|
|
23b60beb2e | ||
|
|
37d7d7faeb | ||
|
|
49c0179ccf | ||
|
|
eacb8a040c | ||
|
|
cb99b8f401 | ||
|
|
94f8d5f65f | ||
|
|
38d332f450 | ||
|
|
c67f5e3a1f | ||
|
|
bfb8a4cb20 | ||
|
|
0f5a24c9b6 | ||
|
|
dd207bd031 | ||
|
|
6069cf949b | ||
|
|
297c34914b | ||
|
|
529a12e7a3 | ||
|
|
bdbdd92be8 | ||
|
|
3807e23914 | ||
|
|
ce7d3ea72f | ||
|
|
eb857df3e1 | ||
|
|
2842944d90 | ||
|
|
7be92f5331 | ||
|
|
403b26b60a | ||
|
|
b91ece8ec3 | ||
|
|
d54bc1b0f3 | ||
|
|
e2dbbe2cc3 | ||
|
|
8aa9d1c5a3 | ||
|
|
2d2814cefc | ||
|
|
89a9d013f0 | ||
|
|
964184cd9f | ||
|
|
3969fd5136 | ||
|
|
01b197d8ec | ||
|
|
8f7b52a387 | ||
|
|
565ceb7dc4 | ||
|
|
b7590b12d1 | ||
|
|
d8da775218 | ||
|
|
13c7ade20d | ||
|
|
d925e89127 | ||
|
|
7a1131cb1a | ||
|
|
d169f001b6 | ||
|
|
250de29c3c | ||
|
|
7c4a020dc1 | ||
|
|
88063d5d6d | ||
|
|
f43bc9701f | ||
|
|
353d782970 | ||
|
|
cdd2f486aa | ||
|
|
a381e94dda | ||
|
|
e78ec82435 | ||
|
|
584d2f3b33 | ||
|
|
18676990cb | ||
|
|
ed9c46ded0 | ||
|
|
d6a060f214 | ||
|
|
6265992162 | ||
|
|
63b7b9b8d2 | ||
|
|
8db06bf3ac | ||
|
|
77dd1c4b98 | ||
|
|
6a67800057 | ||
|
|
a70e259a90 | ||
|
|
7800056597 | ||
|
|
eeecf513cb | ||
|
|
9b0b584c14 | ||
|
|
1c45685926 | ||
|
|
cff544b485 | ||
|
|
1f805b1df2 | ||
|
|
1d20321d69 | ||
|
|
b42a664ac6 | ||
|
|
2d356a315e | ||
|
|
9bfbc45f84 | ||
|
|
a5a0f3508c | ||
|
|
9c9074c1ef | ||
|
|
022503fdc0 | ||
|
|
dba805dc04 | ||
|
|
bbf474811b | ||
|
|
2eca08161d | ||
|
|
46ada2c1f7 | ||
|
|
5faab169a8 | ||
|
|
3148649f86 | ||
|
|
3c40706aae | ||
|
|
4a64c11740 | ||
|
|
2319abe44e | ||
|
|
a5fe9b5d47 | ||
|
|
d5af2cd8ed | ||
|
|
e994078790 | ||
|
|
d99bcc90ec | ||
|
|
c443dec4a0 | ||
|
|
7a59b51849 | ||
|
|
10c58b4be7 | ||
|
|
730e76af75 | ||
|
|
ee80462a4a | ||
|
|
4ecb325e07 | ||
|
|
646b423fe4 | ||
|
|
77c3568885 | ||
|
|
3b43c5112b | ||
|
|
244c18fb51 | ||
|
|
78604f820d | ||
|
|
a68fbd65e9 | ||
|
|
bc0e022d85 | ||
|
|
3fad6baf89 | ||
|
|
df2977e6ad | ||
|
|
1fbf3da4f5 | ||
|
|
4ce6b2df5c | ||
|
|
e5910b1cba | ||
|
|
0df1330f92 | ||
|
|
3ce8496faa | ||
|
|
ef49560d0a | ||
|
|
53ed3a46c4 | ||
|
|
dc07653ae7 | ||
|
|
2d09a5c8e5 | ||
|
|
720c2e45f3 | ||
|
|
aeea3c7183 | ||
|
|
8838ac9e54 | ||
|
|
623db840d3 | ||
|
|
e2b06b908e | ||
|
|
b9e791ca90 | ||
|
|
ed560f10a4 | ||
|
|
1f839c5f99 | ||
|
|
effb8e4063 | ||
|
|
b2e45b0f7f | ||
|
|
24521dff4b | ||
|
|
7bc4a70319 | ||
|
|
cbef14beec | ||
|
|
0e54998d58 | ||
|
|
2e16036bb5 | ||
|
|
f98df743f9 | ||
|
|
4fe1c0804c | ||
|
|
7071949a1f | ||
|
|
e07b5194e3 | ||
|
|
e05115ffac | ||
|
|
38eca2fdd4 | ||
|
|
f276e99342 | ||
|
|
6f517e8fca | ||
|
|
39e2ff40c3 | ||
|
|
ffbe3fcbad | ||
|
|
cf5dbc9ae5 | ||
|
|
2fca44540a | ||
|
|
ec612a451d | ||
|
|
c43006f8c2 | ||
|
|
8ae4ab5af4 | ||
|
|
95cae29206 | ||
|
|
11c28adbb0 | ||
|
|
661442956d | ||
|
|
778c6b038a | ||
|
|
de1c50db4f | ||
|
|
c1e985c9af | ||
|
|
4f787eaaba | ||
|
|
b77277b72e | ||
|
|
02556fcde1 | ||
|
|
f234b0dc26 | ||
|
|
8ba9116454 | ||
|
|
5a31405244 | ||
|
|
902a988350 | ||
|
|
6256a9547d | ||
|
|
5568aa7b69 | ||
|
|
5e86745672 | ||
|
|
cffa8e29ff | ||
|
|
6c74db9763 | ||
|
|
dac6407134 | ||
|
|
f49833291f | ||
|
|
8c1e002892 | ||
|
|
8d49ed5ffc | ||
|
|
a0d9ad7a3a | ||
|
|
d2c25b69bc | ||
|
|
910fb66f3c | ||
|
|
ed12d6f8e9 | ||
|
|
a9799dc77f | ||
|
|
d7d75caecf | ||
|
|
af4eb075c7 | ||
|
|
0c5280e12a | ||
|
|
163e936231 | ||
|
|
1b4dd9bed0 | ||
|
|
5b95e35ca9 | ||
|
|
69ec242095 | ||
|
|
6eb6cd35d0 | ||
|
|
f21e203093 | ||
|
|
e96da09a84 | ||
|
|
6fe16a63e4 | ||
|
|
d5fe405a87 | ||
|
|
54bbc0e9ea | ||
|
|
b0b0187919 | ||
|
|
1f0e1fb8f4 | ||
|
|
f3f34cf66b | ||
|
|
82ed96b2e2 | ||
|
|
92d04f9131 | ||
|
|
7a71bf48fc | ||
|
|
21765a6f10 | ||
|
|
eb2c92bc5c | ||
|
|
7e2ecdbc56 | ||
|
|
e54df525c4 | ||
|
|
1d9107d4bb | ||
|
|
a9be759da3 | ||
|
|
34bfc3b2ef | ||
|
|
d985cf6301 | ||
|
|
0b00e01187 | ||
|
|
bef1922c8f | ||
|
|
107e5c34db | ||
|
|
6157a91fdf | ||
|
|
779db90713 | ||
|
|
a0f1d2334d | ||
|
|
5c6f87ab8f | ||
|
|
cf152c1692 | ||
|
|
04238509ee | ||
|
|
85298062cd | ||
|
|
0464ad8964 | ||
|
|
19197e54a8 | ||
|
|
01a6c9f77f | ||
|
|
ba57cc4527 | ||
|
|
68b5614fb9 | ||
|
|
4fd4d75e21 | ||
|
|
599fd706ce | ||
|
|
28e979939a | ||
|
|
849221fd95 | ||
|
|
011429a982 | ||
|
|
b99181a00c | ||
|
|
041bb788f9 | ||
|
|
20a8ef08f0 | ||
|
|
e757ad2945 | ||
|
|
1ce0d98c34 | ||
|
|
96de6efed6 | ||
|
|
195096ad52 | ||
|
|
1ee86f8634 | ||
|
|
2b9a5829e5 | ||
|
|
8056e64cab | ||
|
|
ffb3beb84a | ||
|
|
d78d9d10aa | ||
|
|
f8d4f1d02e | ||
|
|
b493a81ddc | ||
|
|
5c20909b03 | ||
|
|
27b9e3954a | ||
|
|
440ec5c854 | ||
|
|
cb2744cab3 | ||
|
|
5db1014850 | ||
|
|
421bea6421 | ||
|
|
65039c0959 | ||
|
|
8e36b4c379 | ||
|
|
3652368542 | ||
|
|
e5d1ef29a4 | ||
|
|
0fd36806cc | ||
|
|
7bf26b28fc | ||
|
|
da94a82487 | ||
|
|
c24be5a631 | ||
|
|
46ce9390bf | ||
|
|
c1fd5736f9 | ||
|
|
b1c4ff877e | ||
|
|
6c85d57412 | ||
|
|
233c5a7c7d | ||
|
|
3b4ac12d0f | ||
|
|
2e5b8f4c71 | ||
|
|
a9697ba4e0 | ||
|
|
aec488f070 | ||
|
|
7c5bab8b05 | ||
|
|
21035d649d | ||
|
|
9c90095e96 | ||
|
|
7c5f2cee4b | ||
|
|
5e0c42a9f9 | ||
|
|
da7b918dc4 | ||
|
|
abe91823c0 | ||
|
|
91e049b997 | ||
|
|
cfcfb941e0 | ||
|
|
45c2cf65fe | ||
|
|
1217d82361 | ||
|
|
332debea6d | ||
|
|
8836902dc1 | ||
|
|
bafede2ae5 | ||
|
|
a019b54d69 | ||
|
|
539420b996 | ||
|
|
0c08590dcc | ||
|
|
cac4ae1751 | ||
|
|
974868d8e4 | ||
|
|
2bbaf7c274 | ||
|
|
3adb1a8422 | ||
|
|
df26040838 | ||
|
|
95a9fe4214 | ||
|
|
8af7cac57a | ||
|
|
7997eefdc8 | ||
|
|
e18f841a34 | ||
|
|
73b51cabfc | ||
|
|
539ea61436 | ||
|
|
75771e5e46 | ||
|
|
8e26ec8bcd | ||
|
|
ad3b2bbe4b | ||
|
|
b9ad8f4745 | ||
|
|
26148282e6 | ||
|
|
05044b498d | ||
|
|
8242dc8d80 | ||
|
|
be44947475 | ||
|
|
0d280ca252 | ||
|
|
2f6e4d5568 | ||
|
|
911dab031e | ||
|
|
4c3042a8bf | ||
|
|
b7ceec8d49 | ||
|
|
63e15b19bb | ||
|
|
b2c4e0e1c1 | ||
|
|
b72c596aa6 | ||
|
|
2d81a3c472 | ||
|
|
bb20dd7a53 | ||
|
|
3a71275bc8 | ||
|
|
c258dad8e6 | ||
|
|
9f1e879efe | ||
|
|
57bf97196d | ||
|
|
e8c8ce4d15 | ||
|
|
69d618cc6b | ||
|
|
e057f17180 | ||
|
|
ce040a79f5 | ||
|
|
8ffd1bfe38 | ||
|
|
5105545df0 | ||
|
|
d011314500 | ||
|
|
51cbfe5fe9 | ||
|
|
aa7362f88f | ||
|
|
2b1227b105 | ||
|
|
a79842b33f | ||
|
|
30f51b8453 | ||
|
|
c00c83dfe6 | ||
|
|
cda97259b3 | ||
|
|
c883083a75 | ||
|
|
016780b3de | ||
|
|
24224b78dd | ||
|
|
07729f807b | ||
|
|
e0c793dd0a | ||
|
|
9d6003d1e5 | ||
|
|
147a93d7fb | ||
|
|
f12c55805c | ||
|
|
7e10e25f96 | ||
|
|
9052c66a7f | ||
|
|
443ca69547 | ||
|
|
415c1b2e9c | ||
|
|
d87c1530c7 | ||
|
|
f90baaf095 | ||
|
|
1d7bdb0861 | ||
|
|
fa811e2a0f | ||
|
|
ff498ebfdf | ||
|
|
cceb735cc0 | ||
|
|
5a9de32e02 | ||
|
|
c9eacc4a3d | ||
|
|
c23d92ea28 | ||
|
|
da04fbb824 | ||
|
|
0bfa760903 | ||
|
|
bd9d4286d5 | ||
|
|
3d1c68fa40 | ||
|
|
26ac66e813 | ||
|
|
05afcd706e | ||
|
|
8c90dd55bd | ||
|
|
c6c1d0c6eb | ||
|
|
d4ed55b5a5 | ||
|
|
cfb0b8f3f2 | ||
|
|
eca8656bd9 | ||
|
|
5b5a9fc0fe | ||
|
|
59ec9b75fc | ||
|
|
8b2be54ede | ||
|
|
483073ebb8 | ||
|
|
12250a1c31 | ||
|
|
3f8aed1ecf | ||
|
|
707af84292 | ||
|
|
717e7135d5 | ||
|
|
fc909fa93d | ||
|
|
03f29f5be6 | ||
|
|
267e66eaee | ||
|
|
22317d4322 | ||
|
|
376b49db95 | ||
|
|
9756b451bb | ||
|
|
dcdf605a5e | ||
|
|
6d9208f434 | ||
|
|
9a493b9972 | ||
|
|
ef993d0f7b | ||
|
|
5360d62062 | ||
|
|
6d123e2a0f | ||
|
|
379f136699 | ||
|
|
f3decd8649 | ||
|
|
4f33d999e3 | ||
|
|
46661ee808 | ||
|
|
b1fdb9b1d1 | ||
|
|
b3b2e98ec1 | ||
|
|
cdf96e3564 | ||
|
|
4fe29750f2 | ||
|
|
87bdea7e28 | ||
|
|
cecb5da3a0 | ||
|
|
49cf031037 | ||
|
|
3a2ab27567 | ||
|
|
601e1bcda7 | ||
|
|
dc6f25caf3 | ||
|
|
20345ebd10 | ||
|
|
6674a0bb1e | ||
|
|
c45c70d95d | ||
|
|
061b5e919e | ||
|
|
0dc547dbe5 | ||
|
|
c383f6c446 | ||
|
|
c3d6d4c657 | ||
|
|
e387033266 | ||
|
|
97336bf8d4 | ||
|
|
6ebaa05523 | ||
|
|
00f30ba00c | ||
|
|
363ad1c9e2 | ||
|
|
94806f9bf0 | ||
|
|
03b35cfe88 | ||
|
|
bc9bbaf292 | ||
|
|
e5ca759dea | ||
|
|
f780b2a0b4 | ||
|
|
b9f1adf211 | ||
|
|
880a1b003d | ||
|
|
545860ccbc |
4
.github/FUNDING.yml
vendored
Normal file
4
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: polhenarejos
|
||||
custom: ["https://www.paypal.me/polhenarejos"]
|
||||
18
.github/workflows/codeql.yml
vendored
18
.github/workflows/codeql.yml
vendored
@@ -13,12 +13,13 @@ name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
branches: [ "main", "development", "eddsa" ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ "main" ]
|
||||
branches: [ "main", "development", "eddsa" ]
|
||||
schedule:
|
||||
- cron: '23 5 * * 4'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
@@ -35,6 +36,7 @@ jobs:
|
||||
language: [ 'cpp', 'python' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
mode: [ 'pico', 'local' ]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -42,17 +44,17 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
|
||||
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
# - name: Autobuild
|
||||
@@ -61,12 +63,12 @@ jobs:
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
|
||||
- run: |
|
||||
echo "Run, Build Application using script"
|
||||
./workflows/autobuild.sh
|
||||
./workflows/autobuild.sh ${{ matrix.mode }}
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
||||
35
.github/workflows/nightly.yml
vendored
Normal file
35
.github/workflows/nightly.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
name: "Nightly deploy"
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 2 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
nightly:
|
||||
name: Deploy nightly
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
refs: [main, development]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ matrix.refs }}
|
||||
submodules: 'recursive'
|
||||
- name : Build
|
||||
env:
|
||||
PICO_SDK_PATH: ../pico-sdk
|
||||
run: |
|
||||
./workflows/autobuild.sh pico
|
||||
./build_pico_fido.sh --no-eddsa
|
||||
./workflows/autobuild.sh esp32
|
||||
- name: Update nightly release
|
||||
uses: pyTooling/Actions/releaser@main
|
||||
with:
|
||||
tag: nightly-${{ matrix.refs }}
|
||||
rm: true
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
files: release/*.*
|
||||
37
.github/workflows/test.yml
vendored
Normal file
37
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "Emulation and test"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main", "development", "eddsa" ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ "main", "development", "eddsa" ]
|
||||
schedule:
|
||||
- cron: '23 5 * * 4'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository and submodules
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build in container
|
||||
run: ./tests/build-in-docker.sh
|
||||
- name: Start emulation and test
|
||||
run: ./tests/run-test-in-docker.sh
|
||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -1,3 +1,3 @@
|
||||
[submodule "pico-hsm-sdk"]
|
||||
path = pico-hsm-sdk
|
||||
url = ../pico-hsm-sdk
|
||||
[submodule "pico-keys-sdk"]
|
||||
path = pico-keys-sdk
|
||||
url = https://github.com/polhenarejos/pico-keys-sdk
|
||||
|
||||
110
CMakeLists.txt
110
CMakeLists.txt
@@ -17,38 +17,70 @@
|
||||
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
if(ESP_PLATFORM)
|
||||
set(DEBUG_APDU 1)
|
||||
set(DENABLE_POWER_ON_RESET 0)
|
||||
set(EXTRA_COMPONENT_DIRS src pico-keys-sdk/src)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
else()
|
||||
|
||||
if(ENABLE_EMULATION)
|
||||
else()
|
||||
set(PICO_USE_FASTEST_SUPPORTED_CLOCK 1)
|
||||
include(pico_sdk_import.cmake)
|
||||
endif()
|
||||
|
||||
project(pico_fido C CXX ASM)
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
if(ENABLE_EMULATION)
|
||||
else()
|
||||
pico_sdk_init()
|
||||
endif()
|
||||
|
||||
add_executable(pico_fido)
|
||||
|
||||
option(ENABLE_UP_BUTTON "Enable/disable user presence button" ON)
|
||||
if(ENABLE_UP_BUTTON)
|
||||
add_definitions(-DENABLE_UP_BUTTON=1)
|
||||
message("Enabling user presence with button")
|
||||
else()
|
||||
add_definitions(-DENABLE_UP_BUTTON=0)
|
||||
message("Disabling user presence with button")
|
||||
endif(ENABLE_UP_BUTTON)
|
||||
endif()
|
||||
|
||||
option(ENABLE_POWER_ON_RESET "Enable/disable power cycle on reset" ON)
|
||||
if(ENABLE_POWER_ON_RESET)
|
||||
add_definitions(-DENABLE_POWER_ON_RESET=1)
|
||||
message("Enabling power cycle on reset")
|
||||
message(STATUS "Power cycle on reset: \t enabled")
|
||||
else()
|
||||
add_definitions(-DENABLE_POWER_ON_RESET=0)
|
||||
message("Disabling power cycle on reset")
|
||||
message(STATUS "Power cycle on reset: \t disabled")
|
||||
endif(ENABLE_POWER_ON_RESET)
|
||||
|
||||
target_sources(pico_fido PUBLIC
|
||||
option(ENABLE_OATH_APP "Enable/disable OATH application" ON)
|
||||
if(ENABLE_OATH_APP)
|
||||
add_definitions(-DENABLE_OATH_APP=1)
|
||||
message(STATUS "OATH Application: \t\t enabled")
|
||||
else()
|
||||
add_definitions(-DENABLE_OATH_APP=0)
|
||||
message(STATUS "OATH Application: \t\t disabled")
|
||||
endif(ENABLE_OATH_APP)
|
||||
|
||||
option(ENABLE_OTP_APP "Enable/disable OTP application" ON)
|
||||
if(ENABLE_OTP_APP)
|
||||
add_definitions(-DENABLE_OTP_APP=1)
|
||||
message(STATUS "OTP Application: \t\t enabled")
|
||||
else()
|
||||
add_definitions(-DENABLE_OTP_APP=0)
|
||||
message(STATUS "OTP Application: \t\t disabled")
|
||||
endif(ENABLE_OTP_APP)
|
||||
|
||||
if(ENABLE_OTP_APP OR ENABLE_OATH_APP)
|
||||
set(USB_ITF_CCID 1)
|
||||
set(USB_ITF_WCID 1)
|
||||
else()
|
||||
set(USB_ITF_CCID 0)
|
||||
endif()
|
||||
|
||||
set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/fido.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/files.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/kek.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cmd_register.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cmd_authenticate.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cmd_version.c
|
||||
@@ -65,25 +97,67 @@ target_sources(pico_fido PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_config.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_vendor.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_large_blobs.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/management.c
|
||||
)
|
||||
set(HSM_DRIVER "hid")
|
||||
include(pico-hsm-sdk/pico_hsm_sdk_import.cmake)
|
||||
if (${ENABLE_OATH_APP})
|
||||
set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/oath.c
|
||||
)
|
||||
endif()
|
||||
if (${ENABLE_OTP_APP})
|
||||
set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/otp.c
|
||||
)
|
||||
endif()
|
||||
|
||||
target_include_directories(pico_fido PUBLIC
|
||||
set(USB_ITF_HID 1)
|
||||
include(pico-keys-sdk/pico_keys_sdk_import.cmake)
|
||||
SET_VERSION(ver_major ver_minor "${CMAKE_CURRENT_LIST_DIR}/src/fido/version.h" 1)
|
||||
if(ESP_PLATFORM)
|
||||
project(pico_fido)
|
||||
endif()
|
||||
set(INCLUDES ${INCLUDES}
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido
|
||||
)
|
||||
if(NOT ESP_PLATFORM)
|
||||
target_sources(pico_fido PUBLIC ${SOURCES})
|
||||
target_include_directories(pico_fido PUBLIC ${INCLUDES})
|
||||
|
||||
target_compile_options(pico_fido PUBLIC
|
||||
-Wall
|
||||
)
|
||||
if (NOT MSVC)
|
||||
target_compile_options(pico_fido PUBLIC
|
||||
-Werror
|
||||
)
|
||||
|
||||
string(FIND ${CMAKE_C_COMPILER} ":" COMPILER_COLON)
|
||||
if (${COMPILER_COLON} GREATER_EQUAL 0)
|
||||
target_compile_options(pico_fido PUBLIC
|
||||
-Wno-error=use-after-free
|
||||
)
|
||||
endif()
|
||||
endif(NOT MSVC)
|
||||
|
||||
pico_add_extra_outputs(pico_fido)
|
||||
|
||||
target_link_libraries(pico_fido PRIVATE pico_hsm_sdk pico_stdlib pico_multicore hardware_flash hardware_sync hardware_adc pico_unique_id hardware_rtc tinyusb_device tinyusb_board)
|
||||
if(ENABLE_EMULATION)
|
||||
if(NOT MSVC)
|
||||
target_compile_options(pico_fido PUBLIC
|
||||
-fdata-sections
|
||||
-ffunction-sections
|
||||
)
|
||||
endif(NOT MSVC)
|
||||
if(APPLE)
|
||||
target_link_options(pico_fido PUBLIC
|
||||
-Wl,-dead_strip
|
||||
)
|
||||
else()
|
||||
target_link_options(pico_fido PUBLIC
|
||||
-Wl,--gc-sections
|
||||
)
|
||||
endif (APPLE)
|
||||
target_link_libraries(pico_fido PRIVATE pthread m)
|
||||
else()
|
||||
target_link_libraries(pico_fido PRIVATE pico_keys_sdk pico_stdlib pico_multicore hardware_flash hardware_sync hardware_adc pico_unique_id pico_aon_timer tinyusb_device tinyusb_board)
|
||||
pico_add_extra_outputs(${CMAKE_PROJECT_NAME})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
100
README.md
100
README.md
@@ -1,8 +1,8 @@
|
||||
# Pico FIDO
|
||||
This project aims at transforming your Raspberry Pico into a FIDO key integrated. The Pico works as a FIDO key, like a normal USB key for authentication.
|
||||
This project transforms your Raspberry Pi Pico or ESP32 microcontroller into an integrated FIDO Passkey, functioning like a standard USB Passkey for authentication.
|
||||
|
||||
## Features
|
||||
Pico FIDO has implemented the following features:
|
||||
Pico FIDO includes the following features:
|
||||
|
||||
- CTAP 2.1 / CTAP 1
|
||||
- WebAuthn
|
||||
@@ -10,15 +10,16 @@ Pico FIDO has implemented the following features:
|
||||
- HMAC-Secret extension
|
||||
- CredProtect extension
|
||||
- User presence enforcement through physical button
|
||||
- User Verification with PIN
|
||||
- Discoverable credentials
|
||||
- User verification with PIN
|
||||
- Discoverable credentials (resident keys)
|
||||
- Credential management
|
||||
- ECDSA authentication
|
||||
- ECDSA and EDDSA authentication
|
||||
- Support for SECP256R1, SECP384R1, SECP521R1, SECP256K1 and Ed25519 curves
|
||||
- App registration and login
|
||||
- Device selection
|
||||
- Support for vendor Config
|
||||
- Support for vendor configuration
|
||||
- Backup with 24 words
|
||||
- Secure lock to protect the device from flash dumpings
|
||||
- Secure lock to protect the device from flash dumps
|
||||
- Permissions support (MC, GA, CM, ACFG, LBW)
|
||||
- Authenticator configuration
|
||||
- minPinLength extension
|
||||
@@ -26,33 +27,71 @@ Pico FIDO has implemented the following features:
|
||||
- Enterprise attestation
|
||||
- credBlobs extension
|
||||
- largeBlobKey extension
|
||||
- largeBlobs support (2048 bytes máx.)
|
||||
- Large blobs support (2048 bytes max)
|
||||
- OATH (based on YKOATH protocol specification)
|
||||
- TOTP / HOTP
|
||||
- Yubikey One Time Password
|
||||
- Challenge-response generation
|
||||
- Emulated keyboard interface
|
||||
- Button press generates an OTP that is directly typed
|
||||
- Yubico YKMAN compatible
|
||||
- Nitrokey nitropy and nitroapp compatible
|
||||
- Secure Boot and Secure Lock in RP2350 and ESP32-S3 MCUs
|
||||
- One Time Programming to store the master key that encrypts all resident keys and seeds.
|
||||
- Rescue interface to allow recovery of the device if it becomes unresponsive or undetectable.
|
||||
- LED customization with Pico Commissioner.
|
||||
|
||||
All these features are compliant with the specification. Therefore, if you detect some behaviour that is not expected or it does not follow the rules of specs, please open an issue.
|
||||
All features comply with the specifications. If you encounter unexpected behavior or deviations from the specifications, please open an issue.
|
||||
|
||||
## Security considerations
|
||||
Pico FIDO is an open platform so be careful. The contents in the flash memory may be easily dumpled and obtain the private/master keys. Therefore, it is not possible to encrypt the content. At least, one key (the master, the supreme key) must be stored in clear text.
|
||||
## Security Considerations
|
||||
Microcontrollers RP2350 and ESP32-S3 are designed to support secure environments when Secure Boot is enabled, and optionally, Secure Lock. These features allow a master key encryption key (MKEK) to be stored in a one-time programmable (OTP) memory region, which is inaccessible from outside secure code. This master key is then used to encrypt all private and secret keys on the device, protecting sensitive data from potential flash memory dumps.
|
||||
|
||||
If the Pico is stolen the contents of private and secret keys can be read.
|
||||
**However**, the RP2040 microcontroller lacks this level of security hardware, meaning that it cannot provide the same protection. Data stored on its flash memory, including private or master keys, can be easily accessed or dumped, as encryption of the master key itself is not feasible. Consequently, if an RP2040 device is stolen, any stored private or secret keys may be exposed.
|
||||
|
||||
## Download
|
||||
Please, go to the [Release page](https://github.com/polhenarejos/pico-fido/releases "Release page") and download the UF2 file for your board.
|
||||
**If you own an ESP32-S3 board, go to [ESP32 Flasher](https://www.picokeys.com/esp32-flasher/) for flashing your Pico FIDO.**
|
||||
|
||||
## Build
|
||||
Before building, ensure you have installed the toolchain for the Pico and the Pico SDK is properly located in your drive.
|
||||
If you own a Raspberry Pico (RP2040 or RP2350), go to [Download page](https://www.picokeys.com/getting-started/), select your vendor and model and download the proper firmware; or go to [Release page](https://www.github.com/polhenarejos/pico-fido/releases/) and download the UF2 file for your board.
|
||||
|
||||
git clone https://github.com/polhenarejos/pico-fido
|
||||
cd pico-fido
|
||||
mkdir build
|
||||
cd build
|
||||
PICO_SDK_PATH=/path/to/pico-sdk cmake .. -DPICO_BOARD=board_type -DUSB_VID=0x1234 -DUSB_PID=0x5678
|
||||
make
|
||||
Note that UF2 files are shiped with a dummy VID/PID to avoid license issues (FEFF:FCFD). If you plan to use it with other proprietary tools, you should modify Info.plist of CCID driver to add these VID/PID or use the [Pico Commissioner](https://www.picokeys.com/pico-commissioner/ "Pico Commissioner").
|
||||
|
||||
Note that PICO_BOARD, USB_VID and USB_PID are optional. If not provided, pico board and VID/PID FEFF:FCFD will be used.
|
||||
You can use whatever VID/PID (i.e., 234b:0000 from FISJ), but remember that you are not authorized to distribute the binary with a VID/PID that you do not own.
|
||||
|
||||
After make ends, the binary file pico_fido.uf2 will be generated. Put your pico board into loading mode, by pushing BOOTSEL button while pluging on, and copy the UF2 to the new fresh usb mass storage Pico device. Once copied, the pico mass storage will be disconnected automatically and the pico board will reset with the new firmware. A blinking led will indicate the device is ready to work.
|
||||
Note that the pure-browser option [Pico Commissioner](https://www.picokeys.com/pico-commissioner/ "Pico Commissioner") is the most recommended.
|
||||
|
||||
**Remark:** Pico Fido uses HID interface and thus, VID/PID values are irrelevant in terms of operativity. You can safely use any arbitrary value or the default ones.
|
||||
## Build for Raspberry Pico
|
||||
Before building, ensure you have installed the toolchain for the Pico and that the Pico SDK is properly located on your drive.
|
||||
|
||||
```sh
|
||||
git clone https://github.com/polhenarejos/pico-fido
|
||||
git submodule update --init --recursive
|
||||
cd pico-fido
|
||||
mkdir build
|
||||
cd build
|
||||
PICO_SDK_PATH=/path/to/pico-sdk cmake .. -DPICO_BOARD=board_type -DUSB_VID=0x1234 -DUSB_PID=0x5678
|
||||
make
|
||||
```
|
||||
Note that `PICO_BOARD`, `USB_VID` and `USB_PID` are optional. If not provided, `pico` board and VID/PID `FEFF:FCFD` will be used.
|
||||
|
||||
Additionally, you can pass the `VIDPID=value` parameter to build the firmware with a known VID/PID. The supported values are:
|
||||
|
||||
- `NitroHSM`
|
||||
- `NitroFIDO2`
|
||||
- `NitroStart`
|
||||
- `NitroPro`
|
||||
- `Nitro3`
|
||||
- `Yubikey5`
|
||||
- `YubikeyNeo`
|
||||
- `YubiHSM`
|
||||
- `Gnuk`
|
||||
- `GnuPG`
|
||||
|
||||
After running `make`, the binary file `pico_fido.uf2` will be generated. To load this onto your Pico board:
|
||||
|
||||
1. Put the Pico board into loading mode by holding the `BOOTSEL` button while plugging it in.
|
||||
2. Copy the `pico_fido.uf2` file to the new USB mass storage device that appears.
|
||||
3. Once the file is copied, the Pico mass storage device will automatically disconnect, and the Pico board will reset with the new firmware.
|
||||
4. A blinking LED will indicate that the device is ready to work.
|
||||
|
||||
## Led blink
|
||||
Pico FIDO uses the led to indicate the current status. Four states are available:
|
||||
@@ -78,20 +117,21 @@ While processing, the Pico FIDO is busy and cannot receive additional commands u
|
||||
|
||||
## Driver
|
||||
|
||||
Pico FIDO uses the `HID` driver, present in all OS. It should be detected by all OS and browser/applications, like normal USB FIDO keys.
|
||||
Pico FIDO uses the `HID` driver, which is present in all operating systems. It should be detected by all OS and browser/applications just like normal USB FIDO keys.
|
||||
|
||||
## Tests
|
||||
|
||||
Tests can be found at `tests` folder. It is based on [FIDO2 tests](https://github.com/solokeys/fido2-tests "FIDO2 tests") from Solokeys, but adapted to [python-fido2](https://github.com/Yubico/python-fido2 "python-fido2") v1.0 package, which is a major refactor from previous 0.8 version and includes latests improvements from CTAP 2.1.
|
||||
Tests can be found in the `tests` folder. They are based on [FIDO2 tests](https://github.com/solokeys/fido2-tests "FIDO2 tests") from Solokeys but adapted to the [python-fido2](https://github.com/Yubico/python-fido2 "python-fido2") v1.0 package, which is a major refactor from the previous 0.8 version and includes the latest improvements from CTAP 2.1.
|
||||
|
||||
All tests can be run by
|
||||
To run all tests, use:
|
||||
|
||||
```
|
||||
```sh
|
||||
pytest
|
||||
```
|
||||
|
||||
or by selecting a subset with `-k <test>` flag:
|
||||
```
|
||||
To run a subset of tests, use the `-k <test>` flag:
|
||||
|
||||
```sh
|
||||
pytest -k test_credprotect
|
||||
```
|
||||
|
||||
|
||||
@@ -1,53 +1,47 @@
|
||||
#!/bin/bash
|
||||
|
||||
VERSION_MAJOR="2"
|
||||
VERSION_MINOR="10"
|
||||
VERSION_MAJOR="6"
|
||||
VERSION_MINOR="6"
|
||||
NO_EDDSA=0
|
||||
SUFFIX="${VERSION_MAJOR}.${VERSION_MINOR}"
|
||||
#if ! [[ -z "${GITHUB_SHA}" ]]; then
|
||||
# SUFFIX="${SUFFIX}.${GITHUB_SHA}"
|
||||
#fi
|
||||
|
||||
rm -rf release/*
|
||||
if [[ $1 == "--no-eddsa" ]]; then
|
||||
NO_EDDSA=1
|
||||
echo "Skipping EDDSA build"
|
||||
fi
|
||||
|
||||
mkdir -p build_release
|
||||
mkdir -p release
|
||||
mkdir -p release_eddsa
|
||||
rm -rf -- release/*
|
||||
if [[ $NO_EDDSA -eq 0 ]]; then
|
||||
rm -rf -- release_eddsa/*
|
||||
fi
|
||||
cd build_release
|
||||
|
||||
for board in adafruit_feather_rp2040 \
|
||||
adafruit_itsybitsy_rp2040 \
|
||||
adafruit_kb2040 \
|
||||
adafruit_macropad_rp2040 \
|
||||
adafruit_qtpy_rp2040 \
|
||||
adafruit_trinkey_qt2040 \
|
||||
arduino_nano_rp2040_connect \
|
||||
datanoisetv_rp2040_dsp \
|
||||
eetree_gamekit_rp2040 \
|
||||
garatronic_pybstick26_rp2040 \
|
||||
melopero_shake_rp2040 \
|
||||
pico \
|
||||
pico_w \
|
||||
pimoroni_badger2040 \
|
||||
pimoroni_interstate75 \
|
||||
pimoroni_keybow2040 \
|
||||
pimoroni_motor2040 \
|
||||
pimoroni_pga2040 \
|
||||
pimoroni_picolipo_4mb \
|
||||
pimoroni_picolipo_16mb \
|
||||
pimoroni_picosystem \
|
||||
pimoroni_plasma2040 \
|
||||
pimoroni_servo2040 \
|
||||
pimoroni_tiny2040 \
|
||||
pimoroni_tiny2040_2mb \
|
||||
seeed_xiao_rp2040 \
|
||||
solderparty_rp2040_stamp \
|
||||
solderparty_rp2040_stamp_carrier \
|
||||
solderparty_rp2040_stamp_round_carrier \
|
||||
sparkfun_micromod \
|
||||
sparkfun_promicro \
|
||||
sparkfun_thingplus \
|
||||
vgaboard \
|
||||
waveshare_rp2040_lcd_0.96 \
|
||||
waveshare_rp2040_plus_4mb \
|
||||
waveshare_rp2040_plus_16mb \
|
||||
waveshare_rp2040_zero \
|
||||
wiznet_w5100s_evb_pico
|
||||
PICO_SDK_PATH="${PICO_SDK_PATH:-../../pico-sdk}"
|
||||
board_dir=${PICO_SDK_PATH}/src/boards/include/boards
|
||||
for board in "$board_dir"/*
|
||||
do
|
||||
rm -rf *
|
||||
PICO_SDK_PATH=../../pico-sdk cmake .. -DPICO_BOARD=$board
|
||||
make -kj20
|
||||
mv pico_fido.uf2 ../release/pico_fido_$board-$VERSION_MAJOR.$VERSION_MINOR.uf2
|
||||
|
||||
board_name="$(basename -- "$board" .h)"
|
||||
rm -rf -- ./*
|
||||
PICO_SDK_PATH="${PICO_SDK_PATH}" cmake .. -DPICO_BOARD=$board_name -DSECURE_BOOT_PKEY=../../ec_private_key.pem
|
||||
make -j`nproc`
|
||||
mv pico_fido.uf2 ../release/pico_fido_$board_name-$SUFFIX.uf2
|
||||
done
|
||||
|
||||
# Build with EDDSA
|
||||
|
||||
if [[ $NO_EDDSA -eq 0 ]]; then
|
||||
for board in "$board_dir"/*
|
||||
do
|
||||
board_name="$(basename -- "$board" .h)"
|
||||
rm -rf -- ./*
|
||||
PICO_SDK_PATH="${PICO_SDK_PATH}" cmake .. -DPICO_BOARD=$board_name -DSECURE_BOOT_PKEY=../../ec_private_key.pem -DENABLE_EDDSA=1
|
||||
make -j`nproc`
|
||||
mv pico_fido.uf2 ../release/pico_fido_$board_name-$SUFFIX-eddsa1.uf2
|
||||
done
|
||||
fi
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
VERSION_MAJOR="3" #Version of Pico CCID Core
|
||||
VERSION_MINOR="4"
|
||||
VERSION_MAJOR="4" #Version of Pico CCID Core
|
||||
VERSION_MINOR="0"
|
||||
|
||||
echo "----------------------------"
|
||||
echo "VID/PID patcher for Pico FIDO"
|
||||
@@ -87,7 +87,7 @@ fi
|
||||
LITTLE_VID="\x${VID:2:2}\x${VID:0:2}"
|
||||
LITTLE_PID="\x${PID:2:2}\x${PID:0:2}"
|
||||
|
||||
perl -pi -e "s/\xfe\xca\x31\x42\x$VERSION_MINOR\x$VERSION_MAJOR\x01\x02\x03\x01/$LITTLE_VID$LITTLE_PID\x$VERSION_MINOR\x$VERSION_MAJOR\x01\x02\x03\x01/" $UF2_FILE_OF
|
||||
perl -pi -e "s/[\x00-\xff]{4}\x$VERSION_MINOR\x$VERSION_MAJOR\x01\x02\x03\x01\x00\x00/$LITTLE_VID$LITTLE_PID\x$VERSION_MINOR\x$VERSION_MAJOR\x01\x02\x03\x01\x00\x00/" $UF2_FILE_OF
|
||||
|
||||
echo "Done!"
|
||||
echo ""
|
||||
|
||||
Submodule pico-hsm-sdk deleted from fa54da973c
1
pico-keys-sdk
Submodule
1
pico-keys-sdk
Submodule
Submodule pico-keys-sdk added at 580b0acffa
@@ -18,9 +18,20 @@ if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_P
|
||||
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
|
||||
endif ()
|
||||
|
||||
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG))
|
||||
set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG})
|
||||
message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')")
|
||||
endif ()
|
||||
|
||||
if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG)
|
||||
set(PICO_SDK_FETCH_FROM_GIT_TAG "master")
|
||||
message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG")
|
||||
endif()
|
||||
|
||||
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
|
||||
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
|
||||
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
|
||||
set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK")
|
||||
|
||||
if (NOT PICO_SDK_PATH)
|
||||
if (PICO_SDK_FETCH_FROM_GIT)
|
||||
@@ -29,11 +40,22 @@ if (NOT PICO_SDK_PATH)
|
||||
if (PICO_SDK_FETCH_FROM_GIT_PATH)
|
||||
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
|
||||
endif ()
|
||||
FetchContent_Declare(
|
||||
pico_sdk
|
||||
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||
GIT_TAG master
|
||||
)
|
||||
# GIT_SUBMODULES_RECURSE was added in 3.17
|
||||
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0")
|
||||
FetchContent_Declare(
|
||||
pico_sdk
|
||||
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||
GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
|
||||
GIT_SUBMODULES_RECURSE FALSE
|
||||
)
|
||||
else ()
|
||||
FetchContent_Declare(
|
||||
pico_sdk
|
||||
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||
GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
|
||||
)
|
||||
endif ()
|
||||
|
||||
if (NOT pico_sdk)
|
||||
message("Downloading Raspberry Pi Pico SDK")
|
||||
FetchContent_Populate(pico_sdk)
|
||||
|
||||
55
sdkconfig.defaults
Normal file
55
sdkconfig.defaults
Normal file
@@ -0,0 +1,55 @@
|
||||
# This file was generated using idf.py save-defconfig. It can be edited manually.
|
||||
# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration
|
||||
#
|
||||
IGNORE_UNKNOWN_FILES_FOR_MANAGED_COMPONENTS=1
|
||||
|
||||
CONFIG_TINYUSB=y
|
||||
CONFIG_TINYUSB_TASK_STACK_SIZE=16384
|
||||
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="pico-keys-sdk/config/esp32/partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="pico-keys-sdk/config/esp32/partitions.csv"
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||
CONFIG_WL_SECTOR_SIZE_512=y
|
||||
CONFIG_WL_SECTOR_MODE_PERF=y
|
||||
COMPILER_OPTIMIZATION="Performance"
|
||||
|
||||
CONFIG_MBEDTLS_CMAC_C=y
|
||||
CONFIG_MBEDTLS_CHACHA20_C=y
|
||||
CONFIG_MBEDTLS_POLY1305_C=y
|
||||
CONFIG_MBEDTLS_CHACHAPOLY_C=y
|
||||
CONFIG_MBEDTLS_HKDF_C=y
|
||||
CONFIG_MBEDTLS_HARDWARE_ECC=y
|
||||
CONFIG_MBEDTLS_HARDWARE_GCM=y
|
||||
# CONFIG_MBEDTLS_HARDWARE_MPI is not set
|
||||
CONFIG_MBEDTLS_HARDWARE_SHA=y
|
||||
CONFIG_MBEDTLS_HARDWARE_AES=y
|
||||
# CONFIG_MBEDTLS_ROM_MD5 is not set
|
||||
CONFIG_MBEDTLS_SHA512_C=y
|
||||
CONFIG_MBEDTLS_TLS_DISABLED=y
|
||||
# CONFIG_MBEDTLS_TLS_ENABLED is not set
|
||||
# CONFIG_ESP_TLS_USE_DS_PERIPHERAL is not set
|
||||
# CONFIG_ESP_WIFI_ENABLED is not set
|
||||
# CONFIG_ESP_WIFI_MBEDTLS_CRYPTO is not set
|
||||
# CONFIG_ESP_WIFI_MBEDTLS_TLS_CLIENT is not set
|
||||
# CONFIG_WPA_MBEDTLS_CRYPTO is not set
|
||||
# CONFIG_MBEDTLS_PSK_MODES is not set
|
||||
# CONFIG_MBEDTLS_KEY_EXCHANGE_RSA is not set
|
||||
# CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE is not set
|
||||
# CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA is not set
|
||||
# CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA is not set
|
||||
# CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA is not set
|
||||
# CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA is not set
|
||||
# CONFIG_MBEDTLS_SSL_RENEGOTIATION is not set
|
||||
# CONFIG_MBEDTLS_SSL_PROTO_TLS1_2 is not set
|
||||
# CONFIG_MBEDTLS_SSL_PROTO_GMTSSL1_1 is not set
|
||||
# CONFIG_MBEDTLS_SSL_PROTO_DTLS is not set
|
||||
# CONFIG_MBEDTLS_SSL_ALPN is not set
|
||||
# CONFIG_MBEDTLS_CLIENT_SSL_SESSION_TICKETS is not set
|
||||
# CONFIG_MBEDTLS_SERVER_SSL_SESSION_TICKETS is not set
|
||||
# CONFIG_ESP32_WIFI_ENABLE_WPA3_SAE is not set
|
||||
# CONFIG_ESP32_WIFI_ENABLE_WPA3_OWE_STA is not set
|
||||
# CONFIG_ESP_WIFI_ENABLE_WPA3_SAE is not set
|
||||
# CONFIG_ESP_WIFI_ENABLE_WPA3_OWE_STA is not set
|
||||
|
||||
CONFIG_ESP_COREDUMP_ENABLE_TO_UART=y
|
||||
6
src/fido/CMakeLists.txt
Normal file
6
src/fido/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
idf_component_register(
|
||||
SRCS ${SOURCES}
|
||||
INCLUDE_DIRS . ../../pico-keys-sdk/src ../../pico-keys-sdk/src/fs ../../pico-keys-sdk/src/rng ../../pico-keys-sdk/src/usb ../../pico-keys-sdk/tinycbor/src
|
||||
REQUIRES bootloader_support esp_partition esp_tinyusb zorxx__neopixel mbedtls efuse
|
||||
)
|
||||
idf_component_set_property(${COMPONENT_NAME} WHOLE_ARCHIVE ON)
|
||||
249
src/fido/cbor.c
249
src/fido/cbor.c
@@ -15,15 +15,18 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "pico_keys.h"
|
||||
#if !defined(ENABLE_EMULATION) && !defined(ESP_PLATFORM)
|
||||
#include "pico/stdlib.h"
|
||||
#include "ctap2_cbor.h"
|
||||
#endif
|
||||
#include "hid/ctap_hid.h"
|
||||
#include "ctap.h"
|
||||
#include "ctap_hid.h"
|
||||
#include "fido.h"
|
||||
#include "hsm.h"
|
||||
#include "usb.h"
|
||||
#include "apdu.h"
|
||||
#include "management.h"
|
||||
#include "ctap2_cbor.h"
|
||||
#include "version.h"
|
||||
|
||||
const bool _btrue = true, _bfalse = false;
|
||||
|
||||
@@ -39,72 +42,224 @@ int cbor_config(const uint8_t *data, size_t len);
|
||||
int cbor_vendor(const uint8_t *data, size_t len);
|
||||
int cbor_large_blobs(const uint8_t *data, size_t len);
|
||||
|
||||
const uint8_t aaguid[16] = {0x89, 0xFB, 0x94, 0xB7, 0x06, 0xC9, 0x36, 0x73, 0x9B, 0x7E, 0x30, 0x52, 0x6D, 0x96, 0x81, 0x45}; // First 16 bytes of SHA256("Pico FIDO2")
|
||||
extern int cmd_read_config();
|
||||
|
||||
static const uint8_t *cbor_data = NULL;
|
||||
static size_t cbor_len = 0;
|
||||
static uint8_t cmd = 0;
|
||||
const uint8_t aaguid[16] = { 0x89, 0xFB, 0x94, 0xB7, 0x06, 0xC9, 0x36, 0x73, 0x9B, 0x7E, 0x30, 0x52, 0x6D, 0x96, 0x81, 0x45 }; // First 16 bytes of SHA256("Pico FIDO2")
|
||||
|
||||
const uint8_t *cbor_data = NULL;
|
||||
size_t cbor_len = 0;
|
||||
uint8_t cbor_cmd = 0;
|
||||
|
||||
int cbor_parse(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
if (len == 0)
|
||||
if (len == 0 && cmd == CTAPHID_CBOR) {
|
||||
return CTAP1_ERR_INVALID_LEN;
|
||||
DEBUG_DATA(data+1,len-1);
|
||||
driver_prepare_response();
|
||||
if (cmd == CTAPHID_CBOR) {
|
||||
if (data[0] == CTAP_MAKE_CREDENTIAL)
|
||||
return cbor_make_credential(data + 1, len - 1);
|
||||
if (data[0] == CTAP_GET_INFO)
|
||||
return cbor_get_info();
|
||||
else if (data[0] == CTAP_RESET)
|
||||
return cbor_reset();
|
||||
else if (data[0] == CTAP_CLIENT_PIN)
|
||||
return cbor_client_pin(data + 1, len - 1);
|
||||
else if (data[0] == CTAP_GET_ASSERTION)
|
||||
return cbor_get_assertion(data + 1, len - 1, false);
|
||||
else if (data[0] == CTAP_GET_NEXT_ASSERTION)
|
||||
return cbor_get_next_assertion(data + 1, len - 1);
|
||||
else if (data[0] == CTAP_SELECTION)
|
||||
return cbor_selection();
|
||||
else if (data[0] == CTAP_CREDENTIAL_MGMT || data[0] == 0x41)
|
||||
return cbor_cred_mgmt(data + 1, len - 1);
|
||||
else if (data[0] == CTAP_CONFIG)
|
||||
return cbor_config(data + 1, len - 1);
|
||||
else if (data[0] == CTAP_LARGE_BLOBS)
|
||||
return cbor_large_blobs(data + 1, len - 1);
|
||||
}
|
||||
else if (cmd == CTAP_VENDOR_CBOR) {
|
||||
return cbor_vendor(data, len);
|
||||
if (len > 0) {
|
||||
DEBUG_DATA(data + 1, len - 1);
|
||||
}
|
||||
return CTAP2_ERR_INVALID_CBOR;
|
||||
if (cap_supported(CAP_FIDO2)) {
|
||||
if (cmd == CTAPHID_CBOR) {
|
||||
if (data[0] == CTAP_MAKE_CREDENTIAL) {
|
||||
return cbor_make_credential(data + 1, len - 1);
|
||||
}
|
||||
if (data[0] == CTAP_GET_INFO) {
|
||||
return cbor_get_info();
|
||||
}
|
||||
else if (data[0] == CTAP_RESET) {
|
||||
return cbor_reset();
|
||||
}
|
||||
else if (data[0] == CTAP_CLIENT_PIN) {
|
||||
return cbor_client_pin(data + 1, len - 1);
|
||||
}
|
||||
else if (data[0] == CTAP_GET_ASSERTION) {
|
||||
return cbor_get_assertion(data + 1, len - 1, false);
|
||||
}
|
||||
else if (data[0] == CTAP_GET_NEXT_ASSERTION) {
|
||||
return cbor_get_next_assertion(data + 1, len - 1);
|
||||
}
|
||||
else if (data[0] == CTAP_SELECTION) {
|
||||
return cbor_selection();
|
||||
}
|
||||
else if (data[0] == CTAP_CREDENTIAL_MGMT || data[0] == 0x41) {
|
||||
return cbor_cred_mgmt(data + 1, len - 1);
|
||||
}
|
||||
else if (data[0] == CTAP_CONFIG) {
|
||||
return cbor_config(data + 1, len - 1);
|
||||
}
|
||||
else if (data[0] == CTAP_LARGE_BLOBS) {
|
||||
return cbor_large_blobs(data + 1, len - 1);
|
||||
}
|
||||
}
|
||||
else if (cmd == CTAP_VENDOR_CBOR) {
|
||||
return cbor_vendor(data, len);
|
||||
}
|
||||
else if (cmd == 0xC2) {
|
||||
if (cmd_read_config() == 0x9000) {
|
||||
memmove(res_APDU-1, res_APDU, res_APDU_size);
|
||||
res_APDU_size -= 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return CTAP1_ERR_INVALID_CMD;
|
||||
}
|
||||
|
||||
void cbor_thread() {
|
||||
|
||||
void cbor_thread(void) {
|
||||
card_init_core1();
|
||||
while (1) {
|
||||
uint32_t m;
|
||||
queue_remove_blocking(&usb_to_card_q, &m);
|
||||
uint32_t flag = m + 1;
|
||||
queue_add_blocking(&card_to_usb_q, &flag);
|
||||
|
||||
if (m == EV_EXIT) {
|
||||
break;
|
||||
}
|
||||
apdu.sw = cbor_parse(cbor_cmd, cbor_data, cbor_len);
|
||||
if (apdu.sw == 0) {
|
||||
DEBUG_DATA(res_APDU, res_APDU_size);
|
||||
}
|
||||
else {
|
||||
if (apdu.sw >= CTAP1_ERR_INVALID_CHANNEL) {
|
||||
res_APDU[-1] = apdu.sw;
|
||||
apdu.sw = 0;
|
||||
}
|
||||
else {
|
||||
res_APDU[0] = apdu.sw;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
finished_data_size = res_APDU_size + 1;
|
||||
|
||||
apdu.sw = cbor_parse(cmd, cbor_data, cbor_len);
|
||||
if (apdu.sw == 0)
|
||||
DEBUG_DATA(res_APDU + 1, res_APDU_size);
|
||||
|
||||
finished_data_size = res_APDU_size+1;
|
||||
uint32_t flag = EV_EXEC_FINISHED;
|
||||
flag = EV_EXEC_FINISHED;
|
||||
queue_add_blocking(&card_to_usb_q, &flag);
|
||||
}
|
||||
#ifdef ESP_PLATFORM
|
||||
vTaskDelete(NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
int cbor_process(uint8_t last_cmd, const uint8_t *data, size_t len) {
|
||||
cbor_data = data;
|
||||
cbor_len = len;
|
||||
cmd = last_cmd;
|
||||
cbor_cmd = last_cmd;
|
||||
ctap_resp->init.data[0] = 0;
|
||||
res_APDU = ctap_resp->init.data + 1;
|
||||
res_APDU_size = 0;
|
||||
return 1;
|
||||
return 2; // CBOR processing
|
||||
}
|
||||
|
||||
CborError COSE_key_params(int crv, int alg, mbedtls_ecp_group *grp, mbedtls_ecp_point *Q, CborEncoder *mapEncoderParent, CborEncoder *mapEncoder) {
|
||||
CborError error = CborNoError;
|
||||
int kty = 1;
|
||||
if (crv == FIDO2_CURVE_P256 || crv == FIDO2_CURVE_P384 || crv == FIDO2_CURVE_P521 ||
|
||||
crv == FIDO2_CURVE_P256K1) {
|
||||
kty = 2;
|
||||
}
|
||||
|
||||
CBOR_CHECK(cbor_encoder_create_map(mapEncoderParent, mapEncoder, kty == 2 ? 5 : 4));
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(mapEncoder, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(mapEncoder, kty));
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(mapEncoder, 3));
|
||||
CBOR_CHECK(cbor_encode_negative_int(mapEncoder, -alg));
|
||||
|
||||
CBOR_CHECK(cbor_encode_negative_int(mapEncoder, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(mapEncoder, crv));
|
||||
|
||||
|
||||
CBOR_CHECK(cbor_encode_negative_int(mapEncoder, 2));
|
||||
uint8_t pkey[67];
|
||||
if (kty == 2) {
|
||||
size_t plen = mbedtls_mpi_size(&grp->P);
|
||||
CBOR_CHECK(mbedtls_mpi_write_binary(&Q->X, pkey, plen));
|
||||
CBOR_CHECK(cbor_encode_byte_string(mapEncoder, pkey, plen));
|
||||
|
||||
CBOR_CHECK(cbor_encode_negative_int(mapEncoder, 3));
|
||||
|
||||
CBOR_CHECK(mbedtls_mpi_write_binary(&Q->Y, pkey, plen));
|
||||
CBOR_CHECK(cbor_encode_byte_string(mapEncoder, pkey, plen));
|
||||
}
|
||||
else {
|
||||
size_t olen = 0;
|
||||
CBOR_CHECK(mbedtls_ecp_point_write_binary(grp, Q, MBEDTLS_ECP_PF_COMPRESSED, &olen, pkey,
|
||||
sizeof(pkey)));
|
||||
CBOR_CHECK(cbor_encode_byte_string(mapEncoder, pkey, olen));
|
||||
}
|
||||
|
||||
CBOR_CHECK(cbor_encoder_close_container(mapEncoderParent, mapEncoder));
|
||||
err:
|
||||
return error;
|
||||
}
|
||||
CborError COSE_key(mbedtls_ecp_keypair *key, CborEncoder *mapEncoderParent,
|
||||
CborEncoder *mapEncoder) {
|
||||
int crv = mbedtls_curve_to_fido(key->grp.id), alg = 0;
|
||||
if (key->grp.id == MBEDTLS_ECP_DP_SECP256R1) {
|
||||
alg = FIDO2_ALG_ES256;
|
||||
}
|
||||
else if (key->grp.id == MBEDTLS_ECP_DP_SECP384R1) {
|
||||
alg = FIDO2_ALG_ES384;
|
||||
}
|
||||
else if (key->grp.id == MBEDTLS_ECP_DP_SECP521R1) {
|
||||
alg = FIDO2_ALG_ES512;
|
||||
}
|
||||
else if (key->grp.id == MBEDTLS_ECP_DP_SECP256K1) {
|
||||
alg = FIDO2_ALG_ES256K;
|
||||
}
|
||||
else if (key->grp.id == MBEDTLS_ECP_DP_CURVE25519) {
|
||||
alg = FIDO2_ALG_ECDH_ES_HKDF_256;
|
||||
}
|
||||
#ifdef MBEDTLS_EDDSA_C
|
||||
else if (key->grp.id == MBEDTLS_ECP_DP_ED25519) {
|
||||
alg = FIDO2_ALG_EDDSA;
|
||||
}
|
||||
#endif
|
||||
return COSE_key_params(crv, alg, &key->grp, &key->Q, mapEncoderParent, mapEncoder);
|
||||
}
|
||||
CborError COSE_key_shared(mbedtls_ecdh_context *key,
|
||||
CborEncoder *mapEncoderParent,
|
||||
CborEncoder *mapEncoder) {
|
||||
int crv = mbedtls_curve_to_fido(key->ctx.mbed_ecdh.grp.id), alg = FIDO2_ALG_ECDH_ES_HKDF_256;
|
||||
return COSE_key_params(crv, alg, &key->ctx.mbed_ecdh.grp, &key->ctx.mbed_ecdh.Q, mapEncoderParent, mapEncoder);
|
||||
}
|
||||
CborError COSE_public_key(int alg, CborEncoder *mapEncoderParent, CborEncoder *mapEncoder) {
|
||||
CborError error = CborNoError;
|
||||
CBOR_CHECK(cbor_encoder_create_map(mapEncoderParent, mapEncoder, 2));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(mapEncoder, "alg"));
|
||||
CBOR_CHECK(cbor_encode_negative_int(mapEncoder, -alg));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(mapEncoder, "type"));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(mapEncoder, "public-key"));
|
||||
CBOR_CHECK(cbor_encoder_close_container(mapEncoderParent, mapEncoder));
|
||||
err:
|
||||
return error;
|
||||
}
|
||||
CborError COSE_read_key(CborValue *f, int64_t *kty, int64_t *alg, int64_t *crv, CborByteString *kax, CborByteString *kay) {
|
||||
int64_t kkey = 0;
|
||||
CborError error = CborNoError;
|
||||
CBOR_PARSE_MAP_START(*f, 0)
|
||||
{
|
||||
CBOR_FIELD_GET_INT(kkey, 0);
|
||||
if (kkey == 1) {
|
||||
CBOR_FIELD_GET_INT(*kty, 0);
|
||||
}
|
||||
else if (kkey == 3) {
|
||||
CBOR_FIELD_GET_INT(*alg, 0);
|
||||
}
|
||||
else if (kkey == -1) {
|
||||
CBOR_FIELD_GET_INT(*crv, 0);
|
||||
}
|
||||
else if (kkey == -2) {
|
||||
CBOR_FIELD_GET_BYTES(*kax, 0);
|
||||
}
|
||||
else if (kkey == -3) {
|
||||
CBOR_FIELD_GET_BYTES(*kay, 0);
|
||||
}
|
||||
else {
|
||||
CBOR_ADVANCE(0);
|
||||
}
|
||||
}
|
||||
CBOR_PARSE_MAP_END(*f, 0);
|
||||
err:
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,11 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ESP_PLATFORM
|
||||
#include "common.h"
|
||||
#else
|
||||
#define MBEDTLS_ALLOW_PRIVATE_ACCESS
|
||||
#endif
|
||||
#include "mbedtls/ecp.h"
|
||||
#include "mbedtls/ecdh.h"
|
||||
#include "mbedtls/sha256.h"
|
||||
@@ -23,16 +27,20 @@
|
||||
#include "cbor.h"
|
||||
#include "ctap.h"
|
||||
#include "ctap2_cbor.h"
|
||||
#if !defined(ENABLE_EMULATION) && !defined(ESP_PLATFORM)
|
||||
#include "bsp/board.h"
|
||||
#endif
|
||||
#include "hid/ctap_hid.h"
|
||||
#include "fido.h"
|
||||
#include "files.h"
|
||||
#include "random.h"
|
||||
#include "crypto_utils.h"
|
||||
#include "hsm.h"
|
||||
#include "pico_keys.h"
|
||||
#include "apdu.h"
|
||||
#include "kek.h"
|
||||
|
||||
uint32_t usage_timer = 0, initial_usage_time_limit = 0;
|
||||
uint32_t max_usage_time_period = 600*1000;
|
||||
uint32_t max_usage_time_period = 600 * 1000;
|
||||
bool needs_power_cycle = false;
|
||||
static mbedtls_ecdh_context hkey;
|
||||
static bool hkey_init = false;
|
||||
@@ -47,18 +55,21 @@ int beginUsingPinUvAuthToken(bool userIsPresent) {
|
||||
}
|
||||
|
||||
void clearUserPresentFlag() {
|
||||
if (paut.in_use == true)
|
||||
if (paut.in_use == true) {
|
||||
paut.user_present = false;
|
||||
}
|
||||
}
|
||||
|
||||
void clearUserVerifiedFlag() {
|
||||
if (paut.in_use == true)
|
||||
if (paut.in_use == true) {
|
||||
paut.user_verified = false;
|
||||
}
|
||||
}
|
||||
|
||||
void clearPinUvAuthTokenPermissionsExceptLbw() {
|
||||
if (paut.in_use == true)
|
||||
if (paut.in_use == true) {
|
||||
paut.permissions = CTAP_PERMISSION_LBW;
|
||||
}
|
||||
}
|
||||
|
||||
void stopUsingPinUvAuthToken() {
|
||||
@@ -73,28 +84,36 @@ void stopUsingPinUvAuthToken() {
|
||||
}
|
||||
|
||||
bool getUserPresentFlagValue() {
|
||||
if (paut.in_use != true)
|
||||
if (paut.in_use != true) {
|
||||
paut.user_present = false;
|
||||
}
|
||||
return paut.user_present;
|
||||
}
|
||||
|
||||
bool getUserVerifiedFlagValue() {
|
||||
if (paut.in_use != true)
|
||||
bool getUserVerifiedFlagValue() {
|
||||
if (paut.in_use != true) {
|
||||
paut.user_verified = false;
|
||||
}
|
||||
return paut.user_verified;
|
||||
}
|
||||
}
|
||||
|
||||
int regenerate() {
|
||||
if (hkey_init == true)
|
||||
if (hkey_init == true) {
|
||||
mbedtls_ecdh_free(&hkey);
|
||||
}
|
||||
|
||||
mbedtls_ecdh_init(&hkey);
|
||||
hkey_init = true;
|
||||
mbedtls_ecdh_setup(&hkey, MBEDTLS_ECP_DP_SECP256R1);
|
||||
int ret = mbedtls_ecdh_gen_public(&hkey.ctx.mbed_ecdh.grp, &hkey.ctx.mbed_ecdh.d, &hkey.ctx.mbed_ecdh.Q, random_gen, NULL);
|
||||
int ret = mbedtls_ecdh_gen_public(&hkey.ctx.mbed_ecdh.grp,
|
||||
&hkey.ctx.mbed_ecdh.d,
|
||||
&hkey.ctx.mbed_ecdh.Q,
|
||||
random_gen,
|
||||
NULL);
|
||||
mbedtls_mpi_lset(&hkey.ctx.mbed_ecdh.Qp.Z, 1);
|
||||
if (ret != 0)
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -102,17 +121,38 @@ int kdf(uint8_t protocol, const mbedtls_mpi *z, uint8_t *sharedSecret) {
|
||||
int ret = 0;
|
||||
uint8_t buf[32];
|
||||
ret = mbedtls_mpi_write_binary(z, buf, sizeof(buf));
|
||||
if (ret != 0)
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
if (protocol == 1) {
|
||||
return mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), buf, sizeof(buf), sharedSecret);
|
||||
return mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
|
||||
buf,
|
||||
sizeof(buf),
|
||||
sharedSecret);
|
||||
}
|
||||
else if (protocol == 2) {
|
||||
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
|
||||
ret = mbedtls_hkdf(md_info, NULL, 0, buf, sizeof(buf), (uint8_t *)"CTAP2 HMAC key", 14, sharedSecret, 32);
|
||||
if (ret != 0)
|
||||
ret = mbedtls_hkdf(md_info,
|
||||
NULL,
|
||||
0,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
(uint8_t *) "CTAP2 HMAC key",
|
||||
14,
|
||||
sharedSecret,
|
||||
32);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
return mbedtls_hkdf(md_info, NULL, 0, buf, sizeof(buf), (uint8_t *)"CTAP2 AES key", 13, sharedSecret+32, 32);
|
||||
}
|
||||
return mbedtls_hkdf(md_info,
|
||||
NULL,
|
||||
0,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
(uint8_t *) "CTAP2 AES key",
|
||||
13,
|
||||
sharedSecret + 32,
|
||||
32);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -120,7 +160,12 @@ int kdf(uint8_t protocol, const mbedtls_mpi *z, uint8_t *sharedSecret) {
|
||||
int ecdh(uint8_t protocol, const mbedtls_ecp_point *Q, uint8_t *sharedSecret) {
|
||||
mbedtls_mpi z;
|
||||
mbedtls_mpi_init(&z);
|
||||
int ret = mbedtls_ecdh_compute_shared(&hkey.ctx.mbed_ecdh.grp, &z, Q, &hkey.ctx.mbed_ecdh.d, random_gen, NULL);
|
||||
int ret = mbedtls_ecdh_compute_shared(&hkey.ctx.mbed_ecdh.grp,
|
||||
&z,
|
||||
Q,
|
||||
&hkey.ctx.mbed_ecdh.d,
|
||||
random_gen,
|
||||
NULL);
|
||||
ret = kdf(protocol, &z, sharedSecret);
|
||||
mbedtls_mpi_free(&z);
|
||||
return ret;
|
||||
@@ -129,7 +174,7 @@ int ecdh(uint8_t protocol, const mbedtls_ecp_point *Q, uint8_t *sharedSecret) {
|
||||
int resetPinUvAuthToken() {
|
||||
uint8_t t[32];
|
||||
random_gen(NULL, t, sizeof(t));
|
||||
flash_write_data_to_file(ef_authtoken, t, sizeof(t));
|
||||
file_put_data(ef_authtoken, t, sizeof(t));
|
||||
paut.permissions = 0;
|
||||
paut.data = file_get_data(ef_authtoken);
|
||||
paut.len = file_get_size(ef_authtoken);
|
||||
@@ -138,60 +183,70 @@ int resetPinUvAuthToken() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int encrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, size_t in_len, uint8_t *out) {
|
||||
int encrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, uint16_t in_len, uint8_t *out) {
|
||||
if (protocol == 1) {
|
||||
memcpy(out, in, in_len);
|
||||
return aes_encrypt(key, NULL, 32*8, HSM_AES_MODE_CBC, out, in_len);
|
||||
return aes_encrypt(key, NULL, 32 * 8, PICO_KEYS_AES_MODE_CBC, out, in_len);
|
||||
}
|
||||
else if (protocol == 2) {
|
||||
random_gen(NULL, out, IV_SIZE);
|
||||
memcpy(out + IV_SIZE, in, in_len);
|
||||
return aes_encrypt(key+32, out, 32*8, HSM_AES_MODE_CBC, out+IV_SIZE, in_len);
|
||||
return aes_encrypt(key + 32, out, 32 * 8, PICO_KEYS_AES_MODE_CBC, out + IV_SIZE, in_len);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int decrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, size_t in_len, uint8_t *out) {
|
||||
int decrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, uint16_t in_len, uint8_t *out) {
|
||||
if (protocol == 1) {
|
||||
memcpy(out, in, in_len);
|
||||
return aes_decrypt(key, NULL, 32*8, HSM_AES_MODE_CBC, out, in_len);
|
||||
return aes_decrypt(key, NULL, 32 * 8, PICO_KEYS_AES_MODE_CBC, out, in_len);
|
||||
}
|
||||
else if (protocol == 2) {
|
||||
memcpy(out, in+IV_SIZE, in_len);
|
||||
return aes_decrypt(key+32, in, 32*8, HSM_AES_MODE_CBC, out, in_len-IV_SIZE);
|
||||
memcpy(out, in + IV_SIZE, in_len - IV_SIZE);
|
||||
return aes_decrypt(key + 32, in, 32 * 8, PICO_KEYS_AES_MODE_CBC, out, in_len - IV_SIZE);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int authenticate(uint8_t protocol, const uint8_t *key, const uint8_t *data, size_t len, uint8_t *sign) {
|
||||
int authenticate(uint8_t protocol,
|
||||
const uint8_t *key,
|
||||
const uint8_t *data,
|
||||
size_t len,
|
||||
uint8_t *sign) {
|
||||
uint8_t hmac[32];
|
||||
int ret = mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), key, 32, data, len, hmac);
|
||||
if (ret != 0)
|
||||
int ret =
|
||||
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), key, 32, data, len, hmac);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
if (protocol == 1) {
|
||||
memcpy(sign, hmac, 16);
|
||||
}
|
||||
else if (protocol == 2) {
|
||||
memcpy(sign, hmac, 32);
|
||||
}
|
||||
else
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int verify(uint8_t protocol, const uint8_t *key, const uint8_t *data, size_t len, uint8_t *sign) {
|
||||
int verify(uint8_t protocol, const uint8_t *key, const uint8_t *data, uint16_t len, uint8_t *sign) {
|
||||
uint8_t hmac[32];
|
||||
//if (paut.in_use == false)
|
||||
// return -2;
|
||||
int ret = mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), key, 32, data, len, hmac);
|
||||
if (ret != 0)
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
if (protocol == 1)
|
||||
}
|
||||
if (protocol == 1) {
|
||||
return memcmp(sign, hmac, 16);
|
||||
else if (protocol == 2)
|
||||
}
|
||||
else if (protocol == 2) {
|
||||
return memcmp(sign, hmac, 32);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -205,13 +260,17 @@ int getPublicKey() {
|
||||
}
|
||||
|
||||
int pinUvAuthTokenUsageTimerObserver() {
|
||||
if (usage_timer == 0)
|
||||
if (usage_timer == 0) {
|
||||
return -1;
|
||||
if (usage_timer+max_usage_time_period > board_millis()) {
|
||||
if (user_present_time_limit == 0 || user_present_time_limit+TRANSPORT_TIME_LIMIT < board_millis())
|
||||
}
|
||||
if (usage_timer + max_usage_time_period > board_millis()) {
|
||||
if (user_present_time_limit == 0 ||
|
||||
user_present_time_limit + TRANSPORT_TIME_LIMIT < board_millis()) {
|
||||
clearUserPresentFlag();
|
||||
}
|
||||
if (paut.in_use == true) {
|
||||
if (initial_usage_time_limit == 0 || initial_usage_time_limit+TRANSPORT_TIME_LIMIT < board_millis()) {
|
||||
if (initial_usage_time_limit == 0 ||
|
||||
initial_usage_time_limit + TRANSPORT_TIME_LIMIT < board_millis()) {
|
||||
stopUsingPinUvAuthToken();
|
||||
return 1;
|
||||
}
|
||||
@@ -221,6 +280,21 @@ int pinUvAuthTokenUsageTimerObserver() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int check_mkek_encrypted(const uint8_t *dhash) {
|
||||
if (file_get_size(ef_mkek) == MKEK_IV_SIZE + MKEK_KEY_SIZE) {
|
||||
hash_multi(dhash, 16, session_pin); // Only for storing MKEK
|
||||
uint8_t mkek[MKEK_SIZE] = {0};
|
||||
memcpy(mkek, file_get_data(ef_mkek), MKEK_IV_SIZE + MKEK_KEY_SIZE);
|
||||
int ret = store_mkek(mkek);
|
||||
mbedtls_platform_zeroize(mkek, sizeof(mkek));
|
||||
mbedtls_platform_zeroize(session_pin, sizeof(session_pin));
|
||||
if (ret != PICOKEY_OK) {
|
||||
return CTAP2_ERR_PIN_AUTH_INVALID;
|
||||
}
|
||||
}
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
uint8_t new_pin_mismatches = 0;
|
||||
|
||||
int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
@@ -231,20 +305,24 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
CborEncoder encoder, mapEncoder;
|
||||
CborValue map;
|
||||
CborError error = CborNoError;
|
||||
CborByteString pinUvAuthParam = {0}, newPinEnc = {0}, pinHashEnc = {0}, kax = {0}, kay = {0};
|
||||
CborCharString rpId = {0};
|
||||
CborByteString pinUvAuthParam = { 0 }, newPinEnc = { 0 }, pinHashEnc = { 0 }, kax = { 0 },
|
||||
kay = { 0 };
|
||||
CborCharString rpId = { 0 };
|
||||
CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map));
|
||||
uint64_t val_c = 1;
|
||||
if (hkey_init == false)
|
||||
if (hkey_init == false) {
|
||||
initialize();
|
||||
}
|
||||
CBOR_PARSE_MAP_START(map, 1)
|
||||
{
|
||||
uint64_t val_u = 0;
|
||||
CBOR_FIELD_GET_UINT(val_u, 1);
|
||||
if (val_c <= 2 && val_c != val_u)
|
||||
if (val_c <= 2 && val_c != val_u) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (val_u < val_c)
|
||||
}
|
||||
if (val_u < val_c) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
|
||||
}
|
||||
val_c = val_u + 1;
|
||||
if (val_u == 0x01) {
|
||||
CBOR_FIELD_GET_UINT(pinUvAuthProtocol, 1);
|
||||
@@ -253,28 +331,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
CBOR_FIELD_GET_UINT(subcommand, 1);
|
||||
}
|
||||
else if (val_u == 0x03) {
|
||||
int64_t key = 0;
|
||||
CBOR_PARSE_MAP_START(_f1, 2) {
|
||||
CBOR_FIELD_GET_INT(key, 2);
|
||||
if (key == 1) {
|
||||
CBOR_FIELD_GET_INT(kty, 2);
|
||||
}
|
||||
else if (key == 3) {
|
||||
CBOR_FIELD_GET_INT(alg, 2);
|
||||
}
|
||||
else if (key == -1) {
|
||||
CBOR_FIELD_GET_INT(crv, 2);
|
||||
}
|
||||
else if (key == -2) {
|
||||
CBOR_FIELD_GET_BYTES(kax, 2);
|
||||
}
|
||||
else if (key == -3) {
|
||||
CBOR_FIELD_GET_BYTES(kay, 2);
|
||||
}
|
||||
else
|
||||
CBOR_ADVANCE(2);
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f1, 2);
|
||||
CBOR_CHECK(COSE_read_key(&_f1, &kty, &alg, &crv, &kax, &kay));
|
||||
}
|
||||
else if (val_u == 0x04) {
|
||||
CBOR_FIELD_GET_BYTES(pinUvAuthParam, 1);
|
||||
@@ -294,13 +351,14 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
}
|
||||
CBOR_PARSE_MAP_END(map, 1);
|
||||
|
||||
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
|
||||
if (subcommand == 0x0)
|
||||
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_CBOR_PAYLOAD, 0);
|
||||
if (subcommand == 0x0) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
else if (subcommand == 0x1) { //getPINRetries
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, needs_power_cycle ? 2 : 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, (uint64_t)*file_get_data(ef_pin)));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, (uint64_t) *file_get_data(ef_pin)));
|
||||
if (needs_power_cycle) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04));
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, true));
|
||||
@@ -312,36 +370,30 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
|
||||
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, 5));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 2));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 3));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, -FIDO2_ALG_ECDH_ES_HKDF_256));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, FIDO2_CURVE_P256));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 2));
|
||||
uint8_t pkey[32];
|
||||
mbedtls_mpi_write_binary(&hkey.ctx.mbed_ecdh.Q.X, pkey, 32);
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, pkey, 32));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 3));
|
||||
mbedtls_mpi_write_binary(&hkey.ctx.mbed_ecdh.Q.Y, pkey, 32);
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, pkey, 32));
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
|
||||
CBOR_CHECK(COSE_key_shared(&hkey, &mapEncoder, &mapEncoder2));
|
||||
}
|
||||
else if (pinUvAuthProtocol == 0)
|
||||
else if (pinUvAuthProtocol == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
else
|
||||
}
|
||||
else {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
}
|
||||
else if (subcommand == 0x3) { //setPIN
|
||||
if (kax.present == false || kay.present == false || pinUvAuthProtocol == 0 || newPinEnc.present == false || pinUvAuthParam.present == false || alg == 0)
|
||||
if (kax.present == false || kay.present == false || pinUvAuthProtocol == 0 ||
|
||||
newPinEnc.present == false || pinUvAuthParam.present == false || alg == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2)
|
||||
}
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
if (file_has_data(ef_pin))
|
||||
}
|
||||
if (file_has_data(ef_pin)) {
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
if ((pinUvAuthProtocol == 1 && newPinEnc.len != 64) || (pinUvAuthProtocol == 2 && newPinEnc.len != 64+IV_SIZE))
|
||||
}
|
||||
if ((pinUvAuthProtocol == 1 && newPinEnc.len != 64) ||
|
||||
(pinUvAuthProtocol == 2 && newPinEnc.len != 64 + IV_SIZE)) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
if (mbedtls_mpi_read_binary(&hkey.ctx.mbed_ecdh.Qp.X, kax.data, kax.len) != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
@@ -349,50 +401,72 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
uint8_t sharedSecret[64];
|
||||
int ret = ecdh(pinUvAuthProtocol, &hkey.ctx.mbed_ecdh.Qp, sharedSecret);
|
||||
int ret = ecdh((uint8_t)pinUvAuthProtocol, &hkey.ctx.mbed_ecdh.Qp, sharedSecret);
|
||||
if (ret != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
if (verify(pinUvAuthProtocol, sharedSecret, newPinEnc.data, newPinEnc.len, pinUvAuthParam.data) != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (verify((uint8_t)pinUvAuthProtocol, sharedSecret, newPinEnc.data, (uint16_t)newPinEnc.len, pinUvAuthParam.data) != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
uint8_t paddedNewPin[64];
|
||||
ret = decrypt(pinUvAuthProtocol, sharedSecret, newPinEnc.data, newPinEnc.len, paddedNewPin);
|
||||
ret = decrypt((uint8_t)pinUvAuthProtocol, sharedSecret, newPinEnc.data, (uint16_t)newPinEnc.len, paddedNewPin);
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
if (ret != 0)
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (paddedNewPin[63] != 0)
|
||||
}
|
||||
if (paddedNewPin[63] != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION);
|
||||
}
|
||||
uint8_t pin_len = 0;
|
||||
while (paddedNewPin[pin_len] != 0 && pin_len < sizeof(paddedNewPin))
|
||||
while (paddedNewPin[pin_len] != 0 && pin_len < sizeof(paddedNewPin)) {
|
||||
pin_len++;
|
||||
}
|
||||
uint8_t minPin = 4;
|
||||
file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
|
||||
if (file_has_data(ef_minpin))
|
||||
if (file_has_data(ef_minpin)) {
|
||||
minPin = *file_get_data(ef_minpin);
|
||||
if (pin_len < minPin)
|
||||
}
|
||||
if (pin_len < minPin) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION);
|
||||
uint8_t hsh[34];
|
||||
}
|
||||
uint8_t hsh[34], dhash[32];
|
||||
hsh[0] = MAX_PIN_RETRIES;
|
||||
hsh[1] = pin_len;
|
||||
mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, hsh + 2);
|
||||
flash_write_data_to_file(ef_pin, hsh, 2+16);
|
||||
mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, dhash);
|
||||
double_hash_pin(dhash, 16, hsh + 2);
|
||||
file_put_data(ef_pin, hsh, 2 + 32);
|
||||
low_flash_available();
|
||||
|
||||
ret = check_mkek_encrypted(dhash);
|
||||
if (ret != PICOKEY_OK) {
|
||||
CBOR_ERROR(ret);
|
||||
}
|
||||
mbedtls_platform_zeroize(hsh, sizeof(hsh));
|
||||
mbedtls_platform_zeroize(dhash, sizeof(dhash));
|
||||
goto err; //No return
|
||||
}
|
||||
else if (subcommand == 0x4) { //changePIN
|
||||
if (kax.present == false || kay.present == false || pinUvAuthProtocol == 0 || newPinEnc.present == false || pinUvAuthParam.present == false || alg == 0 || pinHashEnc.present == false)
|
||||
if (kax.present == false || kay.present == false || pinUvAuthProtocol == 0 ||
|
||||
newPinEnc.present == false || pinUvAuthParam.present == false || alg == 0 ||
|
||||
pinHashEnc.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2)
|
||||
}
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
if (!file_has_data(ef_pin))
|
||||
}
|
||||
if (!file_has_data(ef_pin)) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_NOT_SET);
|
||||
if (*file_get_data(ef_pin) == 0)
|
||||
}
|
||||
if (*file_get_data(ef_pin) == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_BLOCKED);
|
||||
if ((pinUvAuthProtocol == 1 && (newPinEnc.len != 64 || pinHashEnc.len != 16)) || (pinUvAuthProtocol == 2 && (newPinEnc.len != 64+IV_SIZE || pinHashEnc.len != 16+IV_SIZE)))
|
||||
}
|
||||
if ((pinUvAuthProtocol == 1 && (newPinEnc.len != 64 || pinHashEnc.len != 16)) ||
|
||||
(pinUvAuthProtocol == 2 &&
|
||||
(newPinEnc.len != 64 + IV_SIZE || pinHashEnc.len != 16 + IV_SIZE))) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
if (mbedtls_mpi_read_binary(&hkey.ctx.mbed_ecdh.Qp.X, kax.data, kax.len) != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
@@ -400,7 +474,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
uint8_t sharedSecret[64];
|
||||
int ret = ecdh(pinUvAuthProtocol, &hkey.ctx.mbed_ecdh.Qp, sharedSecret);
|
||||
int ret = ecdh((uint8_t)pinUvAuthProtocol, &hkey.ctx.mbed_ecdh.Qp, sharedSecret);
|
||||
if (ret != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
@@ -408,91 +482,130 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
uint8_t tmp[80 + 32];
|
||||
memcpy(tmp, newPinEnc.data, newPinEnc.len);
|
||||
memcpy(tmp + newPinEnc.len, pinHashEnc.data, pinHashEnc.len);
|
||||
if (verify(pinUvAuthProtocol, sharedSecret, tmp, newPinEnc.len+pinHashEnc.len, pinUvAuthParam.data) != 0) {
|
||||
if (verify((uint8_t)pinUvAuthProtocol, sharedSecret, tmp, (uint16_t)(newPinEnc.len + pinHashEnc.len), pinUvAuthParam.data) != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
uint8_t pin_data[18];
|
||||
memcpy(pin_data, file_get_data(ef_pin), 18);
|
||||
uint8_t pin_data[34];
|
||||
memcpy(pin_data, file_get_data(ef_pin), 34);
|
||||
pin_data[0] -= 1;
|
||||
flash_write_data_to_file(ef_pin, pin_data, sizeof(pin_data));
|
||||
file_put_data(ef_pin, pin_data, sizeof(pin_data));
|
||||
low_flash_available();
|
||||
uint8_t retries = pin_data[0];
|
||||
uint8_t paddedNewPin[64];
|
||||
ret = decrypt(pinUvAuthProtocol, sharedSecret, pinHashEnc.data, pinHashEnc.len, paddedNewPin);
|
||||
ret = decrypt((uint8_t)pinUvAuthProtocol, sharedSecret, pinHashEnc.data, (uint16_t)pinHashEnc.len, paddedNewPin);
|
||||
if (ret != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
if (memcmp(paddedNewPin, file_get_data(ef_pin)+2, 16) != 0) {
|
||||
uint8_t dhash[32];
|
||||
double_hash_pin(paddedNewPin, 16, dhash);
|
||||
if (memcmp(dhash, file_get_data(ef_pin) + 2, 32) != 0) {
|
||||
regenerate();
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
if (retries == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_BLOCKED);
|
||||
}
|
||||
if (++new_pin_mismatches >= 3) {
|
||||
if (++new_pin_mismatches >= 3) {
|
||||
needs_power_cycle = true;
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_BLOCKED);
|
||||
}
|
||||
else
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_INVALID);
|
||||
}
|
||||
}
|
||||
hash_multi(paddedNewPin, 16, session_pin);
|
||||
pin_data[0] = MAX_PIN_RETRIES;
|
||||
flash_write_data_to_file(ef_pin, pin_data, sizeof(pin_data));
|
||||
file_put_data(ef_pin, pin_data, sizeof(pin_data));
|
||||
low_flash_available();
|
||||
new_pin_mismatches = 0;
|
||||
ret = decrypt(pinUvAuthProtocol, sharedSecret, newPinEnc.data, newPinEnc.len, paddedNewPin);
|
||||
ret = decrypt((uint8_t)pinUvAuthProtocol, sharedSecret, newPinEnc.data, (uint16_t)newPinEnc.len, paddedNewPin);
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
if (paddedNewPin[63] != 0)
|
||||
if (paddedNewPin[63] != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
uint8_t pin_len = 0;
|
||||
while (paddedNewPin[pin_len] != 0 && pin_len < sizeof(paddedNewPin))
|
||||
while (paddedNewPin[pin_len] != 0 && pin_len < sizeof(paddedNewPin)) {
|
||||
pin_len++;
|
||||
}
|
||||
uint8_t minPin = 4;
|
||||
file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
|
||||
if (file_has_data(ef_minpin))
|
||||
if (file_has_data(ef_minpin)) {
|
||||
minPin = *file_get_data(ef_minpin);
|
||||
if (pin_len < minPin)
|
||||
}
|
||||
if (pin_len < minPin) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION);
|
||||
uint8_t hsh[33];
|
||||
}
|
||||
uint8_t hsh[34];
|
||||
hsh[0] = MAX_PIN_RETRIES;
|
||||
hsh[1] = pin_len;
|
||||
mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, hsh + 2);
|
||||
if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1 && memcmp(hsh+2, file_get_data(ef_pin)+2, 16) == 0)
|
||||
mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, dhash);
|
||||
double_hash_pin(dhash, 16, hsh + 2);
|
||||
if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1 &&
|
||||
memcmp(hsh + 2, file_get_data(ef_pin) + 2, 32) == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION);
|
||||
flash_write_data_to_file(ef_pin, hsh, 2+16);
|
||||
}
|
||||
|
||||
uint8_t mkek[MKEK_SIZE] = {0};
|
||||
ret = load_mkek(mkek);
|
||||
if (ret != PICOKEY_OK) {
|
||||
CBOR_ERROR(ret);
|
||||
}
|
||||
file_put_data(ef_pin, hsh, 2 + 32);
|
||||
|
||||
ret = check_mkek_encrypted(dhash);
|
||||
if (ret != PICOKEY_OK) {
|
||||
CBOR_ERROR(ret);
|
||||
}
|
||||
|
||||
hash_multi(dhash, 16, session_pin);
|
||||
ret = store_mkek(mkek);
|
||||
mbedtls_platform_zeroize(mkek, sizeof(mkek));
|
||||
if (ret != PICOKEY_OK) {
|
||||
CBOR_ERROR(ret);
|
||||
}
|
||||
mbedtls_platform_zeroize(hsh, sizeof(hsh));
|
||||
mbedtls_platform_zeroize(dhash, sizeof(dhash));
|
||||
if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1) {
|
||||
uint8_t *tmp = (uint8_t *)calloc(1, file_get_size(ef_minpin));
|
||||
memcpy(tmp, file_get_data(ef_minpin), file_get_size(ef_minpin));
|
||||
tmp[1] = 0;
|
||||
flash_write_data_to_file(ef_minpin, tmp, file_get_size(ef_minpin));
|
||||
free(tmp);
|
||||
uint8_t *tmpf = (uint8_t *) calloc(1, file_get_size(ef_minpin));
|
||||
memcpy(tmpf, file_get_data(ef_minpin), file_get_size(ef_minpin));
|
||||
tmpf[1] = 0;
|
||||
file_put_data(ef_minpin, tmpf, file_get_size(ef_minpin));
|
||||
free(tmpf);
|
||||
}
|
||||
low_flash_available();
|
||||
resetPinUvAuthToken();
|
||||
goto err; // No return
|
||||
}
|
||||
else if (subcommand == 0x9 || subcommand == 0x5) { //getPinUvAuthTokenUsingPinWithPermissions
|
||||
if (kax.present == false || kay.present == false || pinUvAuthProtocol == 0 || alg == 0 || pinHashEnc.present == false)
|
||||
if (kax.present == false || kay.present == false || pinUvAuthProtocol == 0 || alg == 0 ||
|
||||
pinHashEnc.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2)
|
||||
}
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
if (subcommand == 0x5 && (permissions != 0 || rpId.present == true))
|
||||
}
|
||||
if (subcommand == 0x5 && (permissions != 0 || rpId.present == true)) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
if (subcommand == 0x9) {
|
||||
if (permissions == 0)
|
||||
if (permissions == 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
if ((permissions & CTAP_PERMISSION_BE)) // Not supported yet
|
||||
}
|
||||
if ((permissions & CTAP_PERMISSION_BE)) { // Not supported yet
|
||||
CBOR_ERROR(CTAP2_ERR_UNAUTHORIZED_PERMISSION);
|
||||
}
|
||||
|
||||
}
|
||||
if (!file_has_data(ef_pin))
|
||||
if (!file_has_data(ef_pin)) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_NOT_SET);
|
||||
if (*file_get_data(ef_pin) == 0)
|
||||
}
|
||||
if (*file_get_data(ef_pin) == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_BLOCKED);
|
||||
}
|
||||
if (mbedtls_mpi_read_binary(&hkey.ctx.mbed_ecdh.Qp.X, kax.data, kax.len) != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
@@ -500,62 +613,79 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
uint8_t sharedSecret[64];
|
||||
int ret = ecdh(pinUvAuthProtocol, &hkey.ctx.mbed_ecdh.Qp, sharedSecret);
|
||||
int ret = ecdh((uint8_t)pinUvAuthProtocol, &hkey.ctx.mbed_ecdh.Qp, sharedSecret);
|
||||
if (ret != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
uint8_t pin_data[18];
|
||||
memcpy(pin_data, file_get_data(ef_pin), 18);
|
||||
uint8_t pin_data[34];
|
||||
memcpy(pin_data, file_get_data(ef_pin), 34);
|
||||
pin_data[0] -= 1;
|
||||
flash_write_data_to_file(ef_pin, pin_data, sizeof(pin_data));
|
||||
file_put_data(ef_pin, pin_data, sizeof(pin_data));
|
||||
low_flash_available();
|
||||
uint8_t retries = pin_data[0];
|
||||
uint8_t paddedNewPin[64], poff = (pinUvAuthProtocol-1)*IV_SIZE;
|
||||
ret = decrypt(pinUvAuthProtocol, sharedSecret, pinHashEnc.data, pinHashEnc.len, paddedNewPin);
|
||||
uint8_t paddedNewPin[64], poff = ((uint8_t)pinUvAuthProtocol - 1) * IV_SIZE;
|
||||
ret = decrypt((uint8_t)pinUvAuthProtocol, sharedSecret, pinHashEnc.data, (uint16_t)pinHashEnc.len, paddedNewPin);
|
||||
if (ret != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
if (memcmp(paddedNewPin, file_get_data(ef_pin)+2, 16) != 0) {
|
||||
uint8_t dhash[32];
|
||||
double_hash_pin(paddedNewPin, 16, dhash);
|
||||
if (memcmp(dhash, file_get_data(ef_pin) + 2, 32) != 0) {
|
||||
regenerate();
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
if (retries == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_BLOCKED);
|
||||
}
|
||||
if (++new_pin_mismatches >= 3) {
|
||||
if (++new_pin_mismatches >= 3) {
|
||||
needs_power_cycle = true;
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_BLOCKED);
|
||||
}
|
||||
else
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
ret = check_mkek_encrypted(paddedNewPin);
|
||||
if (ret != PICOKEY_OK) {
|
||||
CBOR_ERROR(ret);
|
||||
}
|
||||
|
||||
hash_multi(paddedNewPin, 16, session_pin);
|
||||
pin_data[0] = MAX_PIN_RETRIES;
|
||||
new_pin_mismatches = 0;
|
||||
flash_write_data_to_file(ef_pin, pin_data, sizeof(pin_data));
|
||||
file_put_data(ef_pin, pin_data, sizeof(pin_data));
|
||||
mbedtls_platform_zeroize(pin_data, sizeof(pin_data));
|
||||
mbedtls_platform_zeroize(dhash, sizeof(dhash));
|
||||
|
||||
low_flash_available();
|
||||
file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
|
||||
if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1)
|
||||
if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_INVALID);
|
||||
}
|
||||
resetPinUvAuthToken();
|
||||
beginUsingPinUvAuthToken(false);
|
||||
if (subcommand == 0x05)
|
||||
if (subcommand == 0x05) {
|
||||
permissions = CTAP_PERMISSION_MC | CTAP_PERMISSION_GA;
|
||||
paut.permissions = permissions;
|
||||
}
|
||||
paut.permissions = (uint8_t)permissions;
|
||||
if (rpId.present == true) {
|
||||
mbedtls_sha256((uint8_t *)rpId.data, rpId.len, paut.rp_id_hash, 0);
|
||||
mbedtls_sha256((uint8_t *) rpId.data, rpId.len, paut.rp_id_hash, 0);
|
||||
paut.has_rp_id = true;
|
||||
}
|
||||
else
|
||||
else {
|
||||
paut.has_rp_id = false;
|
||||
}
|
||||
uint8_t pinUvAuthToken_enc[32 + IV_SIZE];
|
||||
encrypt(pinUvAuthProtocol, sharedSecret, paut.data, 32, pinUvAuthToken_enc);
|
||||
encrypt((uint8_t)pinUvAuthProtocol, sharedSecret, paut.data, 32, pinUvAuthToken_enc);
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x02));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, pinUvAuthToken_enc, 32+poff));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, pinUvAuthToken_enc, 32 + poff));
|
||||
}
|
||||
else
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION);
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
resp_size = cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1);
|
||||
err:
|
||||
@@ -566,10 +696,11 @@ err:
|
||||
CBOR_FREE_BYTE_STRING(kay);
|
||||
CBOR_FREE_BYTE_STRING(rpId);
|
||||
if (error != CborNoError) {
|
||||
if (error == CborErrorImproperValue)
|
||||
if (error == CborErrorImproperValue) {
|
||||
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
res_APDU_size = resp_size;
|
||||
res_APDU_size = (uint16_t)resp_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -15,19 +15,19 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "ctap2_cbor.h"
|
||||
#include "fido.h"
|
||||
#include "ctap.h"
|
||||
#include "bsp/board.h"
|
||||
#include "hid/ctap_hid.h"
|
||||
#include "files.h"
|
||||
#include "apdu.h"
|
||||
#include "credential.h"
|
||||
#include "hsm.h"
|
||||
#include "pico_keys.h"
|
||||
#include "random.h"
|
||||
#include "mbedtls/ecdh.h"
|
||||
#include "mbedtls/chachapoly.h"
|
||||
#include "mbedtls/sha256.h"
|
||||
#include "file.h"
|
||||
|
||||
extern uint8_t keydev_dec[32];
|
||||
extern bool has_keydev_dec;
|
||||
@@ -36,32 +36,37 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
CborParser parser;
|
||||
CborValue map;
|
||||
CborError error = CborNoError;
|
||||
uint64_t subcommand = 0, pinUvAuthProtocol = 0, vendorCommandId = 0, newMinPinLength = 0;
|
||||
CborByteString pinUvAuthParam = {0}, vendorAutCt = {0};
|
||||
CborCharString minPinLengthRPIDs[32] = {0};
|
||||
uint64_t subcommand = 0, pinUvAuthProtocol = 0, vendorCommandId = 0, newMinPinLength = 0, vendorParam = 0;
|
||||
CborByteString pinUvAuthParam = { 0 }, vendorAutCt = { 0 };
|
||||
CborCharString minPinLengthRPIDs[32] = { 0 };
|
||||
size_t resp_size = 0, raw_subpara_len = 0, minPinLengthRPIDs_len = 0;
|
||||
CborEncoder encoder, mapEncoder;
|
||||
CborEncoder encoder;
|
||||
//CborEncoder mapEncoder;
|
||||
uint8_t *raw_subpara = NULL;
|
||||
const bool *forceChangePin = NULL;
|
||||
|
||||
CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map));
|
||||
uint64_t val_c = 1;
|
||||
CBOR_PARSE_MAP_START(map, 1) {
|
||||
CBOR_PARSE_MAP_START(map, 1)
|
||||
{
|
||||
uint64_t val_u = 0;
|
||||
CBOR_FIELD_GET_UINT(val_u, 1);
|
||||
if (val_c <= 1 && val_c != val_u)
|
||||
if (val_c <= 1 && val_c != val_u) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (val_u < val_c)
|
||||
}
|
||||
if (val_u < val_c) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
|
||||
}
|
||||
val_c = val_u + 1;
|
||||
if (val_u == 0x01) {
|
||||
CBOR_FIELD_GET_UINT(subcommand, 1);
|
||||
}
|
||||
else if (val_u == 0x02) {
|
||||
uint64_t subpara = 0;
|
||||
raw_subpara = (uint8_t *)cbor_value_get_next_byte(&_f1);
|
||||
CBOR_PARSE_MAP_START(_f1, 2) {
|
||||
if (subcommand == 0xff) {
|
||||
raw_subpara = (uint8_t *) cbor_value_get_next_byte(&_f1);
|
||||
CBOR_PARSE_MAP_START(_f1, 2)
|
||||
{
|
||||
if (subcommand == 0x7f) { // Config Aut
|
||||
CBOR_FIELD_GET_UINT(subpara, 2);
|
||||
if (subpara == 0x01) {
|
||||
CBOR_FIELD_GET_UINT(vendorCommandId, 2);
|
||||
@@ -70,17 +75,19 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
CBOR_FIELD_GET_BYTES(vendorAutCt, 2);
|
||||
}
|
||||
}
|
||||
else if (subcommand == 0x03) {
|
||||
else if (subcommand == 0x03) { // Extensions
|
||||
CBOR_FIELD_GET_UINT(subpara, 2);
|
||||
if (subpara == 0x01) {
|
||||
CBOR_FIELD_GET_UINT(newMinPinLength, 2);
|
||||
}
|
||||
else if (subpara == 0x02) {
|
||||
CBOR_PARSE_ARRAY_START(_f2, 3) {
|
||||
CBOR_PARSE_ARRAY_START(_f2, 3)
|
||||
{
|
||||
CBOR_FIELD_GET_TEXT(minPinLengthRPIDs[minPinLengthRPIDs_len], 3);
|
||||
minPinLengthRPIDs_len++;
|
||||
if (minPinLengthRPIDs_len >= 32)
|
||||
if (minPinLengthRPIDs_len >= 32) {
|
||||
CBOR_ERROR(CTAP2_ERR_KEY_STORE_FULL);
|
||||
}
|
||||
}
|
||||
CBOR_PARSE_ARRAY_END(_f2, 3);
|
||||
}
|
||||
@@ -88,6 +95,15 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
CBOR_FIELD_GET_BOOL(forceChangePin, 2);
|
||||
}
|
||||
}
|
||||
else if (subcommand == 0x1B) { // PHY
|
||||
CBOR_FIELD_GET_UINT(subpara, 2);
|
||||
if (subpara == 0x01) {
|
||||
CBOR_FIELD_GET_UINT(vendorCommandId, 2);
|
||||
}
|
||||
else if (subpara == 0x02) {
|
||||
CBOR_FIELD_GET_UINT(vendorParam, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f1, 2);
|
||||
raw_subpara_len = cbor_value_get_next_byte(&_f1) - raw_subpara;
|
||||
@@ -101,42 +117,50 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
}
|
||||
CBOR_PARSE_MAP_END(map, 1);
|
||||
|
||||
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
|
||||
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_CBOR_PAYLOAD, 0);
|
||||
|
||||
if (pinUvAuthParam.present == false)
|
||||
if (pinUvAuthParam.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_PUAT_REQUIRED);
|
||||
if (pinUvAuthProtocol == 0)
|
||||
}
|
||||
if (pinUvAuthProtocol == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
|
||||
uint8_t *verify_payload = (uint8_t *)calloc(1, 32 + 1 + 1 + raw_subpara_len);
|
||||
uint8_t *verify_payload = (uint8_t *) calloc(1, 32 + 1 + 1 + raw_subpara_len);
|
||||
memset(verify_payload, 0xff, 32);
|
||||
verify_payload[32] = 0x0d;
|
||||
verify_payload[33] = subcommand;
|
||||
verify_payload[33] = (uint8_t)subcommand;
|
||||
memcpy(verify_payload + 34, raw_subpara, raw_subpara_len);
|
||||
error = verify(pinUvAuthProtocol, paut.data, verify_payload, 32 + 1 + 1 + raw_subpara_len, pinUvAuthParam.data);
|
||||
error = verify((uint8_t)pinUvAuthProtocol, paut.data, verify_payload, (uint16_t)(32 + 1 + 1 + raw_subpara_len), pinUvAuthParam.data);
|
||||
free(verify_payload);
|
||||
if (error != CborNoError)
|
||||
if (error != CborNoError) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
|
||||
if (!(paut.permissions & CTAP_PERMISSION_ACFG))
|
||||
if (!(paut.permissions & CTAP_PERMISSION_ACFG)) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
|
||||
if (subcommand == 0xff) {
|
||||
if (subcommand == 0x7f) {
|
||||
if (vendorCommandId == CTAP_CONFIG_AUT_DISABLE) {
|
||||
if (!file_has_data(ef_keydev_enc))
|
||||
if (!file_has_data(ef_keydev_enc)) {
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
if (has_keydev_dec == false)
|
||||
}
|
||||
if (has_keydev_dec == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
flash_write_data_to_file(ef_keydev, keydev_dec, sizeof(keydev_dec));
|
||||
}
|
||||
file_put_data(ef_keydev, keydev_dec, sizeof(keydev_dec));
|
||||
mbedtls_platform_zeroize(keydev_dec, sizeof(keydev_dec));
|
||||
flash_write_data_to_file(ef_keydev_enc, NULL, 0); // Set ef to 0 bytes
|
||||
file_put_data(ef_keydev_enc, NULL, 0); // Set ef to 0 bytes
|
||||
low_flash_available();
|
||||
}
|
||||
else if (vendorCommandId == CTAP_CONFIG_AUT_ENABLE) {
|
||||
if (!file_has_data(ef_keydev))
|
||||
if (!file_has_data(ef_keydev)) {
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
if (mse.init == false)
|
||||
}
|
||||
if (mse.init == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
}
|
||||
|
||||
mbedtls_chachapoly_context chatx;
|
||||
int ret = mse_decrypt_ct(vendorAutCt.data, vendorAutCt.len);
|
||||
@@ -144,20 +168,20 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
uint8_t key_dev_enc[12+32+16];
|
||||
uint8_t key_dev_enc[12 + 32 + 16];
|
||||
random_gen(NULL, key_dev_enc, 12);
|
||||
mbedtls_chachapoly_init(&chatx);
|
||||
mbedtls_chachapoly_setkey(&chatx, vendorAutCt.data);
|
||||
ret = mbedtls_chachapoly_encrypt_and_tag(&chatx, file_get_size(ef_keydev), key_dev_enc, NULL, 0, file_get_data(ef_keydev), key_dev_enc + 12, key_dev_enc + 12 + file_get_size(ef_keydev));
|
||||
mbedtls_chachapoly_free(&chatx);
|
||||
if (ret != 0){
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
flash_write_data_to_file(ef_keydev_enc, key_dev_enc, sizeof(key_dev_enc));
|
||||
file_put_data(ef_keydev_enc, key_dev_enc, sizeof(key_dev_enc));
|
||||
mbedtls_platform_zeroize(key_dev_enc, sizeof(key_dev_enc));
|
||||
flash_write_data_to_file(ef_keydev, key_dev_enc, file_get_size(ef_keydev)); // Overwrite ef with 0
|
||||
flash_write_data_to_file(ef_keydev, NULL, 0); // Set ef to 0 bytes
|
||||
file_put_data(ef_keydev, key_dev_enc, file_get_size(ef_keydev)); // Overwrite ef with 0
|
||||
file_put_data(ef_keydev, NULL, 0); // Set ef to 0 bytes
|
||||
low_flash_available();
|
||||
}
|
||||
else {
|
||||
@@ -168,47 +192,84 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
else if (subcommand == 0x03) {
|
||||
uint8_t currentMinPinLen = 4;
|
||||
file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
|
||||
if (file_has_data(ef_minpin))
|
||||
if (file_has_data(ef_minpin)) {
|
||||
currentMinPinLen = *file_get_data(ef_minpin);
|
||||
if (newMinPinLength == 0)
|
||||
newMinPinLength = currentMinPinLen;
|
||||
else if (newMinPinLength > 0 && newMinPinLength < currentMinPinLen)
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION);
|
||||
if (forceChangePin == ptrue && !file_has_data(ef_pin))
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_NOT_SET);
|
||||
if (file_has_data(ef_pin) && file_get_data(ef_pin)[1] < newMinPinLength)
|
||||
forceChangePin = ptrue;
|
||||
uint8_t *data = (uint8_t *)calloc(1, 2 + minPinLengthRPIDs_len * 32);
|
||||
data[0] = newMinPinLength;
|
||||
data[1] = forceChangePin == ptrue ? 1 : 0;
|
||||
for (int m = 0; m < minPinLengthRPIDs_len; m++) {
|
||||
mbedtls_sha256((uint8_t *)minPinLengthRPIDs[m].data, minPinLengthRPIDs[m].len, data + 2 + m*32, 0);
|
||||
}
|
||||
flash_write_data_to_file(ef_minpin, data, 2 + minPinLengthRPIDs_len * 32);
|
||||
if (newMinPinLength == 0) {
|
||||
newMinPinLength = currentMinPinLen;
|
||||
}
|
||||
else if (newMinPinLength > 0 && newMinPinLength < currentMinPinLen) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION);
|
||||
}
|
||||
if (forceChangePin == ptrue && !file_has_data(ef_pin)) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_NOT_SET);
|
||||
}
|
||||
if (file_has_data(ef_pin) && file_get_data(ef_pin)[1] < newMinPinLength) {
|
||||
forceChangePin = ptrue;
|
||||
}
|
||||
uint8_t *dataf = (uint8_t *) calloc(1, 2 + minPinLengthRPIDs_len * 32);
|
||||
dataf[0] = (uint8_t)newMinPinLength;
|
||||
dataf[1] = forceChangePin == ptrue ? 1 : 0;
|
||||
for (size_t m = 0; m < minPinLengthRPIDs_len; m++) {
|
||||
mbedtls_sha256((uint8_t *) minPinLengthRPIDs[m].data, minPinLengthRPIDs[m].len, dataf + 2 + m * 32, 0);
|
||||
}
|
||||
file_put_data(ef_minpin, dataf, (uint16_t)(2 + minPinLengthRPIDs_len * 32));
|
||||
low_flash_available();
|
||||
free(dataf);
|
||||
goto err; //No return
|
||||
}
|
||||
else if (subcommand == 0x01) {
|
||||
set_opts(get_opts() | FIDO2_OPT_EA);
|
||||
goto err;
|
||||
}
|
||||
else
|
||||
#ifndef ENABLE_EMULATION
|
||||
else if (subcommand == 0x1B) {
|
||||
if (vendorParam == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
if (vendorCommandId == CTAP_CONFIG_PHY_VIDPID) {
|
||||
phy_data.vid = (vendorParam >> 16) & 0xFFFF;
|
||||
phy_data.pid = vendorParam & 0xFFFF;
|
||||
phy_data.vidpid_present = true;
|
||||
}
|
||||
else if (vendorCommandId == CTAP_CONFIG_PHY_LED_GPIO) {
|
||||
phy_data.led_gpio = (uint8_t)vendorParam;
|
||||
phy_data.led_gpio_present = true;
|
||||
}
|
||||
else if (vendorCommandId == CTAP_CONFIG_PHY_LED_BTNESS) {
|
||||
phy_data.led_brightness = (uint8_t)vendorParam;
|
||||
phy_data.led_brightness_present = true;
|
||||
}
|
||||
else if (vendorCommandId == CTAP_CONFIG_PHY_OPTS) {
|
||||
phy_data.opts = (uint16_t)vendorParam;
|
||||
}
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION);
|
||||
}
|
||||
if (phy_save() != PICOKEY_OK) {
|
||||
CBOR_ERROR(CTAP2_ERR_PROCESSING);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION);
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
resp_size = cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1);
|
||||
}
|
||||
//CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
//resp_size = cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1);
|
||||
|
||||
err:
|
||||
err:
|
||||
CBOR_FREE_BYTE_STRING(pinUvAuthParam);
|
||||
CBOR_FREE_BYTE_STRING(vendorAutCt);
|
||||
for (int i = 0; i < minPinLengthRPIDs_len; i++) {
|
||||
for (size_t i = 0; i < minPinLengthRPIDs_len; i++) {
|
||||
CBOR_FREE_BYTE_STRING(minPinLengthRPIDs[i]);
|
||||
}
|
||||
|
||||
if (error != CborNoError) {
|
||||
if (error == CborErrorImproperValue)
|
||||
if (error == CborErrorImproperValue) {
|
||||
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
res_APDU_size = resp_size;
|
||||
res_APDU_size = (uint16_t)resp_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -15,30 +15,29 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ctap2_cbor.h"
|
||||
#include "fido.h"
|
||||
#include "ctap.h"
|
||||
#include "bsp/board.h"
|
||||
#include "hid/ctap_hid.h"
|
||||
#include "cbor_make_credential.h"
|
||||
#include "files.h"
|
||||
#include "apdu.h"
|
||||
#include "credential.h"
|
||||
#include "hsm.h"
|
||||
#include "pico_keys.h"
|
||||
|
||||
uint8_t rp_counter = 1;
|
||||
uint8_t rp_total = 0;
|
||||
uint8_t cred_counter = 1;
|
||||
uint8_t cred_total = 0;
|
||||
CborByteString rpIdHashx = {0};
|
||||
CborByteString rpIdHashx = { 0 };
|
||||
|
||||
int cbor_cred_mgmt(const uint8_t *data, size_t len) {
|
||||
CborParser parser;
|
||||
CborValue map;
|
||||
CborError error = CborNoError;
|
||||
uint64_t subcommand = 0, pinUvAuthProtocol = 0;
|
||||
CborByteString pinUvAuthParam = {0}, rpIdHash = {0};
|
||||
PublicKeyCredentialDescriptor credentialId = {0};
|
||||
PublicKeyCredentialUserEntity user = {0};
|
||||
CborByteString pinUvAuthParam = { 0 }, rpIdHash = { 0 };
|
||||
PublicKeyCredentialDescriptor credentialId = { 0 };
|
||||
PublicKeyCredentialUserEntity user = { 0 };
|
||||
size_t resp_size = 0;
|
||||
CborEncoder encoder, mapEncoder, mapEncoder2;
|
||||
uint8_t *raw_subpara = NULL;
|
||||
@@ -47,34 +46,41 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
|
||||
|
||||
CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map));
|
||||
uint64_t val_c = 1;
|
||||
CBOR_PARSE_MAP_START(map, 1) {
|
||||
CBOR_PARSE_MAP_START(map, 1)
|
||||
{
|
||||
uint64_t val_u = 0;
|
||||
CBOR_FIELD_GET_UINT(val_u, 1);
|
||||
if (val_c <= 1 && val_c != val_u)
|
||||
if (val_c <= 1 && val_c != val_u) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (val_u < val_c)
|
||||
}
|
||||
if (val_u < val_c) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
|
||||
}
|
||||
val_c = val_u + 1;
|
||||
if (val_u == 0x01) {
|
||||
CBOR_FIELD_GET_UINT(subcommand, 1);
|
||||
}
|
||||
else if (val_u == 0x02) {
|
||||
uint64_t subpara = 0;
|
||||
raw_subpara = (uint8_t *)cbor_value_get_next_byte(&_f1);
|
||||
CBOR_PARSE_MAP_START(_f1, 2) {
|
||||
raw_subpara = (uint8_t *) cbor_value_get_next_byte(&_f1);
|
||||
CBOR_PARSE_MAP_START(_f1, 2)
|
||||
{
|
||||
CBOR_FIELD_GET_UINT(subpara, 2);
|
||||
if (subpara == 0x01) {
|
||||
CBOR_FIELD_GET_BYTES(rpIdHash, 2);
|
||||
}
|
||||
else if (subpara == 0x02) {
|
||||
|
||||
CBOR_PARSE_MAP_START(_f2, 3) {
|
||||
CBOR_PARSE_MAP_START(_f2, 3)
|
||||
{
|
||||
CBOR_FIELD_GET_KEY_TEXT(3);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BYTES(3, "id", credentialId.id);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_TEXT(3, "type", credentialId.type);
|
||||
if (strcmp(_fd3, "transports") == 0) {
|
||||
CBOR_PARSE_ARRAY_START(_f3, 4) {
|
||||
CBOR_FIELD_GET_TEXT(credentialId.transports[credentialId.transports_len], 4);
|
||||
CBOR_PARSE_ARRAY_START(_f3, 4)
|
||||
{
|
||||
CBOR_FIELD_GET_TEXT(credentialId.transports[credentialId.
|
||||
transports_len], 4);
|
||||
credentialId.transports_len++;
|
||||
}
|
||||
CBOR_PARSE_ARRAY_END(_f3, 4);
|
||||
@@ -83,7 +89,8 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
|
||||
CBOR_PARSE_MAP_END(_f2, 3);
|
||||
}
|
||||
else if (subpara == 0x03) {
|
||||
CBOR_PARSE_MAP_START(_f1, 3) {
|
||||
CBOR_PARSE_MAP_START(_f1, 3)
|
||||
{
|
||||
CBOR_FIELD_GET_KEY_TEXT(3);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BYTES(3, "id", user.id);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_TEXT(3, "name", user.parent.name);
|
||||
@@ -105,82 +112,101 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
|
||||
CBOR_PARSE_MAP_END(map, 1);
|
||||
|
||||
if (subcommand != 0x03 && subcommand != 0x05) {
|
||||
if (pinUvAuthParam.present == false)
|
||||
if (pinUvAuthParam.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_PUAT_REQUIRED);
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2)
|
||||
}
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
}
|
||||
|
||||
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
|
||||
if(subcommand == 0x01) {
|
||||
if (verify(pinUvAuthProtocol, paut.data, (const uint8_t *)"\x01", 1, pinUvAuthParam.data) != CborNoError)
|
||||
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_CBOR_PAYLOAD, 0);
|
||||
if (subcommand == 0x01) {
|
||||
if (verify((uint8_t)pinUvAuthProtocol, paut.data, (const uint8_t *) "\x01", 1, pinUvAuthParam.data) != CborNoError) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (is_preview == false && (!(paut.permissions & CTAP_PERMISSION_CM) || paut.has_rp_id == true))
|
||||
}
|
||||
if (is_preview == false &&
|
||||
(!(paut.permissions & CTAP_PERMISSION_CM) || paut.has_rp_id == true)) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
uint8_t existing = 0;
|
||||
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
|
||||
if (file_has_data(search_dynamic_file(EF_CRED + i)))
|
||||
if (file_has_data(search_dynamic_file((uint16_t)(EF_CRED + i)))) {
|
||||
existing++;
|
||||
}
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 2));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, existing));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x02));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, MAX_RESIDENT_CREDENTIALS-existing));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, MAX_RESIDENT_CREDENTIALS - existing));
|
||||
}
|
||||
else if (subcommand == 0x02 || subcommand == 0x03) {
|
||||
file_t *rp_ef = NULL;
|
||||
if (subcommand == 0x02) {
|
||||
if (verify(pinUvAuthProtocol, paut.data, (const uint8_t *)"\x02", 1, pinUvAuthParam.data) != CborNoError)
|
||||
if (verify((uint8_t)pinUvAuthProtocol, paut.data, (const uint8_t *) "\x02", 1, pinUvAuthParam.data) != CborNoError) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (is_preview == false && (!(paut.permissions & CTAP_PERMISSION_CM) || paut.has_rp_id == true))
|
||||
}
|
||||
if (is_preview == false && (!(paut.permissions & CTAP_PERMISSION_CM) || paut.has_rp_id == true)) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
rp_counter = 1;
|
||||
rp_total = 0;
|
||||
}
|
||||
else {
|
||||
if (rp_counter > rp_total)
|
||||
if (rp_counter > rp_total) {
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
}
|
||||
}
|
||||
uint8_t skip = 0;
|
||||
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
|
||||
file_t *tef = search_dynamic_file(EF_RP + i);
|
||||
file_t *tef = search_dynamic_file((uint16_t)(EF_RP + i));
|
||||
if (file_has_data(tef) && *file_get_data(tef) > 0) {
|
||||
if (++skip == rp_counter) {
|
||||
if (rp_ef == NULL)
|
||||
if (rp_ef == NULL) {
|
||||
rp_ef = tef;
|
||||
if (subcommand == 0x03)
|
||||
}
|
||||
if (subcommand == 0x03) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (subcommand == 0x02)
|
||||
if (subcommand == 0x02) {
|
||||
rp_total++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rp_ef == NULL)
|
||||
if (rp_ef == NULL) {
|
||||
CBOR_ERROR(CTAP2_ERR_NO_CREDENTIALS);
|
||||
}
|
||||
rp_counter++;
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, subcommand == 0x02 ? 3 : 2));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03));
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, 1));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "id"));
|
||||
CBOR_CHECK(cbor_encode_text_string(&mapEncoder2, (char *)file_get_data(rp_ef)+33, file_get_size(rp_ef)-33));
|
||||
CBOR_CHECK(cbor_encode_text_string(&mapEncoder2, (char *) file_get_data(rp_ef) + 33,
|
||||
file_get_size(rp_ef) - 33));
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, file_get_data(rp_ef)+1, 32));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, file_get_data(rp_ef) + 1, 32));
|
||||
if (subcommand == 0x02) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x05));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, rp_total));
|
||||
}
|
||||
}
|
||||
else if (subcommand == 0x04 || subcommand == 0x05) {
|
||||
if (subcommand == 0x04 && rpIdHash.present == false)
|
||||
if (subcommand == 0x04 && rpIdHash.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
if (subcommand == 0x04) {
|
||||
*(raw_subpara-1) = 0x04;
|
||||
if (verify(pinUvAuthProtocol, paut.data, raw_subpara-1, raw_subpara_len+1, pinUvAuthParam.data) != CborNoError)
|
||||
*(raw_subpara - 1) = 0x04;
|
||||
if (verify((uint8_t)pinUvAuthProtocol, paut.data, raw_subpara - 1, (uint16_t)(raw_subpara_len + 1), pinUvAuthParam.data) != CborNoError) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (is_preview == false && (!(paut.permissions & CTAP_PERMISSION_CM) || (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0)))
|
||||
}
|
||||
if (is_preview == false &&
|
||||
(!(paut.permissions & CTAP_PERMISSION_CM) ||
|
||||
(paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0))) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
cred_counter = 1;
|
||||
cred_total = 0;
|
||||
}
|
||||
@@ -193,54 +219,65 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
|
||||
file_t *cred_ef = NULL;
|
||||
uint8_t skip = 0;
|
||||
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
|
||||
file_t *tef = search_dynamic_file(EF_CRED + i);
|
||||
file_t *tef = search_dynamic_file((uint16_t)(EF_CRED + i));
|
||||
if (file_has_data(tef) && memcmp(file_get_data(tef), rpIdHash.data, 32) == 0) {
|
||||
if (++skip == cred_counter) {
|
||||
if (cred_ef == NULL)
|
||||
if (cred_ef == NULL) {
|
||||
cred_ef = tef;
|
||||
if (subcommand == 0x05)
|
||||
}
|
||||
if (subcommand == 0x05) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (subcommand == 0x04)
|
||||
if (subcommand == 0x04) {
|
||||
cred_total++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!file_has_data(cred_ef))
|
||||
if (!file_has_data(cred_ef)) {
|
||||
CBOR_ERROR(CTAP2_ERR_NO_CREDENTIALS);
|
||||
}
|
||||
|
||||
Credential cred = {0};
|
||||
if (credential_load(file_get_data(cred_ef)+32, file_get_size(cred_ef)-32, rpIdHash.data, &cred) != 0)
|
||||
Credential cred = { 0 };
|
||||
if (credential_load(file_get_data(cred_ef) + 32, file_get_size(cred_ef) - 32, rpIdHash.data, &cred) != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
}
|
||||
|
||||
mbedtls_ecdsa_context key;
|
||||
mbedtls_ecdsa_init(&key);
|
||||
if (fido_load_key(cred.curve, cred.id.data, &key) != 0) {
|
||||
mbedtls_ecp_keypair key;
|
||||
mbedtls_ecp_keypair_init(&key);
|
||||
if (fido_load_key((int)cred.curve, cred.id.data, &key) != 0) {
|
||||
credential_free(&cred);
|
||||
mbedtls_ecdsa_free(&key);
|
||||
mbedtls_ecp_keypair_free(&key);
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
}
|
||||
|
||||
cred_counter++;
|
||||
|
||||
uint8_t l = 4;
|
||||
if (subcommand == 0x04)
|
||||
if (subcommand == 0x04) {
|
||||
l++;
|
||||
}
|
||||
if (cred.extensions.present == true) {
|
||||
if (cred.extensions.credProtect > 0)
|
||||
l++;
|
||||
if (cred.extensions.largeBlobKey == ptrue)
|
||||
l++;
|
||||
if (cred.extensions.credProtect > 0) {
|
||||
l++;
|
||||
}
|
||||
if (cred.extensions.largeBlobKey == ptrue) {
|
||||
l++;
|
||||
}
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, l));
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x06));
|
||||
l = 0;
|
||||
if (cred.userId.present == true)
|
||||
if (cred.userId.present == true) {
|
||||
l++;
|
||||
if (cred.userName.present == true)
|
||||
}
|
||||
if (cred.userName.present == true) {
|
||||
l++;
|
||||
if (cred.userDisplayName.present == true)
|
||||
}
|
||||
if (cred.userDisplayName.present == true) {
|
||||
l++;
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, l));
|
||||
if (cred.userId.present == true) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "id"));
|
||||
@@ -265,21 +302,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x08));
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, 5));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 2));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 3));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, -cred.alg));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, cred.curve));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 2));
|
||||
uint8_t pkey[66];
|
||||
mbedtls_mpi_write_binary(&key.Q.X, pkey, mbedtls_mpi_size(&key.Q.X));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, pkey, mbedtls_mpi_size(&key.Q.X)));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 3));
|
||||
mbedtls_mpi_write_binary(&key.Q.Y, pkey, mbedtls_mpi_size(&key.Q.Y));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, pkey, mbedtls_mpi_size(&key.Q.Y)));
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
|
||||
CBOR_CHECK(COSE_key(&key, &mapEncoder, &mapEncoder2));
|
||||
|
||||
if (subcommand == 0x04) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x09));
|
||||
@@ -304,34 +327,48 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, largeBlobKey, sizeof(largeBlobKey)));
|
||||
mbedtls_platform_zeroize(largeBlobKey, sizeof(largeBlobKey));
|
||||
}
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0C));
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, cred.extensions.thirdPartyPayment == ptrue));
|
||||
}
|
||||
else {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0C));
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, false));
|
||||
}
|
||||
credential_free(&cred);
|
||||
mbedtls_ecdsa_free(&key);
|
||||
mbedtls_ecp_keypair_free(&key);
|
||||
}
|
||||
else if (subcommand == 0x06) {
|
||||
if (credentialId.id.present == false)
|
||||
if (credentialId.id.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
*(raw_subpara - 1) = 0x06;
|
||||
if (verify(pinUvAuthProtocol, paut.data, raw_subpara-1, raw_subpara_len+1, pinUvAuthParam.data) != CborNoError)
|
||||
if (verify((uint8_t)pinUvAuthProtocol, paut.data, raw_subpara - 1, (uint16_t)(raw_subpara_len + 1), pinUvAuthParam.data) != CborNoError) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (is_preview == false && (!(paut.permissions & CTAP_PERMISSION_CM) || (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0)))
|
||||
}
|
||||
if (is_preview == false &&
|
||||
(!(paut.permissions & CTAP_PERMISSION_CM) ||
|
||||
(paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0))) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
|
||||
file_t *ef = search_dynamic_file(EF_CRED + i);
|
||||
if (file_has_data(ef) && memcmp(file_get_data(ef)+32, credentialId.id.data, MIN(file_get_size(ef)-32, credentialId.id.len)) == 0) {
|
||||
file_t *ef = search_dynamic_file((uint16_t)(EF_CRED + i));
|
||||
if (file_has_data(ef) && memcmp(file_get_data(ef) + 32, credentialId.id.data, MIN(file_get_size(ef) - 32, credentialId.id.len)) == 0) {
|
||||
uint8_t *rp_id_hash = file_get_data(ef);
|
||||
if (delete_file(ef) != 0)
|
||||
if (delete_file(ef) != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
}
|
||||
for (int j = 0; j < MAX_RESIDENT_CREDENTIALS; j++) {
|
||||
file_t *rp_ef = search_dynamic_file(EF_RP + j);
|
||||
if (file_has_data(rp_ef) && memcmp(file_get_data(rp_ef)+1, rp_id_hash, 32) == 0) {
|
||||
uint8_t *rp_data = (uint8_t *)calloc(1, file_get_size(rp_ef));
|
||||
file_t *rp_ef = search_dynamic_file((uint16_t)(EF_RP + j));
|
||||
if (file_has_data(rp_ef) && memcmp(file_get_data(rp_ef) + 1, rp_id_hash, 32) == 0) {
|
||||
uint8_t *rp_data = (uint8_t *) calloc(1, file_get_size(rp_ef));
|
||||
memcpy(rp_data, file_get_data(rp_ef), file_get_size(rp_ef));
|
||||
rp_data[0] -= 1;
|
||||
if (rp_data[0] == 0)
|
||||
if (rp_data[0] == 0) {
|
||||
delete_file(rp_ef);
|
||||
else
|
||||
flash_write_data_to_file(rp_ef, rp_data, file_get_size(rp_ef));
|
||||
}
|
||||
else {
|
||||
file_put_data(rp_ef, rp_data, file_get_size(rp_ef));
|
||||
}
|
||||
free(rp_data);
|
||||
break;
|
||||
}
|
||||
@@ -343,27 +380,36 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
|
||||
CBOR_ERROR(CTAP2_ERR_NO_CREDENTIALS);
|
||||
}
|
||||
else if (subcommand == 0x07) {
|
||||
if (credentialId.id.present == false || user.id.present == false)
|
||||
if (credentialId.id.present == false || user.id.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
*(raw_subpara - 1) = 0x07;
|
||||
if (verify(pinUvAuthProtocol, paut.data, raw_subpara-1, raw_subpara_len+1, pinUvAuthParam.data) != CborNoError)
|
||||
if (verify((uint8_t)pinUvAuthProtocol, paut.data, raw_subpara - 1, (uint16_t)(raw_subpara_len + 1), pinUvAuthParam.data) != CborNoError) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (is_preview == false && (!(paut.permissions & CTAP_PERMISSION_CM) || (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0)))
|
||||
}
|
||||
if (is_preview == false &&
|
||||
(!(paut.permissions & CTAP_PERMISSION_CM) ||
|
||||
(paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0))) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
|
||||
file_t *ef = search_dynamic_file(EF_CRED + i);
|
||||
if (file_has_data(ef) && memcmp(file_get_data(ef)+32, credentialId.id.data, MIN(file_get_size(ef)-32, credentialId.id.len)) == 0) {
|
||||
Credential cred = {0};
|
||||
file_t *ef = search_dynamic_file((uint16_t)(EF_CRED + i));
|
||||
if (file_has_data(ef) && memcmp(file_get_data(ef) + 32, credentialId.id.data, MIN(file_get_size(ef) - 32, credentialId.id.len)) == 0) {
|
||||
Credential cred = { 0 };
|
||||
uint8_t *rp_id_hash = file_get_data(ef);
|
||||
if (credential_load(rp_id_hash+32, file_get_size(ef)-32, rp_id_hash, &cred) != 0)
|
||||
if (credential_load(rp_id_hash + 32, file_get_size(ef) - 32, rp_id_hash, &cred) != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
}
|
||||
if (memcmp(user.id.data, cred.userId.data, MIN(user.id.len, cred.userId.len)) != 0) {
|
||||
credential_free(&cred);
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
uint8_t newcred[MAX_CRED_ID_LENGTH];
|
||||
size_t newcred_len = 0;
|
||||
if (credential_create(&cred.rpId, &cred.userId, &user.parent.name, &user.displayName, &cred.opts, &cred.extensions, cred.use_sign_count, cred.alg, cred.curve, newcred, &newcred_len) != 0) {
|
||||
if (credential_create(&cred.rpId, &cred.userId, &user.parent.name,
|
||||
&user.displayName, &cred.opts, &cred.extensions,
|
||||
cred.use_sign_count, (int)cred.alg,
|
||||
(int)cred.curve, newcred, &newcred_len) != 0) {
|
||||
credential_free(&cred);
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
}
|
||||
@@ -379,7 +425,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
resp_size = cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1);
|
||||
err:
|
||||
err:
|
||||
CBOR_FREE_BYTE_STRING(pinUvAuthParam);
|
||||
|
||||
if (asserted == false) {
|
||||
@@ -389,14 +435,16 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
|
||||
CBOR_FREE_BYTE_STRING(user.displayName);
|
||||
CBOR_FREE_BYTE_STRING(user.parent.name);
|
||||
CBOR_FREE_BYTE_STRING(credentialId.type);
|
||||
for (int n = 0; n < credentialId.transports_len; n++) {
|
||||
CBOR_FREE_BYTE_STRING(credentialId.transports[n]);
|
||||
}
|
||||
CBOR_FREE_BYTE_STRING(credentialId.id);
|
||||
for (size_t n = 0; n < credentialId.transports_len; n++) {
|
||||
CBOR_FREE_BYTE_STRING(credentialId.transports[n]);
|
||||
}
|
||||
if (error != CborNoError) {
|
||||
if (error == CborErrorImproperValue)
|
||||
if (error == CborErrorImproperValue) {
|
||||
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
res_APDU_size = resp_size;
|
||||
res_APDU_size = (uint16_t)resp_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -17,22 +17,24 @@
|
||||
|
||||
#include "cbor.h"
|
||||
#include "ctap.h"
|
||||
#include "ctap2_cbor.h"
|
||||
#if !defined(ENABLE_EMULATION) && !defined(ESP_PLATFORM)
|
||||
#include "bsp/board.h"
|
||||
#endif
|
||||
#include "hid/ctap_hid.h"
|
||||
#include "fido.h"
|
||||
#include "files.h"
|
||||
#include "random.h"
|
||||
#include "crypto_utils.h"
|
||||
#include "hsm.h"
|
||||
#include "pico_keys.h"
|
||||
#include "apdu.h"
|
||||
#include "cbor_make_credential.h"
|
||||
#include "credential.h"
|
||||
#include <math.h>
|
||||
#include "mbedtls/sha256.h"
|
||||
#include "random.h"
|
||||
|
||||
int cbor_get_assertion(const uint8_t *data, size_t len, bool next);
|
||||
|
||||
bool residentx = false;
|
||||
Credential credsx[MAX_CREDENTIAL_COUNT_IN_LIST] = {0};
|
||||
Credential credsx[MAX_CREDENTIAL_COUNT_IN_LIST] = { 0 };
|
||||
uint8_t credentialCounter = 1;
|
||||
uint8_t numberOfCredentialsx = 0;
|
||||
uint8_t flagsx = 0;
|
||||
@@ -41,18 +43,23 @@ uint8_t *datax = NULL;
|
||||
size_t lenx = 0;
|
||||
|
||||
int cbor_get_next_assertion(const uint8_t *data, size_t len) {
|
||||
(void) data;
|
||||
(void) len;
|
||||
CborError error = CborNoError;
|
||||
if (credentialCounter >= numberOfCredentialsx)
|
||||
if (credentialCounter >= numberOfCredentialsx) {
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
if (timerx+30*1000 < board_millis())
|
||||
}
|
||||
if (timerx + 30 * 1000 < board_millis()) {
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
}
|
||||
CBOR_CHECK(cbor_get_assertion(datax, lenx, true));
|
||||
timerx = board_millis();
|
||||
credentialCounter++;
|
||||
err:
|
||||
if (error != CborNoError || credentialCounter == numberOfCredentialsx) {
|
||||
for (int i = 0; i < MAX_CREDENTIAL_COUNT_IN_LIST; i++)
|
||||
for (int i = 0; i < MAX_CREDENTIAL_COUNT_IN_LIST; i++) {
|
||||
credential_free(&credsx[i]);
|
||||
}
|
||||
if (datax) {
|
||||
free(datax);
|
||||
datax = NULL;
|
||||
@@ -63,8 +70,9 @@ err:
|
||||
flagsx = 0;
|
||||
credentialCounter = 0;
|
||||
numberOfCredentialsx = 0;
|
||||
if (error == CborErrorImproperValue)
|
||||
if (error == CborErrorImproperValue) {
|
||||
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
return 0;
|
||||
@@ -73,32 +81,35 @@ err:
|
||||
int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
size_t resp_size = 0;
|
||||
uint64_t pinUvAuthProtocol = 0, hmacSecretPinUvAuthProtocol = 1;
|
||||
CredOptions options = {0};
|
||||
CredExtensions extensions = {0};
|
||||
CredOptions options = { 0 };
|
||||
CredExtensions extensions = { 0 };
|
||||
CborParser parser;
|
||||
CborEncoder encoder, mapEncoder, mapEncoder2;
|
||||
CborValue map;
|
||||
CborError error = CborNoError;
|
||||
CborByteString pinUvAuthParam = {0}, clientDataHash = {0};
|
||||
CborCharString rpId = {0};
|
||||
PublicKeyCredentialDescriptor allowList[MAX_CREDENTIAL_COUNT_IN_LIST] = {0};
|
||||
Credential creds[MAX_CREDENTIAL_COUNT_IN_LIST] = {0};
|
||||
CborByteString pinUvAuthParam = { 0 }, clientDataHash = { 0 };
|
||||
CborCharString rpId = { 0 };
|
||||
PublicKeyCredentialDescriptor allowList[MAX_CREDENTIAL_COUNT_IN_LIST] = { 0 };
|
||||
Credential creds[MAX_CREDENTIAL_COUNT_IN_LIST] = { 0 };
|
||||
size_t allowList_len = 0, creds_len = 0;
|
||||
uint8_t *aut_data = NULL;
|
||||
bool asserted = false, up = true, uv = false;
|
||||
bool asserted = false, up = false, uv = false;
|
||||
int64_t kty = 2, alg = 0, crv = 0;
|
||||
CborByteString kax = {0}, kay = {0}, salt_enc = {0}, salt_auth = {0};
|
||||
CborByteString kax = { 0 }, kay = { 0 }, salt_enc = { 0 }, salt_auth = { 0 };
|
||||
const bool *credBlob = NULL;
|
||||
|
||||
CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map));
|
||||
uint64_t val_c = 1;
|
||||
CBOR_PARSE_MAP_START(map, 1) {
|
||||
CBOR_PARSE_MAP_START(map, 1)
|
||||
{
|
||||
uint64_t val_u = 0;
|
||||
CBOR_FIELD_GET_UINT(val_u, 1);
|
||||
if (val_c <= 2 && val_c != val_u)
|
||||
if (val_c <= 2 && val_c != val_u) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (val_u < val_c)
|
||||
}
|
||||
if (val_u < val_c) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
|
||||
}
|
||||
val_c = val_u + 1;
|
||||
if (val_u == 0x01) {
|
||||
CBOR_FIELD_GET_TEXT(rpId, 1);
|
||||
@@ -107,14 +118,17 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
CBOR_FIELD_GET_BYTES(clientDataHash, 1);
|
||||
}
|
||||
else if (val_u == 0x03) { // excludeList
|
||||
CBOR_PARSE_ARRAY_START(_f1, 2) {
|
||||
PublicKeyCredentialDescriptor *pc = &allowList[allowList_len];
|
||||
CBOR_PARSE_MAP_START(_f2, 3) {
|
||||
CBOR_PARSE_ARRAY_START(_f1, 2)
|
||||
{
|
||||
PublicKeyCredentialDescriptor *pc = &allowList[allowList_len];
|
||||
CBOR_PARSE_MAP_START(_f2, 3)
|
||||
{
|
||||
CBOR_FIELD_GET_KEY_TEXT(3);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BYTES(3, "id", pc->id);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_TEXT(3, "type", pc->type);
|
||||
if (strcmp(_fd3, "transports") == 0) {
|
||||
CBOR_PARSE_ARRAY_START(_f3, 4) {
|
||||
CBOR_PARSE_ARRAY_START(_f3, 4)
|
||||
{
|
||||
CBOR_FIELD_GET_TEXT(pc->transports[pc->transports_len], 4);
|
||||
pc->transports_len++;
|
||||
}
|
||||
@@ -128,36 +142,17 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
}
|
||||
else if (val_u == 0x04) { // extensions
|
||||
extensions.present = true;
|
||||
CBOR_PARSE_MAP_START(_f1, 2) {
|
||||
CBOR_PARSE_MAP_START(_f1, 2)
|
||||
{
|
||||
CBOR_FIELD_GET_KEY_TEXT(2);
|
||||
if (strcmp(_fd2, "hmac-secret") == 0) {
|
||||
extensions.hmac_secret = ptrue;
|
||||
uint64_t ukey = 0;
|
||||
CBOR_PARSE_MAP_START(_f2, 3) {
|
||||
CBOR_PARSE_MAP_START(_f2, 3)
|
||||
{
|
||||
CBOR_FIELD_GET_UINT(ukey, 3);
|
||||
if (ukey == 0x01) {
|
||||
int64_t kkey = 0;
|
||||
CBOR_PARSE_MAP_START(_f3, 4) {
|
||||
CBOR_FIELD_GET_INT(kkey, 4);
|
||||
if (kkey == 1) {
|
||||
CBOR_FIELD_GET_INT(kty, 4);
|
||||
}
|
||||
else if (kkey == 3) {
|
||||
CBOR_FIELD_GET_INT(alg, 4);
|
||||
}
|
||||
else if (kkey == -1) {
|
||||
CBOR_FIELD_GET_INT(crv, 4);
|
||||
}
|
||||
else if (kkey == -2) {
|
||||
CBOR_FIELD_GET_BYTES(kax, 4);
|
||||
}
|
||||
else if (kkey == -3) {
|
||||
CBOR_FIELD_GET_BYTES(kay, 4);
|
||||
}
|
||||
else
|
||||
CBOR_ADVANCE(4);
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f3, 4);
|
||||
CBOR_CHECK(COSE_read_key(&_f3, &kty, &alg, &crv, &kax, &kay));
|
||||
}
|
||||
else if (ukey == 0x02) {
|
||||
CBOR_FIELD_GET_BYTES(salt_enc, 3);
|
||||
@@ -168,21 +163,24 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
else if (ukey == 0x04) {
|
||||
CBOR_FIELD_GET_UINT(hmacSecretPinUvAuthProtocol, 3);
|
||||
}
|
||||
else
|
||||
else {
|
||||
CBOR_ADVANCE(3);
|
||||
}
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f2, 3);
|
||||
continue;
|
||||
}
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "credBlob", credBlob);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "largeBlobKey", extensions.largeBlobKey);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "thirdPartyPayment", extensions.thirdPartyPayment);
|
||||
CBOR_ADVANCE(2);
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f1, 2);
|
||||
}
|
||||
else if (val_u == 0x05) { // options
|
||||
options.present = true;
|
||||
CBOR_PARSE_MAP_START(_f1, 2) {
|
||||
CBOR_PARSE_MAP_START(_f1, 2)
|
||||
{
|
||||
CBOR_FIELD_GET_KEY_TEXT(2);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "rk", options.rk);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "up", options.up);
|
||||
@@ -200,12 +198,13 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
}
|
||||
CBOR_PARSE_MAP_END(map, 1);
|
||||
|
||||
if (rpId.present == false || clientDataHash.present == false)
|
||||
if (rpId.present == false || clientDataHash.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
|
||||
uint8_t flags = 0;
|
||||
uint8_t rp_id_hash[32];
|
||||
mbedtls_sha256((uint8_t *)rpId.data, rpId.len, rp_id_hash, 0);
|
||||
uint8_t rp_id_hash[32] = {0};
|
||||
mbedtls_sha256((uint8_t *) rpId.data, rpId.len, rp_id_hash, 0);
|
||||
|
||||
bool resident = false;
|
||||
uint8_t numberOfCredentials = 0;
|
||||
@@ -213,24 +212,35 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
if (next == false) {
|
||||
if (pinUvAuthParam.present == true) {
|
||||
if (pinUvAuthParam.len == 0 || pinUvAuthParam.data == NULL) {
|
||||
if (check_user_presence() == false)
|
||||
if (check_user_presence() == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_OPERATION_DENIED);
|
||||
if (!file_has_data(ef_pin))
|
||||
}
|
||||
if (!file_has_data(ef_pin)) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_NOT_SET);
|
||||
else
|
||||
}
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (pinUvAuthProtocol == 0)
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2)
|
||||
if (pinUvAuthProtocol == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (options.present) {
|
||||
if (options.uv == ptrue) { //4.3
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_OPTION);
|
||||
}
|
||||
if (options.uv == NULL || pinUvAuthParam.present == true) {
|
||||
uv = false;
|
||||
}
|
||||
else {
|
||||
uv = *options.uv;
|
||||
}
|
||||
//if (options.up != NULL) { //4.5
|
||||
// CBOR_ERROR(CTAP2_ERR_INVALID_OPTION);
|
||||
//}
|
||||
@@ -238,83 +248,151 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION);
|
||||
}
|
||||
//else if (options.up == NULL) //5.7
|
||||
//rup = ptrue;
|
||||
if (options.uv != NULL)
|
||||
uv = *options.uv;
|
||||
if (options.up != NULL)
|
||||
//rup = ptrue;
|
||||
if (options.up != NULL) {
|
||||
up = *options.up;
|
||||
}
|
||||
else {
|
||||
up = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (pinUvAuthParam.present == true) { //6.1
|
||||
int ret = verify(pinUvAuthProtocol, paut.data, clientDataHash.data, clientDataHash.len, pinUvAuthParam.data);
|
||||
if (ret != CborNoError)
|
||||
int ret = verify((uint8_t)pinUvAuthProtocol, paut.data, clientDataHash.data, (uint16_t)clientDataHash.len, pinUvAuthParam.data);
|
||||
if (ret != CborNoError) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (getUserVerifiedFlagValue() == false)
|
||||
}
|
||||
if (getUserVerifiedFlagValue() == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (!(paut.permissions & CTAP_PERMISSION_GA))
|
||||
}
|
||||
if (!(paut.permissions & CTAP_PERMISSION_GA)) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rp_id_hash, 32) != 0)
|
||||
}
|
||||
if (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rp_id_hash, 32) != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
flags |= FIDO2_AUT_FLAG_UV;
|
||||
// Check pinUvAuthToken permissions. See 6.2.2.4
|
||||
}
|
||||
if (extensions.present == true && extensions.hmac_secret == ptrue) {
|
||||
if (kax.present == false || kay.present == false || crv == 0 || alg == 0 || salt_enc.present == false || salt_auth.present == false)
|
||||
if (kax.present == false || kay.present == false || crv == 0 || alg == 0 ||
|
||||
salt_enc.present == false || salt_auth.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (salt_enc.len != 32+(hmacSecretPinUvAuthProtocol-1)*IV_SIZE && salt_enc.len != 64+(hmacSecretPinUvAuthProtocol-1)*IV_SIZE)
|
||||
}
|
||||
if (salt_enc.len != 32 + (hmacSecretPinUvAuthProtocol - 1) * IV_SIZE &&
|
||||
salt_enc.len != 64 + (hmacSecretPinUvAuthProtocol - 1) * IV_SIZE) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
bool silent = (up == false && uv == false);
|
||||
|
||||
if (allowList_len > 0) {
|
||||
for (int e = 0; e < allowList_len; e++) {
|
||||
if (allowList[e].type.present == false || allowList[e].id.present == false)
|
||||
for (size_t e = 0; e < allowList_len; e++) {
|
||||
if (allowList[e].type.present == false || allowList[e].id.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (strcmp(allowList[e].type.data, "public-key") != 0)
|
||||
}
|
||||
if (strcmp(allowList[e].type.data, "public-key") != 0) {
|
||||
continue;
|
||||
}
|
||||
if (credential_load(allowList[e].id.data, allowList[e].id.len, rp_id_hash, &creds[creds_len]) != 0) {
|
||||
CBOR_FREE_BYTE_STRING(allowList[e].id);
|
||||
credential_free(&creds[creds_len]);
|
||||
}
|
||||
else
|
||||
else {
|
||||
creds_len++;
|
||||
silent = false; // If we are able to load a credential, we are not silent
|
||||
// Even we provide allowList, we need to check if the credential is resident
|
||||
if (!resident) {
|
||||
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS && creds_len < MAX_CREDENTIAL_COUNT_IN_LIST; i++) {
|
||||
file_t *ef = search_dynamic_file((uint16_t)(EF_CRED + i));
|
||||
if (!file_has_data(ef) || memcmp(file_get_data(ef), rp_id_hash, 32) != 0) {
|
||||
continue;
|
||||
}
|
||||
if (memcmp(file_get_data(ef) + 32, allowList[e].id.data, allowList[e].id.len) == 0) {
|
||||
resident = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (resident) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS && creds_len < MAX_CREDENTIAL_COUNT_IN_LIST; i++) {
|
||||
file_t *ef = search_dynamic_file(EF_CRED + i);
|
||||
if (!file_has_data(ef) || memcmp(file_get_data(ef), rp_id_hash, 32) != 0)
|
||||
file_t *ef = search_dynamic_file((uint16_t)(EF_CRED + i));
|
||||
if (!file_has_data(ef) || memcmp(file_get_data(ef), rp_id_hash, 32) != 0) {
|
||||
continue;
|
||||
int ret = credential_load(file_get_data(ef) + 32, file_get_size(ef) - 32, rp_id_hash, &creds[creds_len]);
|
||||
if (ret != 0)
|
||||
}
|
||||
int ret = credential_load(file_get_data(ef) + 32, file_get_size(ef) - 32, rp_id_hash, &creds[creds_len]);
|
||||
if (ret != 0) {
|
||||
credential_free(&creds[creds_len]);
|
||||
else
|
||||
}
|
||||
else {
|
||||
creds_len++;
|
||||
silent = false; // If we are able to load a credential, we are not silent
|
||||
}
|
||||
}
|
||||
resident = true;
|
||||
}
|
||||
for (int i = 0; i < creds_len; i++) {
|
||||
for (size_t i = 0; i < creds_len; i++) {
|
||||
if (creds[i].present == true) {
|
||||
if (creds[i].extensions.present == true) {
|
||||
if (creds[i].extensions.credProtect == CRED_PROT_UV_REQUIRED && !(flags & FIDO2_AUT_FLAG_UV))
|
||||
if (creds[i].extensions.credProtect == CRED_PROT_UV_REQUIRED && !(flags & FIDO2_AUT_FLAG_UV)) {
|
||||
credential_free(&creds[i]);
|
||||
else if (creds[i].extensions.credProtect == CRED_PROT_UV_OPTIONAL_WITH_LIST && resident == true && !(flags & FIDO2_AUT_FLAG_UV))
|
||||
}
|
||||
else if (creds[i].extensions.credProtect == CRED_PROT_UV_OPTIONAL_WITH_LIST &&
|
||||
resident == true && !(flags & FIDO2_AUT_FLAG_UV)) {
|
||||
credential_free(&creds[i]);
|
||||
else
|
||||
creds[numberOfCredentials++] = creds[i];
|
||||
}
|
||||
else {
|
||||
if (numberOfCredentials != i) {
|
||||
creds[numberOfCredentials++] = creds[i];
|
||||
}
|
||||
else {
|
||||
numberOfCredentials++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (numberOfCredentials != i) {
|
||||
creds[numberOfCredentials++] = creds[i];
|
||||
}
|
||||
else {
|
||||
numberOfCredentials++;
|
||||
}
|
||||
}
|
||||
else
|
||||
creds[numberOfCredentials++] = creds[i];
|
||||
}
|
||||
}
|
||||
if (numberOfCredentials == 0)
|
||||
CBOR_ERROR(CTAP2_ERR_NO_CREDENTIALS);
|
||||
if (numberOfCredentials == 0) {
|
||||
if (silent && allowList_len > 0) {
|
||||
for (size_t e = 0; e < allowList_len; e++) {
|
||||
if (allowList[e].type.present == false || allowList[e].id.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
if (strcmp(allowList[e].type.data, "public-key") != 0) {
|
||||
continue;
|
||||
}
|
||||
if (credential_verify(allowList[e].id.data, allowList[e].id.len, rp_id_hash, true) == 0) {
|
||||
numberOfCredentials++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (numberOfCredentials == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_NO_CREDENTIALS);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < numberOfCredentials; i++) {
|
||||
for (int j = i + 1; j < numberOfCredentials; j++) {
|
||||
if (creds[j].creation > creds[i].creation) {
|
||||
Credential tmp = creds[j];
|
||||
creds[j] = creds[i];
|
||||
creds[i] = tmp;
|
||||
if (!silent) {
|
||||
for (int i = 0; i < numberOfCredentials; i++) {
|
||||
for (int j = i + 1; j < numberOfCredentials; j++) {
|
||||
if (creds[j].creation > creds[i].creation) {
|
||||
Credential tmp = creds[j];
|
||||
creds[j] = creds[i];
|
||||
creds[i] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -322,14 +400,16 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
if (options.up == ptrue || options.present == false || options.up == NULL) { //9.1
|
||||
if (pinUvAuthParam.present == true) {
|
||||
if (getUserPresentFlagValue() == false) {
|
||||
if (check_user_presence() == false)
|
||||
if (check_user_presence() == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_OPERATION_DENIED);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!(flags & FIDO2_AUT_FLAG_UP)) {
|
||||
if (check_user_presence() == false)
|
||||
if (check_user_presence() == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_OPERATION_DENIED);
|
||||
}
|
||||
}
|
||||
}
|
||||
flags |= FIDO2_AUT_FLAG_UP;
|
||||
@@ -342,18 +422,19 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_OPTION);
|
||||
}
|
||||
|
||||
if (up == false && uv == false) {
|
||||
selcred = &creds[0];
|
||||
if (silent && !resident) {
|
||||
// Silent authentication, do nothing
|
||||
}
|
||||
else {
|
||||
selcred = &creds[0];
|
||||
if (numberOfCredentials > 1) {
|
||||
asserted = true;
|
||||
residentx = resident;
|
||||
for (int i = 0; i < MAX_CREDENTIAL_COUNT_IN_LIST; i++)
|
||||
for (int i = 0; i < MAX_CREDENTIAL_COUNT_IN_LIST; i++) {
|
||||
credsx[i] = creds[i];
|
||||
}
|
||||
numberOfCredentialsx = numberOfCredentials;
|
||||
datax = (uint8_t *)calloc(1, len);
|
||||
datax = (uint8_t *) calloc(1, len);
|
||||
memcpy(datax, data, len);
|
||||
lenx = len;
|
||||
flagsx = flags;
|
||||
@@ -368,134 +449,195 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
flags = flagsx;
|
||||
selcred = &credsx[credentialCounter];
|
||||
}
|
||||
mbedtls_ecdsa_context ekey;
|
||||
mbedtls_ecdsa_init(&ekey);
|
||||
int ret = fido_load_key(selcred->curve, selcred->id.data, &ekey);
|
||||
if (ret != 0) {
|
||||
if (derive_key(rp_id_hash, false, selcred->id.data, MBEDTLS_ECP_DP_SECP256R1, &ekey) != 0) {
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t largeBlobKey[32];
|
||||
if (extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
|
||||
ret = credential_derive_large_blob_key(selcred->id.data, selcred->id.len, largeBlobKey);
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PROCESSING);
|
||||
int ret = 0;
|
||||
uint8_t largeBlobKey[32] = {0};
|
||||
if (selcred) {
|
||||
if (extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
|
||||
ret = credential_derive_large_blob_key(selcred->id.data, selcred->id.len, largeBlobKey);
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PROCESSING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t ext_len = 0;
|
||||
uint8_t ext [512];
|
||||
if (extensions.present == true) {
|
||||
uint8_t ext[512] = {0};
|
||||
if (selcred && extensions.present == true) {
|
||||
cbor_encoder_init(&encoder, ext, sizeof(ext), 0);
|
||||
int l = 0;
|
||||
if (options.up == pfalse)
|
||||
if (options.up == pfalse) {
|
||||
extensions.hmac_secret = NULL;
|
||||
if (extensions.hmac_secret != NULL)
|
||||
}
|
||||
if (extensions.hmac_secret == ptrue) {
|
||||
l++;
|
||||
if (credBlob == ptrue)
|
||||
l++;
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, l));
|
||||
}
|
||||
if (credBlob == ptrue) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "credBlob"));
|
||||
if (selcred->extensions.credBlob.present == true)
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, selcred->extensions.credBlob.data, selcred->extensions.credBlob.len));
|
||||
else
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, NULL, 0));
|
||||
l++;
|
||||
}
|
||||
if (extensions.hmac_secret != NULL) {
|
||||
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "hmac-secret"));
|
||||
|
||||
uint8_t sharedSecret[64];
|
||||
mbedtls_ecp_point Qp;
|
||||
mbedtls_ecp_point_init(&Qp);
|
||||
mbedtls_mpi_lset(&Qp.Z, 1);
|
||||
if (mbedtls_mpi_read_binary(&Qp.X, kax.data, kax.len) != 0) {
|
||||
mbedtls_ecp_point_free(&Qp);
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
if (mbedtls_mpi_read_binary(&Qp.Y, kay.data, kay.len) != 0) {
|
||||
mbedtls_ecp_point_free(&Qp);
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
int ret = ecdh(hmacSecretPinUvAuthProtocol, &Qp, sharedSecret);
|
||||
mbedtls_ecp_point_free(&Qp);
|
||||
if (ret != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
if (verify(hmacSecretPinUvAuthProtocol, sharedSecret, salt_enc.data, salt_enc.len, salt_auth.data) != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP2_ERR_EXTENSION_FIRST);
|
||||
}
|
||||
uint8_t salt_dec[64], poff = (hmacSecretPinUvAuthProtocol-1)*IV_SIZE;
|
||||
ret = decrypt(hmacSecretPinUvAuthProtocol, sharedSecret, salt_enc.data, salt_enc.len, salt_dec);
|
||||
if (ret != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
uint8_t cred_random[64], *crd = NULL;
|
||||
ret = credential_derive_hmac_key(selcred->id.data, selcred->id.len, cred_random);
|
||||
if (ret != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
if (flags & FIDO2_AUT_FLAG_UV)
|
||||
crd = cred_random + 32;
|
||||
else
|
||||
crd = cred_random;
|
||||
uint8_t out1[64], hmac_res[80];
|
||||
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), crd, 32, salt_dec, 32, out1);
|
||||
if (salt_enc.len == 64+poff)
|
||||
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), crd, 32, salt_dec+32, 32, out1+32);
|
||||
encrypt(hmacSecretPinUvAuthProtocol, sharedSecret, out1, salt_enc.len-poff, hmac_res);
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, hmac_res, salt_enc.len));
|
||||
if (extensions.thirdPartyPayment == ptrue) {
|
||||
l++;
|
||||
}
|
||||
if (l > 0) {
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, l));
|
||||
if (credBlob == ptrue) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "credBlob"));
|
||||
if (selcred->extensions.credBlob.present == true) {
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, selcred->extensions.credBlob.data,
|
||||
selcred->extensions.credBlob.len));
|
||||
}
|
||||
else {
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, NULL, 0));
|
||||
}
|
||||
}
|
||||
if (extensions.hmac_secret == ptrue) {
|
||||
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
ext_len = cbor_encoder_get_buffer_size(&encoder, ext);
|
||||
flags |= FIDO2_AUT_FLAG_ED;
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "hmac-secret"));
|
||||
|
||||
uint8_t sharedSecret[64] = {0};
|
||||
mbedtls_ecp_point Qp;
|
||||
mbedtls_ecp_point_init(&Qp);
|
||||
mbedtls_mpi_lset(&Qp.Z, 1);
|
||||
if (mbedtls_mpi_read_binary(&Qp.X, kax.data, kax.len) != 0) {
|
||||
mbedtls_ecp_point_free(&Qp);
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
if (mbedtls_mpi_read_binary(&Qp.Y, kay.data, kay.len) != 0) {
|
||||
mbedtls_ecp_point_free(&Qp);
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
ret = ecdh((uint8_t)hmacSecretPinUvAuthProtocol, &Qp, sharedSecret);
|
||||
mbedtls_ecp_point_free(&Qp);
|
||||
if (ret != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
if (verify((uint8_t)hmacSecretPinUvAuthProtocol, sharedSecret, salt_enc.data, (uint16_t)salt_enc.len, salt_auth.data) != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP2_ERR_EXTENSION_FIRST);
|
||||
}
|
||||
uint8_t salt_dec[64] = {0}, poff = ((uint8_t)hmacSecretPinUvAuthProtocol - 1) * IV_SIZE;
|
||||
ret = decrypt((uint8_t)hmacSecretPinUvAuthProtocol, sharedSecret, salt_enc.data, (uint16_t)salt_enc.len, salt_dec);
|
||||
if (ret != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
uint8_t cred_random[64] = {0}, *crd = NULL;
|
||||
ret = credential_derive_hmac_key(selcred->id.data, selcred->id.len, cred_random);
|
||||
if (ret != 0) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
if (flags & FIDO2_AUT_FLAG_UV) {
|
||||
crd = cred_random + 32;
|
||||
}
|
||||
else {
|
||||
crd = cred_random;
|
||||
}
|
||||
uint8_t out1[64] = {0}, hmac_res[80] = {0};
|
||||
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), crd, 32, salt_dec, 32, out1);
|
||||
if ((uint8_t)salt_enc.len == 64 + poff) {
|
||||
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), crd, 32, salt_dec + 32, 32, out1 + 32);
|
||||
}
|
||||
encrypt((uint8_t)hmacSecretPinUvAuthProtocol, sharedSecret, out1, (uint16_t)(salt_enc.len - poff), hmac_res);
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, hmac_res, salt_enc.len));
|
||||
}
|
||||
if (extensions.thirdPartyPayment == ptrue) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "thirdPartyPayment"));
|
||||
if (selcred->extensions.thirdPartyPayment == ptrue) {
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, true));
|
||||
}
|
||||
else {
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, false));
|
||||
}
|
||||
}
|
||||
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
ext_len = cbor_encoder_get_buffer_size(&encoder, ext);
|
||||
flags |= FIDO2_AUT_FLAG_ED;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ctr = get_sign_counter();
|
||||
|
||||
size_t aut_data_len = 32 + 1 + 4 + ext_len;
|
||||
aut_data = (uint8_t *)calloc(1, aut_data_len + clientDataHash.len);
|
||||
aut_data = (uint8_t *) calloc(1, aut_data_len + clientDataHash.len);
|
||||
uint8_t *pa = aut_data;
|
||||
memcpy(pa, rp_id_hash, 32); pa += 32;
|
||||
*pa++ = flags;
|
||||
*pa++ = ctr >> 24;
|
||||
*pa++ = ctr >> 16;
|
||||
*pa++ = ctr >> 8;
|
||||
*pa++ = ctr & 0xff;
|
||||
pa += put_uint32_t_be(ctr, pa);
|
||||
memcpy(pa, ext, ext_len); pa += ext_len;
|
||||
if (pa-aut_data != aut_data_len)
|
||||
if ((size_t)(pa - aut_data) != aut_data_len) {
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
}
|
||||
|
||||
memcpy(pa, clientDataHash.data, clientDataHash.len);
|
||||
uint8_t hash[32], sig[MBEDTLS_ECDSA_MAX_LEN];
|
||||
ret = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), aut_data, aut_data_len+clientDataHash.len, hash);
|
||||
uint8_t hash[64] = {0}, sig[MBEDTLS_ECDSA_MAX_LEN] = {0};
|
||||
const mbedtls_md_info_t *md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
|
||||
mbedtls_ecp_keypair ekey;
|
||||
mbedtls_ecp_keypair_init(&ekey);
|
||||
size_t olen = 0;
|
||||
ret = mbedtls_ecdsa_write_signature(&ekey, MBEDTLS_MD_SHA256, hash, 32, sig, sizeof(sig), &olen, random_gen, NULL);
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
if (selcred) {
|
||||
ret = fido_load_key((int)selcred->curve, selcred->id.data, &ekey);
|
||||
if (ret != 0) {
|
||||
if (derive_key(rp_id_hash, false, selcred->id.data, MBEDTLS_ECP_DP_SECP256R1, &ekey) != 0) {
|
||||
mbedtls_ecp_keypair_free(&ekey);
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
}
|
||||
}
|
||||
if (ekey.grp.id == MBEDTLS_ECP_DP_SECP384R1) {
|
||||
md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
|
||||
}
|
||||
else if (ekey.grp.id == MBEDTLS_ECP_DP_SECP521R1) {
|
||||
md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
|
||||
}
|
||||
#ifdef MBEDTLS_EDDSA_C
|
||||
else if (ekey.grp.id == MBEDTLS_ECP_DP_ED25519) {
|
||||
md = NULL;
|
||||
}
|
||||
#endif
|
||||
if (md != NULL) {
|
||||
ret = mbedtls_md(md, aut_data, aut_data_len + clientDataHash.len, hash);
|
||||
ret = mbedtls_ecdsa_write_signature(&ekey, mbedtls_md_get_type(md), hash, mbedtls_md_get_size(md), sig, sizeof(sig), &olen, random_gen, NULL);
|
||||
}
|
||||
#ifdef MBEDTLS_EDDSA_C
|
||||
else {
|
||||
ret = mbedtls_eddsa_write_signature(&ekey, aut_data, aut_data_len + clientDataHash.len, sig, sizeof(sig), &olen, MBEDTLS_EDDSA_PURE, NULL, 0, random_gen, NULL);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
// Bogus signature
|
||||
olen = 64;
|
||||
memset(sig, 0x0B, olen);
|
||||
}
|
||||
mbedtls_ecp_keypair_free(&ekey);
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PROCESSING);
|
||||
}
|
||||
|
||||
uint8_t lfields = 3;
|
||||
if (selcred->opts.present == true && selcred->opts.rk == ptrue)
|
||||
if (selcred && selcred->opts.present == true && selcred->opts.rk == ptrue) {
|
||||
lfields++;
|
||||
if (numberOfCredentials > 1 && next == false)
|
||||
}
|
||||
if (numberOfCredentials > 1 && next == false && (!resident || allowList_len <= 1)) {
|
||||
lfields++;
|
||||
if (extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue)
|
||||
}
|
||||
if (selcred && extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
|
||||
lfields++;
|
||||
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
|
||||
}
|
||||
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_CBOR_PAYLOAD, 0);
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, lfields));
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, 2));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "id"));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, selcred->id.data, selcred->id.len));
|
||||
if (selcred) {
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, selcred->id.data, selcred->id.len));
|
||||
}
|
||||
else {
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, (uint8_t *)"\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01", 16));
|
||||
}
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "type"));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "public-key"));
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
|
||||
@@ -505,18 +647,21 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, sig, olen));
|
||||
|
||||
if (selcred->opts.present == true && selcred->opts.rk == ptrue) {
|
||||
if (selcred && selcred->opts.present == true && selcred->opts.rk == ptrue) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04));
|
||||
uint8_t lu = 1;
|
||||
if (numberOfCredentials > 1 && allowList_len == 0) {
|
||||
if (selcred->userName.present == true)
|
||||
if (selcred->userName.present == true) {
|
||||
lu++;
|
||||
if (selcred->userDisplayName.present == true)
|
||||
}
|
||||
if (selcred->userDisplayName.present == true) {
|
||||
lu++;
|
||||
}
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, lu));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "id"));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, selcred->userId.data, selcred->userId.len));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, selcred->userId.data,
|
||||
selcred->userId.len));
|
||||
if (numberOfCredentials > 1 && allowList_len == 0) {
|
||||
if (selcred->userName.present == true) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "name"));
|
||||
@@ -529,11 +674,11 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
|
||||
}
|
||||
if (numberOfCredentials > 1 && next == false) {
|
||||
if (numberOfCredentials > 1 && next == false && (!resident || allowList_len <= 1)) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x05));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, numberOfCredentials));
|
||||
}
|
||||
if (extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
|
||||
if (selcred && extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x07));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, largeBlobKey, sizeof(largeBlobKey)));
|
||||
}
|
||||
@@ -541,31 +686,38 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
resp_size = cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1);
|
||||
ctr++;
|
||||
flash_write_data_to_file(ef_counter, (uint8_t *)&ctr, sizeof(ctr));
|
||||
file_put_data(ef_counter, (uint8_t *) &ctr, sizeof(ctr));
|
||||
low_flash_available();
|
||||
err:
|
||||
err:
|
||||
CBOR_FREE_BYTE_STRING(clientDataHash);
|
||||
CBOR_FREE_BYTE_STRING(pinUvAuthParam);
|
||||
CBOR_FREE_BYTE_STRING(rpId);
|
||||
CBOR_FREE_BYTE_STRING(kax);
|
||||
CBOR_FREE_BYTE_STRING(kay);
|
||||
CBOR_FREE_BYTE_STRING(salt_enc);
|
||||
CBOR_FREE_BYTE_STRING(salt_auth);
|
||||
if (asserted == false) {
|
||||
for (int i = 0; i < MAX_CREDENTIAL_COUNT_IN_LIST; i++)
|
||||
for (int i = 0; i < MAX_CREDENTIAL_COUNT_IN_LIST; i++) {
|
||||
credential_free(&creds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int m = 0; m < allowList_len; m++) {
|
||||
for (size_t m = 0; m < MAX_CREDENTIAL_COUNT_IN_LIST; m++) {
|
||||
CBOR_FREE_BYTE_STRING(allowList[m].type);
|
||||
CBOR_FREE_BYTE_STRING(allowList[m].id);
|
||||
for (int n = 0; n < allowList[m].transports_len; n++) {
|
||||
for (size_t n = 0; n < 8; n++) {
|
||||
CBOR_FREE_BYTE_STRING(allowList[m].transports[n]);
|
||||
}
|
||||
}
|
||||
if (aut_data)
|
||||
if (aut_data) {
|
||||
free(aut_data);
|
||||
}
|
||||
if (error != CborNoError) {
|
||||
if (error == CborErrorImproperValue)
|
||||
if (error == CborErrorImproperValue) {
|
||||
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
res_APDU_size = resp_size;
|
||||
res_APDU_size = (uint16_t)resp_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
#include "ctap2_cbor.h"
|
||||
#include "hid/ctap_hid.h"
|
||||
#include "fido.h"
|
||||
#include "ctap.h"
|
||||
#include "files.h"
|
||||
@@ -25,7 +26,7 @@
|
||||
int cbor_get_info() {
|
||||
CborEncoder encoder, mapEncoder, arrayEncoder, mapEncoder2;
|
||||
CborError error = CborNoError;
|
||||
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
|
||||
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_CBOR_PAYLOAD, 0);
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 15));
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
|
||||
@@ -36,32 +37,42 @@ int cbor_get_info() {
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &arrayEncoder));
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x02));
|
||||
CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 5));
|
||||
CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 6));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "credBlob"));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "credProtect"));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "hmac-secret"));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "largeBlobKey"));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "minPinLength"));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "thirdPartyPayment"));
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &arrayEncoder));
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, aaguid, sizeof(aaguid)));
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04));
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &arrayEncoder, 8));
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &arrayEncoder, 9));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "ep"));
|
||||
CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, get_opts() & FIDO2_OPT_EA));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "rk"));
|
||||
CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, true));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "alwaysUv"));
|
||||
if (file_has_data(ef_pin) && (get_opts() & FIDO2_OPT_AUV || !getUserVerifiedFlagValue())) {
|
||||
CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, true));
|
||||
}
|
||||
else {
|
||||
CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, false));
|
||||
}
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "credMgmt"));
|
||||
CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, true));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "authnrCfg"));
|
||||
CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, true));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "clientPin"));
|
||||
if (file_has_data(ef_pin))
|
||||
if (file_has_data(ef_pin)) {
|
||||
CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, true));
|
||||
else
|
||||
}
|
||||
else {
|
||||
CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, false));
|
||||
}
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "largeBlobs"));
|
||||
CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, true));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "pinUvAuthToken"));
|
||||
@@ -86,25 +97,33 @@ int cbor_get_info() {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, MAX_CRED_ID_LENGTH)); // MAX_CRED_ID_MAX_LENGTH
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0A));
|
||||
CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 3));
|
||||
CBOR_CHECK(cbor_encoder_create_map(&arrayEncoder, &mapEncoder2, 2));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "alg"));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, -FIDO2_ALG_ES256));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "type"));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "public-key"));
|
||||
CBOR_CHECK(cbor_encoder_close_container(&arrayEncoder, &mapEncoder2));
|
||||
CBOR_CHECK(cbor_encoder_create_map(&arrayEncoder, &mapEncoder2, 2));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "alg"));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, -FIDO2_ALG_ES384));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "type"));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "public-key"));
|
||||
CBOR_CHECK(cbor_encoder_close_container(&arrayEncoder, &mapEncoder2));
|
||||
CBOR_CHECK(cbor_encoder_create_map(&arrayEncoder, &mapEncoder2, 2));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "alg"));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, -FIDO2_ALG_ES512));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "type"));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "public-key"));
|
||||
CBOR_CHECK(cbor_encoder_close_container(&arrayEncoder, &mapEncoder2));
|
||||
|
||||
uint8_t curves = 3;
|
||||
#ifdef MBEDTLS_EDDSA_C
|
||||
curves++;
|
||||
#endif
|
||||
#ifndef ENABLE_EMULATION
|
||||
if (phy_data.enabled_curves & PHY_CURVE_SECP256K1) {
|
||||
#endif
|
||||
curves++;
|
||||
#ifndef ENABLE_EMULATION
|
||||
}
|
||||
#endif
|
||||
CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, curves));
|
||||
CBOR_CHECK(COSE_public_key(FIDO2_ALG_ES256, &arrayEncoder, &mapEncoder2));
|
||||
#ifdef MBEDTLS_EDDSA_C
|
||||
CBOR_CHECK(COSE_public_key(FIDO2_ALG_EDDSA, &arrayEncoder, &mapEncoder2));
|
||||
#endif
|
||||
CBOR_CHECK(COSE_public_key(FIDO2_ALG_ES384, &arrayEncoder, &mapEncoder2));
|
||||
CBOR_CHECK(COSE_public_key(FIDO2_ALG_ES512, &arrayEncoder, &mapEncoder2));
|
||||
#ifndef ENABLE_EMULATION
|
||||
if (phy_data.enabled_curves & PHY_CURVE_SECP256K1) {
|
||||
#endif
|
||||
CBOR_CHECK(COSE_public_key(FIDO2_ALG_ES256K, &arrayEncoder, &mapEncoder2));
|
||||
#ifndef ENABLE_EMULATION
|
||||
}
|
||||
#endif
|
||||
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &arrayEncoder));
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0B));
|
||||
@@ -112,16 +131,20 @@ int cbor_get_info() {
|
||||
|
||||
file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0C));
|
||||
if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1)
|
||||
if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1) {
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, true));
|
||||
else
|
||||
}
|
||||
else {
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, false));
|
||||
}
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0D));
|
||||
if (file_has_data(ef_minpin))
|
||||
if (file_has_data(ef_minpin)) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, *file_get_data(ef_minpin))); // minPINLength
|
||||
else
|
||||
}
|
||||
else {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 4)); // minPINLength
|
||||
|
||||
}
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0E));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, PICO_FIDO_VERSION)); // firmwareVersion
|
||||
|
||||
@@ -135,9 +158,10 @@ int cbor_get_info() {
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &arrayEncoder));
|
||||
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
err:
|
||||
if (error != CborNoError)
|
||||
err:
|
||||
if (error != CborNoError) {
|
||||
return -CTAP2_ERR_INVALID_CBOR;
|
||||
res_APDU_size = cbor_encoder_get_buffer_size(&encoder, res_APDU + 1);
|
||||
}
|
||||
res_APDU_size = (uint16_t)cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
#include "ctap2_cbor.h"
|
||||
#include "fido.h"
|
||||
#include "ctap.h"
|
||||
#include "hid/ctap_hid.h"
|
||||
#include "files.h"
|
||||
#include "apdu.h"
|
||||
#include "version.h"
|
||||
#include "hsm.h"
|
||||
#include "pico_keys.h"
|
||||
#include "mbedtls/sha256.h"
|
||||
|
||||
static uint64_t expectedLength = 0, expectedNextOffset = 0;
|
||||
@@ -33,17 +33,20 @@ int cbor_large_blobs(const uint8_t *data, size_t len) {
|
||||
CborEncoder encoder, mapEncoder;
|
||||
CborError error = CborNoError;
|
||||
uint64_t get = 0, offset = UINT64_MAX, length = 0, pinUvAuthProtocol = 0;
|
||||
CborByteString set = {0}, pinUvAuthParam = {0};
|
||||
CborByteString set = { 0 }, pinUvAuthParam = { 0 };
|
||||
|
||||
CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map));
|
||||
uint64_t val_c = 1;
|
||||
CBOR_PARSE_MAP_START(map, 1) {
|
||||
CBOR_PARSE_MAP_START(map, 1)
|
||||
{
|
||||
uint64_t val_u = 0;
|
||||
CBOR_FIELD_GET_UINT(val_u, 1);
|
||||
if (val_c <= 0 && val_c != val_u)
|
||||
if (val_c <= 0 && val_c != val_u) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (val_u < val_c)
|
||||
}
|
||||
if (val_u < val_c) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
|
||||
}
|
||||
val_c = val_u + 1;
|
||||
if (val_u == 0x01) {
|
||||
CBOR_FIELD_GET_UINT(get, 1);
|
||||
@@ -66,31 +69,40 @@ int cbor_large_blobs(const uint8_t *data, size_t len) {
|
||||
}
|
||||
CBOR_PARSE_MAP_END(map, 1);
|
||||
|
||||
if (offset == UINT64_MAX)
|
||||
if (offset == UINT64_MAX) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
if (get == 0 && set.present == false)
|
||||
}
|
||||
if (get == 0 && set.present == false) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
if (get != 0 && set.present == true)
|
||||
}
|
||||
if (get != 0 && set.present == true) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
|
||||
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_CBOR_PAYLOAD, 0);
|
||||
if (get > 0) {
|
||||
if (length != 0)
|
||||
if (length != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
if (length > MAX_FRAGMENT_LENGTH)
|
||||
}
|
||||
if (length > MAX_FRAGMENT_LENGTH) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_LEN);
|
||||
if (offset > file_get_size(ef_largeblob))
|
||||
}
|
||||
if (offset > file_get_size(ef_largeblob)) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, file_get_data(ef_largeblob)+offset, MIN(get, file_get_size(ef_largeblob)-offset)));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, file_get_data(ef_largeblob) + offset,
|
||||
MIN(get, file_get_size(ef_largeblob) - offset)));
|
||||
}
|
||||
else {
|
||||
if (set.len > MAX_FRAGMENT_LENGTH)
|
||||
if (set.len > MAX_FRAGMENT_LENGTH) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_LEN);
|
||||
}
|
||||
if (offset == 0) {
|
||||
if (length == 0)
|
||||
if (length == 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
if (length > MAX_LARGE_BLOB_SIZE) {
|
||||
CBOR_ERROR(CTAP2_ERR_LARGE_BLOB_STORAGE_FULL);
|
||||
}
|
||||
@@ -101,50 +113,57 @@ int cbor_large_blobs(const uint8_t *data, size_t len) {
|
||||
expectedNextOffset = 0;
|
||||
}
|
||||
else {
|
||||
if (length != 0)
|
||||
if (length != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
}
|
||||
if (offset != expectedNextOffset)
|
||||
if (offset != expectedNextOffset) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_SEQ);
|
||||
if (pinUvAuthParam.present == false)
|
||||
}
|
||||
if (pinUvAuthParam.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_PUAT_REQUIRED);
|
||||
if (pinUvAuthProtocol == 0)
|
||||
}
|
||||
if (pinUvAuthProtocol == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
uint8_t verify_data[70] = {0};
|
||||
}
|
||||
uint8_t verify_data[70] = { 0 };
|
||||
memset(verify_data, 0xff, 32);
|
||||
verify_data[32] = 0x0C;
|
||||
verify_data[34] = offset & 0xff;
|
||||
verify_data[35] = offset >> 8;
|
||||
verify_data[36] = offset >> 16;
|
||||
verify_data[37] = offset >> 24;
|
||||
mbedtls_sha256(set.data, set.len, verify_data+38, 0);
|
||||
if (verify(pinUvAuthProtocol, paut.data, verify_data, sizeof(verify_data), pinUvAuthParam.data) != 0)
|
||||
put_uint32_t_le(offset, verify_data + 34);
|
||||
mbedtls_sha256(set.data, set.len, verify_data + 38, 0);
|
||||
if (verify((uint8_t)pinUvAuthProtocol, paut.data, verify_data, (uint16_t)sizeof(verify_data), pinUvAuthParam.data) != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (!(paut.permissions & CTAP_PERMISSION_LBW))
|
||||
}
|
||||
if (!(paut.permissions & CTAP_PERMISSION_LBW)) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (offset+set.len > expectedLength)
|
||||
}
|
||||
if (offset + set.len > expectedLength) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
if (offset == 0)
|
||||
}
|
||||
if (offset == 0) {
|
||||
memset(temp_lba, 0, sizeof(temp_lba));
|
||||
memcpy(temp_lba+expectedNextOffset, set.data, set.len);
|
||||
}
|
||||
memcpy(temp_lba + expectedNextOffset, set.data, set.len);
|
||||
expectedNextOffset += set.len;
|
||||
if (expectedNextOffset == expectedLength) {
|
||||
uint8_t sha[32];
|
||||
mbedtls_sha256(temp_lba, expectedLength-16, sha, 0);
|
||||
if (expectedLength > 17 && memcmp(sha, temp_lba+expectedLength-16, 16) != 0)
|
||||
mbedtls_sha256(temp_lba, expectedLength - 16, sha, 0);
|
||||
if (expectedLength > 17 && memcmp(sha, temp_lba + expectedLength - 16, 16) != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_INTEGRITY_FAILURE);
|
||||
flash_write_data_to_file(ef_largeblob, temp_lba, expectedLength);
|
||||
}
|
||||
file_put_data(ef_largeblob, temp_lba, (uint16_t)expectedLength);
|
||||
low_flash_available();
|
||||
}
|
||||
goto err;
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
|
||||
err:
|
||||
err:
|
||||
CBOR_FREE_BYTE_STRING(pinUvAuthParam);
|
||||
CBOR_FREE_BYTE_STRING(set);
|
||||
if (error != CborNoError)
|
||||
if (error != CborNoError) {
|
||||
return -CTAP2_ERR_INVALID_CBOR;
|
||||
res_APDU_size = cbor_encoder_get_buffer_size(&encoder, res_APDU + 1);
|
||||
}
|
||||
res_APDU_size = (uint16_t)cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -15,54 +15,58 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "ctap2_cbor.h"
|
||||
#include "cbor_make_credential.h"
|
||||
#include "ctap2_cbor.h"
|
||||
#include "hid/ctap_hid.h"
|
||||
#include "fido.h"
|
||||
#include "ctap.h"
|
||||
#include "files.h"
|
||||
#include "random.h"
|
||||
#include "hsm.h"
|
||||
#include <math.h>
|
||||
#include "apdu.h"
|
||||
#include "credential.h"
|
||||
#include "mbedtls/sha256.h"
|
||||
#include "random.h"
|
||||
#include "pico_keys.h"
|
||||
|
||||
int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
CborParser parser;
|
||||
CborValue map;
|
||||
CborError error = CborNoError;
|
||||
CborByteString clientDataHash = {0}, pinUvAuthParam = {0};
|
||||
PublicKeyCredentialRpEntity rp = {0};
|
||||
PublicKeyCredentialUserEntity user = {0};
|
||||
PublicKeyCredentialParameters pubKeyCredParams[MAX_CREDENTIAL_COUNT_IN_LIST] = {0};
|
||||
CborByteString clientDataHash = { 0 }, pinUvAuthParam = { 0 };
|
||||
PublicKeyCredentialRpEntity rp = { 0 };
|
||||
PublicKeyCredentialUserEntity user = { 0 };
|
||||
PublicKeyCredentialParameters pubKeyCredParams[MAX_CREDENTIAL_COUNT_IN_LIST] = { 0 };
|
||||
size_t pubKeyCredParams_len = 0;
|
||||
PublicKeyCredentialDescriptor excludeList[MAX_CREDENTIAL_COUNT_IN_LIST] = {0};
|
||||
PublicKeyCredentialDescriptor excludeList[MAX_CREDENTIAL_COUNT_IN_LIST] = { 0 };
|
||||
size_t excludeList_len = 0;
|
||||
CredOptions options = {0};
|
||||
CredOptions options = { 0 };
|
||||
uint64_t pinUvAuthProtocol = 0, enterpriseAttestation = 0;
|
||||
uint8_t *aut_data = NULL;
|
||||
size_t resp_size = 0;
|
||||
CredExtensions extensions = {0};
|
||||
CredExtensions extensions = { 0 };
|
||||
//options.present = true;
|
||||
//options.up = ptrue;
|
||||
//options.uv = pfalse;
|
||||
options.uv = pfalse;
|
||||
//options.rk = pfalse;
|
||||
|
||||
CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map));
|
||||
uint64_t val_c = 1;
|
||||
CBOR_PARSE_MAP_START(map, 1) {
|
||||
CBOR_PARSE_MAP_START(map, 1)
|
||||
{
|
||||
uint64_t val_u = 0;
|
||||
CBOR_FIELD_GET_UINT(val_u, 1);
|
||||
if (val_c <= 4 && val_c != val_u)
|
||||
if (val_c <= 4 && val_c != val_u) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (val_u < val_c)
|
||||
}
|
||||
if (val_u < val_c) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
|
||||
}
|
||||
val_c = val_u + 1;
|
||||
if (val_u == 0x01) { // clientDataHash
|
||||
CBOR_FIELD_GET_BYTES(clientDataHash, 1);
|
||||
}
|
||||
else if (val_u == 0x02) { // rp
|
||||
CBOR_PARSE_MAP_START(_f1, 2) {
|
||||
CBOR_PARSE_MAP_START(_f1, 2)
|
||||
{
|
||||
CBOR_FIELD_GET_KEY_TEXT(2);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_TEXT(2, "id", rp.id);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_TEXT(2, "name", rp.parent.name);
|
||||
@@ -70,7 +74,8 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
CBOR_PARSE_MAP_END(_f1, 2);
|
||||
}
|
||||
else if (val_u == 0x03) { // user
|
||||
CBOR_PARSE_MAP_START(_f1, 2) {
|
||||
CBOR_PARSE_MAP_START(_f1, 2)
|
||||
{
|
||||
CBOR_FIELD_GET_KEY_TEXT(2);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BYTES(2, "id", user.id);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_TEXT(2, "name", user.parent.name);
|
||||
@@ -80,9 +85,11 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
CBOR_PARSE_MAP_END(_f1, 2);
|
||||
}
|
||||
else if (val_u == 0x04) { // pubKeyCredParams
|
||||
CBOR_PARSE_ARRAY_START(_f1, 2) {
|
||||
CBOR_PARSE_ARRAY_START(_f1, 2)
|
||||
{
|
||||
PublicKeyCredentialParameters *pk = &pubKeyCredParams[pubKeyCredParams_len];
|
||||
CBOR_PARSE_MAP_START(_f2, 3) {
|
||||
CBOR_PARSE_MAP_START(_f2, 3)
|
||||
{
|
||||
CBOR_FIELD_GET_KEY_TEXT(3);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_TEXT(3, "type", pk->type);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_INT(3, "alg", pk->alg);
|
||||
@@ -93,14 +100,17 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
CBOR_PARSE_ARRAY_END(_f1, 2);
|
||||
}
|
||||
else if (val_u == 0x05) { // excludeList
|
||||
CBOR_PARSE_ARRAY_START(_f1, 2) {
|
||||
PublicKeyCredentialDescriptor *pc = &excludeList[excludeList_len];
|
||||
CBOR_PARSE_MAP_START(_f2, 3) {
|
||||
CBOR_PARSE_ARRAY_START(_f1, 2)
|
||||
{
|
||||
PublicKeyCredentialDescriptor *pc = &excludeList[excludeList_len];
|
||||
CBOR_PARSE_MAP_START(_f2, 3)
|
||||
{
|
||||
CBOR_FIELD_GET_KEY_TEXT(3);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BYTES(3, "id", pc->id);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_TEXT(3, "type", pc->type);
|
||||
if (strcmp(_fd3, "transports") == 0) {
|
||||
CBOR_PARSE_ARRAY_START(_f3, 4) {
|
||||
CBOR_PARSE_ARRAY_START(_f3, 4)
|
||||
{
|
||||
CBOR_FIELD_GET_TEXT(pc->transports[pc->transports_len], 4);
|
||||
pc->transports_len++;
|
||||
}
|
||||
@@ -114,20 +124,23 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
}
|
||||
else if (val_u == 0x06) { // extensions
|
||||
extensions.present = true;
|
||||
CBOR_PARSE_MAP_START(_f1, 2) {
|
||||
CBOR_PARSE_MAP_START(_f1, 2)
|
||||
{
|
||||
CBOR_FIELD_GET_KEY_TEXT(2);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "hmac-secret", extensions.hmac_secret);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_UINT(2, "credProtect", extensions.credProtect);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "minPinLength", extensions.minPinLength);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BYTES(2, "credBlob", extensions.credBlob);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "largeBlobKey", extensions.largeBlobKey);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "thirdPartyPayment", extensions.thirdPartyPayment);
|
||||
CBOR_ADVANCE(2);
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f1, 2);
|
||||
}
|
||||
else if (val_u == 0x07) { // options
|
||||
options.present = true;
|
||||
CBOR_PARSE_MAP_START(_f1, 2) {
|
||||
CBOR_PARSE_MAP_START(_f1, 2)
|
||||
{
|
||||
CBOR_FIELD_GET_KEY_TEXT(2);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "rk", options.rk);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "up", options.up);
|
||||
@@ -149,52 +162,91 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
CBOR_PARSE_MAP_END(map, 1);
|
||||
|
||||
uint8_t flags = FIDO2_AUT_FLAG_AT;
|
||||
uint8_t rp_id_hash[32];
|
||||
mbedtls_sha256((uint8_t *)rp.id.data, rp.id.len, rp_id_hash, 0);
|
||||
|
||||
int curve = -1, alg = 0;
|
||||
if (pubKeyCredParams_len == 0)
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
|
||||
for (int i = 0; i < pubKeyCredParams_len; i++) {
|
||||
if (strcmp(pubKeyCredParams[i].type.data, "public-key") != 0)
|
||||
continue;
|
||||
if (pubKeyCredParams[i].alg == FIDO2_ALG_ES256)
|
||||
curve = FIDO2_CURVE_P256;
|
||||
else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES384)
|
||||
curve = FIDO2_CURVE_P384;
|
||||
else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES512)
|
||||
curve = FIDO2_CURVE_P521;
|
||||
else if (pubKeyCredParams[i].alg == 0) // no present
|
||||
curve = -1;
|
||||
else
|
||||
curve = 0;
|
||||
if (curve > 0) {
|
||||
alg = pubKeyCredParams[i].alg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (curve == 0)
|
||||
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_ALGORITHM);
|
||||
else if (curve == -1)
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
uint8_t rp_id_hash[32] = {0};
|
||||
mbedtls_sha256((uint8_t *) rp.id.data, rp.id.len, rp_id_hash, 0);
|
||||
|
||||
if (pinUvAuthParam.present == true) {
|
||||
if (pinUvAuthParam.len == 0 || pinUvAuthParam.data == NULL) {
|
||||
if (check_user_presence() == false)
|
||||
if (check_user_presence() == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_OPERATION_DENIED);
|
||||
if (!file_has_data(ef_pin))
|
||||
}
|
||||
if (!file_has_data(ef_pin)) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_NOT_SET);
|
||||
else
|
||||
}
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (pinUvAuthProtocol == 0)
|
||||
if (pinUvAuthProtocol == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2)
|
||||
}
|
||||
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int curve = -1, alg = 0;
|
||||
if (pubKeyCredParams_len == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < pubKeyCredParams_len; i++) {
|
||||
if (pubKeyCredParams[i].type.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
|
||||
}
|
||||
if (pubKeyCredParams[i].alg == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
|
||||
}
|
||||
if (strcmp(pubKeyCredParams[i].type.data, "public-key") != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_CBOR_UNEXPECTED_TYPE);
|
||||
}
|
||||
if (pubKeyCredParams[i].alg == FIDO2_ALG_ES256) {
|
||||
if (curve <= 0) {
|
||||
curve = FIDO2_CURVE_P256;
|
||||
}
|
||||
}
|
||||
else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES384) {
|
||||
if (curve <= 0) {
|
||||
curve = FIDO2_CURVE_P384;
|
||||
}
|
||||
}
|
||||
else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES512) {
|
||||
if (curve <= 0) {
|
||||
curve = FIDO2_CURVE_P521;
|
||||
}
|
||||
}
|
||||
else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES256K
|
||||
#ifndef ENABLE_EMULATION
|
||||
&& (phy_data.enabled_curves & PHY_CURVE_SECP256K1)
|
||||
#endif
|
||||
) {
|
||||
if (curve <= 0) {
|
||||
curve = FIDO2_CURVE_P256K1;
|
||||
}
|
||||
}
|
||||
#ifdef MBEDTLS_EDDSA_C
|
||||
else if (pubKeyCredParams[i].alg == FIDO2_ALG_EDDSA) {
|
||||
if (curve <= 0) {
|
||||
curve = FIDO2_CURVE_ED25519;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if (pubKeyCredParams[i].alg <= FIDO2_ALG_RS256 && pubKeyCredParams[i].alg >= FIDO2_ALG_RS512) {
|
||||
// pass
|
||||
}
|
||||
//else {
|
||||
// CBOR_ERROR(CTAP2_ERR_CBOR_UNEXPECTED_TYPE);
|
||||
//}
|
||||
if (curve > 0 && alg == 0) {
|
||||
alg = (int)pubKeyCredParams[i].alg;
|
||||
}
|
||||
}
|
||||
if (curve <= 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_ALGORITHM);
|
||||
}
|
||||
|
||||
if (options.present) {
|
||||
if (options.uv == ptrue) { //5.3
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_OPTION);
|
||||
@@ -203,9 +255,9 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_OPTION);
|
||||
}
|
||||
//else if (options.up == NULL) //5.7
|
||||
//rup = ptrue;
|
||||
//rup = ptrue;
|
||||
}
|
||||
if (pinUvAuthParam.present == false && options.uv != ptrue && file_has_data(ef_pin)) { //8.1
|
||||
if (pinUvAuthParam.present == false && options.uv == pfalse && file_has_data(ef_pin)) { //8.1
|
||||
CBOR_ERROR(CTAP2_ERR_PUAT_REQUIRED);
|
||||
}
|
||||
if (enterpriseAttestation > 0) {
|
||||
@@ -218,15 +270,19 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
//Unfinished. See 6.1.2.9
|
||||
}
|
||||
if (pinUvAuthParam.present == true) { //11.1
|
||||
int ret = verify(pinUvAuthProtocol, paut.data, clientDataHash.data, clientDataHash.len, pinUvAuthParam.data);
|
||||
if (ret != CborNoError)
|
||||
int ret = verify((uint8_t)pinUvAuthProtocol, paut.data, clientDataHash.data, (uint16_t)clientDataHash.len, pinUvAuthParam.data);
|
||||
if (ret != CborNoError) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (!(paut.permissions & CTAP_PERMISSION_MC))
|
||||
}
|
||||
if (!(paut.permissions & CTAP_PERMISSION_MC)) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rp_id_hash, 32) != 0)
|
||||
}
|
||||
if (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rp_id_hash, 32) != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
if (getUserVerifiedFlagValue() == false)
|
||||
}
|
||||
if (getUserVerifiedFlagValue() == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
flags |= FIDO2_AUT_FLAG_UV;
|
||||
if (paut.has_rp_id == false) {
|
||||
memcpy(paut.rp_id_hash, rp_id_hash, 32);
|
||||
@@ -234,162 +290,212 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
}
|
||||
}
|
||||
|
||||
for (int e = 0; e < excludeList_len; e++) { //12.1
|
||||
if (excludeList[e].type.present == false || excludeList[e].id.present == false)
|
||||
for (size_t e = 0; e < excludeList_len; e++) { //12.1
|
||||
if (excludeList[e].type.present == false || excludeList[e].id.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (strcmp(excludeList[e].type.data, "public-key") != 0)
|
||||
}
|
||||
if (strcmp(excludeList[e].type.data, (char *)"public-key") != 0) {
|
||||
continue;
|
||||
Credential ecred;
|
||||
if (credential_load(excludeList[e].id.data, excludeList[e].id.len, rp_id_hash, &ecred) == 0 && (ecred.extensions.credProtect != CRED_PROT_UV_REQUIRED || (flags & FIDO2_AUT_FLAG_UV)))
|
||||
CBOR_ERROR(CTAP2_ERR_CREDENTIAL_EXCLUDED);
|
||||
}
|
||||
Credential ecred = {0};
|
||||
if (credential_load(excludeList[e].id.data, excludeList[e].id.len, rp_id_hash,
|
||||
&ecred) == 0 &&
|
||||
(ecred.extensions.credProtect != CRED_PROT_UV_REQUIRED ||
|
||||
(flags & FIDO2_AUT_FLAG_UV))) {
|
||||
credential_free(&ecred);
|
||||
CBOR_ERROR(CTAP2_ERR_CREDENTIAL_EXCLUDED);
|
||||
}
|
||||
credential_free(&ecred);
|
||||
}
|
||||
|
||||
if (extensions.largeBlobKey == pfalse || (extensions.largeBlobKey == ptrue && options.rk != ptrue)) {
|
||||
if (extensions.largeBlobKey == pfalse ||
|
||||
(extensions.largeBlobKey == ptrue && options.rk != ptrue)) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_OPTION);
|
||||
}
|
||||
|
||||
if (options.up == ptrue || options.up == NULL) { //14.1
|
||||
if (pinUvAuthParam.present == true) {
|
||||
if (getUserPresentFlagValue() == false) {
|
||||
if (check_user_presence() == false)
|
||||
if (check_user_presence() == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_OPERATION_DENIED);
|
||||
}
|
||||
}
|
||||
}
|
||||
flags |= FIDO2_AUT_FLAG_UP;
|
||||
clearUserPresentFlag();
|
||||
clearUserVerifiedFlag();
|
||||
clearPinUvAuthTokenPermissionsExceptLbw();
|
||||
if (options.up == ptrue) {
|
||||
clearUserPresentFlag();
|
||||
clearUserVerifiedFlag();
|
||||
clearPinUvAuthTokenPermissionsExceptLbw();
|
||||
}
|
||||
}
|
||||
|
||||
const known_app_t *ka = find_app_by_rp_id_hash(rp_id_hash);
|
||||
|
||||
uint8_t cred_id[MAX_CRED_ID_LENGTH];
|
||||
uint8_t cred_id[MAX_CRED_ID_LENGTH] = {0};
|
||||
size_t cred_id_len = 0;
|
||||
|
||||
CBOR_CHECK(credential_create(&rp.id, &user.id, &user.parent.name, &user.displayName, &options, &extensions, (!ka || ka->use_sign_count == ptrue), alg, curve, cred_id, &cred_id_len));
|
||||
CBOR_CHECK(credential_create(&rp.id, &user.id, &user.parent.name, &user.displayName, &options,
|
||||
&extensions, (!ka || ka->use_sign_count == ptrue), alg, curve,
|
||||
cred_id, &cred_id_len));
|
||||
|
||||
if (getUserVerifiedFlagValue())
|
||||
if (getUserVerifiedFlagValue()) {
|
||||
flags |= FIDO2_AUT_FLAG_UV;
|
||||
}
|
||||
size_t ext_len = 0;
|
||||
uint8_t ext [512];
|
||||
uint8_t ext[512] = {0};
|
||||
CborEncoder encoder, mapEncoder, mapEncoder2;
|
||||
if (extensions.present == true) {
|
||||
cbor_encoder_init(&encoder, ext, sizeof(ext), 0);
|
||||
int l = 0;
|
||||
uint8_t minPinLen = 0;
|
||||
if (extensions.hmac_secret != NULL)
|
||||
if (extensions.hmac_secret == ptrue) {
|
||||
l++;
|
||||
if (extensions.credProtect != 0)
|
||||
}
|
||||
if (extensions.credProtect != 0) {
|
||||
l++;
|
||||
if (extensions.minPinLength != NULL) {
|
||||
}
|
||||
if (extensions.minPinLength == ptrue) {
|
||||
file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
|
||||
if (file_has_data(ef_minpin)) {
|
||||
uint8_t *minpin_data = file_get_data(ef_minpin);
|
||||
for (int o = 2; o < file_get_size(ef_minpin); o += 32) {
|
||||
if (memcmp(minpin_data + o, rp_id_hash, 32) == 0) {
|
||||
minPinLen = minpin_data[0];
|
||||
if (minPinLen > 0)
|
||||
if (minPinLen > 0) {
|
||||
l++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (extensions.credBlob.present == true)
|
||||
l++;
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, l));
|
||||
if (extensions.credBlob.present == true) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "credBlob"));
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, extensions.credBlob.len < MAX_CREDBLOB_LENGTH));
|
||||
l++;
|
||||
}
|
||||
if (extensions.credProtect != 0) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "credProtect"));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, extensions.credProtect));
|
||||
}
|
||||
if (extensions.hmac_secret != NULL) {
|
||||
if (l > 0) {
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, l));
|
||||
if (extensions.credBlob.present == true) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "credBlob"));
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, extensions.credBlob.len < MAX_CREDBLOB_LENGTH));
|
||||
}
|
||||
if (extensions.credProtect != 0) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "credProtect"));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, extensions.credProtect));
|
||||
}
|
||||
if (extensions.hmac_secret == ptrue) {
|
||||
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "hmac-secret"));
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, *extensions.hmac_secret));
|
||||
}
|
||||
if (minPinLen > 0) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "hmac-secret"));
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, true));
|
||||
}
|
||||
if (minPinLen > 0) {
|
||||
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "minPinLength"));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, minPinLen));
|
||||
}
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "minPinLength"));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, minPinLen));
|
||||
}
|
||||
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
ext_len = cbor_encoder_get_buffer_size(&encoder, ext);
|
||||
flags |= FIDO2_AUT_FLAG_ED;
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
ext_len = cbor_encoder_get_buffer_size(&encoder, ext);
|
||||
flags |= FIDO2_AUT_FLAG_ED;
|
||||
}
|
||||
}
|
||||
uint8_t pkey[66];
|
||||
mbedtls_ecdsa_context ekey;
|
||||
mbedtls_ecdsa_init(&ekey);
|
||||
mbedtls_ecp_keypair ekey;
|
||||
mbedtls_ecp_keypair_init(&ekey);
|
||||
int ret = fido_load_key(curve, cred_id, &ekey);
|
||||
if (ret != 0) {
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
mbedtls_ecp_keypair_free(&ekey);
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
}
|
||||
const mbedtls_ecp_curve_info *cinfo = mbedtls_ecp_curve_info_from_grp_id(ekey.grp.id);
|
||||
if (cinfo == NULL) {
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
mbedtls_ecp_keypair_free(&ekey);
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
}
|
||||
size_t olen = 0;
|
||||
uint32_t ctr = get_sign_counter();
|
||||
uint8_t cbor_buf[1024];
|
||||
uint8_t cbor_buf[1024] = {0};
|
||||
cbor_encoder_init(&encoder, cbor_buf, sizeof(cbor_buf), 0);
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 5));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 2));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 3));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder, -alg));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, curve));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder, 2));
|
||||
mbedtls_mpi_write_binary(&ekey.Q.X, pkey, mbedtls_mpi_size(&ekey.Q.X));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, pkey, mbedtls_mpi_size(&ekey.Q.X)));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder, 3));
|
||||
mbedtls_mpi_write_binary(&ekey.Q.Y, pkey, mbedtls_mpi_size(&ekey.Q.Y));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, pkey, mbedtls_mpi_size(&ekey.Q.Y)));
|
||||
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
CBOR_CHECK(COSE_key(&ekey, &encoder, &mapEncoder));
|
||||
size_t rs = cbor_encoder_get_buffer_size(&encoder, cbor_buf);
|
||||
|
||||
size_t aut_data_len = 32 + 1 + 4 + (16 + 2 + cred_id_len + rs) + ext_len;
|
||||
aut_data = (uint8_t *)calloc(1, aut_data_len + clientDataHash.len);
|
||||
aut_data = (uint8_t *) calloc(1, aut_data_len + clientDataHash.len);
|
||||
uint8_t *pa = aut_data;
|
||||
memcpy(pa, rp_id_hash, 32); pa += 32;
|
||||
*pa++ = flags;
|
||||
*pa++ = ctr >> 24;
|
||||
*pa++ = ctr >> 16;
|
||||
*pa++ = ctr >> 8;
|
||||
*pa++ = ctr & 0xff;
|
||||
pa += put_uint32_t_be(ctr, pa);
|
||||
memcpy(pa, aaguid, 16); pa += 16;
|
||||
*pa++ = cred_id_len >> 8;
|
||||
*pa++ = cred_id_len & 0xff;
|
||||
memcpy(pa, cred_id, cred_id_len); pa += cred_id_len;
|
||||
memcpy(pa, cbor_buf, rs); pa += rs;
|
||||
memcpy(pa, ext, ext_len); pa += ext_len;
|
||||
if (pa-aut_data != aut_data_len) {
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
pa += put_uint16_t_be(cred_id_len, pa);
|
||||
memcpy(pa, cred_id, cred_id_len); pa += (uint16_t)cred_id_len;
|
||||
memcpy(pa, cbor_buf, rs); pa += (uint16_t)rs;
|
||||
memcpy(pa, ext, ext_len); pa += (uint16_t)ext_len;
|
||||
if ((size_t)(pa - aut_data) != aut_data_len) {
|
||||
mbedtls_ecp_keypair_free(&ekey);
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
}
|
||||
|
||||
memcpy(pa, clientDataHash.data, clientDataHash.len);
|
||||
uint8_t hash[32], sig[MBEDTLS_ECDSA_MAX_LEN];
|
||||
ret = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), aut_data, aut_data_len+clientDataHash.len, hash);
|
||||
uint8_t hash[64] = {0}, sig[MBEDTLS_ECDSA_MAX_LEN] = {0};
|
||||
const mbedtls_md_info_t *md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
|
||||
if (ekey.grp.id == MBEDTLS_ECP_DP_SECP384R1) {
|
||||
md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
|
||||
}
|
||||
else if (ekey.grp.id == MBEDTLS_ECP_DP_SECP521R1) {
|
||||
md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
|
||||
}
|
||||
#ifdef MBEDTLS_EDDSA_C
|
||||
else if (ekey.grp.id == MBEDTLS_ECP_DP_ED25519) {
|
||||
md = NULL;
|
||||
}
|
||||
#endif
|
||||
if (md != NULL) {
|
||||
ret = mbedtls_md(md, aut_data, aut_data_len + clientDataHash.len, hash);
|
||||
}
|
||||
|
||||
bool self_attestation = true;
|
||||
if (enterpriseAttestation == 2 || (ka && ka->use_self_attestation == pfalse)) {
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
mbedtls_ecdsa_init(&ekey);
|
||||
ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &ekey, file_get_data(ef_keydev), 32);
|
||||
mbedtls_ecp_keypair_free(&ekey);
|
||||
mbedtls_ecp_keypair_init(&ekey);
|
||||
uint8_t key[32] = {0};
|
||||
if (load_keydev(key) != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
}
|
||||
ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &ekey, key, 32);
|
||||
mbedtls_platform_zeroize(key, sizeof(key));
|
||||
md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
|
||||
self_attestation = false;
|
||||
}
|
||||
ret = mbedtls_ecdsa_write_signature(&ekey, MBEDTLS_MD_SHA256, hash, 32, sig, sizeof(sig), &olen, random_gen, NULL);
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
if (md != NULL) {
|
||||
ret = mbedtls_ecdsa_write_signature(&ekey, mbedtls_md_get_type(md), hash, mbedtls_md_get_size(md), sig, sizeof(sig), &olen, random_gen, NULL);
|
||||
}
|
||||
#ifdef MBEDTLS_EDDSA_C
|
||||
else {
|
||||
ret = mbedtls_eddsa_write_signature(&ekey, aut_data, aut_data_len + clientDataHash.len, sig, sizeof(sig), &olen, MBEDTLS_EDDSA_PURE, NULL, 0, random_gen, NULL);
|
||||
}
|
||||
#endif
|
||||
mbedtls_ecp_keypair_free(&ekey);
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PROCESSING);
|
||||
}
|
||||
|
||||
uint8_t largeBlobKey[32];
|
||||
if (user.id.len > 0 && user.parent.name.len > 0 && user.displayName.len > 0) {
|
||||
if (memcmp(user.parent.name.data, "+pico", 5) == 0) {
|
||||
options.rk = pfalse;
|
||||
#ifndef ENABLE_EMULATION
|
||||
uint8_t *p = (uint8_t *)user.parent.name.data + 5;
|
||||
if (memcmp(p, "CommissionProfile", 17) == 0) {
|
||||
ret = phy_unserialize_data(user.id.data, user.id.len, &phy_data);
|
||||
if (ret == PICOKEY_OK) {
|
||||
ret = phy_save();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (ret != PICOKEY_OK) {
|
||||
CBOR_ERROR(CTAP2_ERR_PROCESSING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t largeBlobKey[32] = {0};
|
||||
if (extensions.largeBlobKey == ptrue && options.rk == ptrue) {
|
||||
ret = credential_derive_large_blob_key(cred_id, cred_id_len, largeBlobKey);
|
||||
if (ret != 0) {
|
||||
@@ -397,8 +503,15 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
}
|
||||
}
|
||||
|
||||
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, extensions.largeBlobKey == ptrue && options.rk == ptrue ? 5 : 4));
|
||||
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_CBOR_PAYLOAD, 0);
|
||||
uint8_t lparams = 3;
|
||||
if (enterpriseAttestation == 2) {
|
||||
lparams++;
|
||||
}
|
||||
if (extensions.largeBlobKey == ptrue && options.rk == ptrue) {
|
||||
lparams++;
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, lparams));
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "packed"));
|
||||
@@ -406,18 +519,20 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, aut_data, aut_data_len));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03));
|
||||
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, self_attestation == false ? 3 : 2));
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, self_attestation == false || is_nitrokey ? 3 : 2));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "alg"));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, self_attestation ? -alg : -FIDO2_ALG_ES256));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, self_attestation || is_nitrokey ? -alg : -FIDO2_ALG_ES256));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "sig"));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, sig, olen));
|
||||
if (self_attestation == false) {
|
||||
if (self_attestation == false || is_nitrokey) {
|
||||
CborEncoder arrEncoder;
|
||||
file_t *ef_cert = NULL;
|
||||
if (enterpriseAttestation == 2)
|
||||
if (enterpriseAttestation == 2) {
|
||||
ef_cert = search_by_fid(EF_EE_DEV_EA, NULL, SPECIFY_EF);
|
||||
if (!file_has_data(ef_cert))
|
||||
}
|
||||
if (!file_has_data(ef_cert)) {
|
||||
ef_cert = ef_certdev;
|
||||
}
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "x5c"));
|
||||
CBOR_CHECK(cbor_encoder_create_array(&mapEncoder2, &arrEncoder, 1));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&arrEncoder, file_get_data(ef_cert), file_get_size(ef_cert)));
|
||||
@@ -425,8 +540,10 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
|
||||
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04));
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, enterpriseAttestation == 2));
|
||||
if (enterpriseAttestation == 2) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04));
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, true));
|
||||
}
|
||||
|
||||
if (extensions.largeBlobKey == ptrue && options.rk == ptrue) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x05));
|
||||
@@ -437,36 +554,44 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
resp_size = cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1);
|
||||
|
||||
if (options.rk == ptrue) {
|
||||
if (credential_store(cred_id, cred_id_len, rp_id_hash) != 0)
|
||||
if (credential_store(cred_id, cred_id_len, rp_id_hash) != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_KEY_STORE_FULL);
|
||||
}
|
||||
}
|
||||
err:
|
||||
CBOR_FREE_BYTE_STRING(clientDataHash);
|
||||
ctr++;
|
||||
file_put_data(ef_counter, (uint8_t *) &ctr, sizeof(ctr));
|
||||
low_flash_available();
|
||||
err:
|
||||
CBOR_FREE_BYTE_STRING(clientDataHash);
|
||||
CBOR_FREE_BYTE_STRING(pinUvAuthParam);
|
||||
CBOR_FREE_BYTE_STRING(rp.id);
|
||||
CBOR_FREE_BYTE_STRING(rp.parent.name);
|
||||
CBOR_FREE_BYTE_STRING(user.id);
|
||||
CBOR_FREE_BYTE_STRING(user.displayName);
|
||||
CBOR_FREE_BYTE_STRING(user.parent.name);
|
||||
for (int n = 0; n < pubKeyCredParams_len; n++) {
|
||||
if (extensions.present == true) {
|
||||
CBOR_FREE_BYTE_STRING(extensions.credBlob);
|
||||
}
|
||||
for (size_t n = 0; n < MAX_CREDENTIAL_COUNT_IN_LIST; n++) {
|
||||
CBOR_FREE_BYTE_STRING(pubKeyCredParams[n].type);
|
||||
}
|
||||
|
||||
for (int m = 0; m < excludeList_len; m++) {
|
||||
for (size_t m = 0; m < MAX_CREDENTIAL_COUNT_IN_LIST; m++) {
|
||||
CBOR_FREE_BYTE_STRING(excludeList[m].type);
|
||||
CBOR_FREE_BYTE_STRING(excludeList[m].id);
|
||||
for (int n = 0; n < excludeList[m].transports_len; n++) {
|
||||
for (size_t n = 0; n < excludeList[m].transports_len; n++) {
|
||||
CBOR_FREE_BYTE_STRING(excludeList[m].transports[n]);
|
||||
}
|
||||
}
|
||||
if (aut_data)
|
||||
if (aut_data) {
|
||||
free(aut_data);
|
||||
}
|
||||
if (error != CborNoError) {
|
||||
if (error == CborErrorImproperValue)
|
||||
if (error == CborErrorImproperValue) {
|
||||
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
res_APDU_size = resp_size;
|
||||
res_APDU_size = (uint16_t)resp_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,27 +18,18 @@
|
||||
#ifndef _CBOR_MAKE_CREDENTIAL_H_
|
||||
#define _CBOR_MAKE_CREDENTIAL_H_
|
||||
|
||||
#include "common.h"
|
||||
#include "mbedtls/chachapoly.h"
|
||||
#include <stdlib.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "ctap2_cbor.h"
|
||||
#include "random.h"
|
||||
#include "mbedtls/sha256.h"
|
||||
|
||||
typedef struct PublicKeyCredentialEntity
|
||||
{
|
||||
typedef struct PublicKeyCredentialEntity {
|
||||
CborCharString name;
|
||||
} PublicKeyCredentialEntity;
|
||||
|
||||
typedef struct PublicKeyCredentialRpEntity
|
||||
{
|
||||
typedef struct PublicKeyCredentialRpEntity {
|
||||
PublicKeyCredentialEntity parent;
|
||||
CborCharString id;
|
||||
} PublicKeyCredentialRpEntity;
|
||||
|
||||
typedef struct PublicKeyCredentialUserEntity
|
||||
{
|
||||
typedef struct PublicKeyCredentialUserEntity {
|
||||
PublicKeyCredentialEntity parent;
|
||||
CborByteString id;
|
||||
CborCharString displayName;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/*
|
||||
* This file is part of the Pico FIDO distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
@@ -16,23 +15,31 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ctap2_cbor.h"
|
||||
#include "file.h"
|
||||
#include "fido.h"
|
||||
#include "apdu.h"
|
||||
#include "ctap.h"
|
||||
#if !defined(ENABLE_EMULATION) && !defined(ESP_PLATFORM)
|
||||
#include "bsp/board.h"
|
||||
#endif
|
||||
#ifdef ESP_PLATFORM
|
||||
#include "esp_compat.h"
|
||||
#endif
|
||||
#include "fs/phy.h"
|
||||
|
||||
extern void scan_all();
|
||||
|
||||
int cbor_reset() {
|
||||
#if defined(ENABLE_POWER_ON_RESET) && ENABLE_POWER_ON_RESET==1
|
||||
if (board_millis() > 10000)
|
||||
#ifndef ENABLE_EMULATION
|
||||
#if defined(ENABLE_POWER_ON_RESET) && ENABLE_POWER_ON_RESET == 1
|
||||
if (!(phy_data.opts & PHY_OPT_DISABLE_POWER_RESET) && board_millis() > 10000) {
|
||||
return CTAP2_ERR_NOT_ALLOWED;
|
||||
}
|
||||
#endif
|
||||
if (wait_button_pressed() == true)
|
||||
if (wait_button_pressed() == true) {
|
||||
return CTAP2_ERR_USER_ACTION_TIMEOUT;
|
||||
}
|
||||
#endif
|
||||
initialize_flash(true);
|
||||
init_fido(true);
|
||||
init_fido();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/*
|
||||
* This file is part of the Pico FIDO distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
@@ -16,13 +15,12 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ctap2_cbor.h"
|
||||
#include "fido.h"
|
||||
#include "ctap.h"
|
||||
#include "bsp/board.h"
|
||||
|
||||
int cbor_selection() {
|
||||
if (wait_button_pressed() == true)
|
||||
if (wait_button_pressed() == true) {
|
||||
return CTAP2_ERR_USER_ACTION_TIMEOUT;
|
||||
}
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
@@ -15,13 +15,13 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "ctap2_cbor.h"
|
||||
#include "fido.h"
|
||||
#include "ctap.h"
|
||||
#include "hid/ctap_hid.h"
|
||||
#include "files.h"
|
||||
#include "apdu.h"
|
||||
#include "hsm.h"
|
||||
#include "pico_keys.h"
|
||||
#include "random.h"
|
||||
#include "mbedtls/ecdh.h"
|
||||
#include "mbedtls/chachapoly.h"
|
||||
@@ -31,7 +31,7 @@
|
||||
extern uint8_t keydev_dec[32];
|
||||
extern bool has_keydev_dec;
|
||||
|
||||
mse_t mse = {.init = false};
|
||||
mse_t mse = { .init = false };
|
||||
|
||||
int mse_decrypt_ct(uint8_t *data, size_t len) {
|
||||
mbedtls_chachapoly_context chatx;
|
||||
@@ -46,7 +46,7 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
CborParser parser;
|
||||
CborValue map;
|
||||
CborError error = CborNoError;
|
||||
CborByteString pinUvAuthParam = {0}, vendorParam = {0}, kax = {0}, kay = {0};
|
||||
CborByteString pinUvAuthParam = { 0 }, vendorParam = { 0 }, kax = { 0 }, kay = { 0 };
|
||||
size_t resp_size = 0;
|
||||
uint64_t vendorCmd = 0, pinUvAuthProtocol = 0;
|
||||
int64_t kty = 0, alg = 0, crv = 0;
|
||||
@@ -54,50 +54,34 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
|
||||
CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map));
|
||||
uint64_t val_c = 1;
|
||||
CBOR_PARSE_MAP_START(map, 1) {
|
||||
CBOR_PARSE_MAP_START(map, 1)
|
||||
{
|
||||
uint64_t val_u = 0;
|
||||
CBOR_FIELD_GET_UINT(val_u, 1);
|
||||
if (val_c <= 1 && val_c != val_u)
|
||||
if (val_c <= 1 && val_c != val_u) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
if (val_u < val_c)
|
||||
}
|
||||
if (val_u < val_c) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
|
||||
}
|
||||
val_c = val_u + 1;
|
||||
if (val_u == 0x01) {
|
||||
CBOR_FIELD_GET_UINT(vendorCmd, 1);
|
||||
}
|
||||
else if (val_u == 0x02) {
|
||||
uint64_t subpara = 0;
|
||||
CBOR_PARSE_MAP_START(_f1, 2) {
|
||||
CBOR_PARSE_MAP_START(_f1, 2)
|
||||
{
|
||||
CBOR_FIELD_GET_UINT(subpara, 2);
|
||||
if (subpara == 0x01) {
|
||||
CBOR_FIELD_GET_BYTES(vendorParam, 2);
|
||||
}
|
||||
else if (subpara == 0x02) {
|
||||
int64_t key = 0;
|
||||
CBOR_PARSE_MAP_START(_f2, 3) {
|
||||
CBOR_FIELD_GET_INT(key, 3);
|
||||
if (key == 1) {
|
||||
CBOR_FIELD_GET_INT(kty, 3);
|
||||
}
|
||||
else if (key == 3) {
|
||||
CBOR_FIELD_GET_INT(alg, 3);
|
||||
}
|
||||
else if (key == -1) {
|
||||
CBOR_FIELD_GET_INT(crv, 3);
|
||||
}
|
||||
else if (key == -2) {
|
||||
CBOR_FIELD_GET_BYTES(kax, 3);
|
||||
}
|
||||
else if (key == -3) {
|
||||
CBOR_FIELD_GET_BYTES(kay, 3);
|
||||
}
|
||||
else
|
||||
CBOR_ADVANCE(3);
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f2, 3);
|
||||
CBOR_CHECK(COSE_read_key(&_f2, &kty, &alg, &crv, &kax, &kay));
|
||||
}
|
||||
else
|
||||
else {
|
||||
CBOR_ADVANCE(2);
|
||||
}
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f1, 2);
|
||||
}
|
||||
@@ -110,12 +94,13 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
}
|
||||
CBOR_PARSE_MAP_END(map, 1);
|
||||
|
||||
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
|
||||
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_CBOR_PAYLOAD, 0);
|
||||
|
||||
if (cmd == CTAP_VENDOR_BACKUP) {
|
||||
if (vendorCmd == 0x01) {
|
||||
if (has_keydev_dec == false)
|
||||
if (has_keydev_dec == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
|
||||
@@ -123,13 +108,14 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, file_get_data(ef_keydev_enc), file_get_size(ef_keydev_enc)));
|
||||
}
|
||||
else if (vendorCmd == 0x02) {
|
||||
if (vendorParam.present == false)
|
||||
if (vendorParam.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
uint8_t zeros[32];
|
||||
memset(zeros, 0, sizeof(zeros));
|
||||
flash_write_data_to_file(ef_keydev_enc, vendorParam.data, vendorParam.len);
|
||||
flash_write_data_to_file(ef_keydev, zeros, file_get_size(ef_keydev)); // Overwrite ef with 0
|
||||
flash_write_data_to_file(ef_keydev, NULL, 0); // Set ef to 0 bytes
|
||||
file_put_data(ef_keydev_enc, vendorParam.data, (uint16_t)vendorParam.len);
|
||||
file_put_data(ef_keydev, zeros, file_get_size(ef_keydev)); // Overwrite ef with 0
|
||||
file_put_data(ef_keydev, NULL, 0); // Set ef to 0 bytes
|
||||
low_flash_available();
|
||||
goto err;
|
||||
}
|
||||
@@ -139,8 +125,9 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
}
|
||||
else if (cmd == CTAP_VENDOR_MSE) {
|
||||
if (vendorCmd == 0x01) { // KeyAgreement
|
||||
if (kax.present == false || kay.present == false || alg == 0)
|
||||
if (kax.present == false || kay.present == false || alg == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
|
||||
mbedtls_ecdh_context hkey;
|
||||
mbedtls_ecdh_init(&hkey);
|
||||
@@ -161,7 +148,7 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
|
||||
uint8_t buf[MBEDTLS_ECP_MAX_BYTES];
|
||||
size_t olen = 0;
|
||||
ret = mbedtls_ecp_point_write_binary(&hkey.ctx.mbed_ecdh.grp, &hkey.ctx.mbed_ecdh.Qp, MBEDTLS_ECP_PF_UNCOMPRESSED, &olen, mse.Qpt, sizeof(mse.Qpt));
|
||||
ret = mbedtls_ecp_point_write_binary(&hkey.ctx.mbed_ecdh.grp, &hkey.ctx.mbed_ecdh.Qp, MBEDTLS_ECP_PF_UNCOMPRESSED, &olen, mse.Qpt,sizeof(mse.Qpt));
|
||||
if (ret != 0) {
|
||||
mbedtls_ecdh_free(&hkey);
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
@@ -183,28 +170,14 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
|
||||
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, 5));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 2));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 3));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, -FIDO2_ALG_ECDH_ES_HKDF_256));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, FIDO2_CURVE_P256));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 2));
|
||||
uint8_t pkey[32];
|
||||
mbedtls_mpi_write_binary(&hkey.ctx.mbed_ecdh.Q.X, pkey, 32);
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, pkey, 32));
|
||||
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 3));
|
||||
mbedtls_mpi_write_binary(&hkey.ctx.mbed_ecdh.Q.Y, pkey, 32);
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, pkey, 32));
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
|
||||
CBOR_CHECK(COSE_key_shared(&hkey, &mapEncoder, &mapEncoder2));
|
||||
mbedtls_ecdh_free(&hkey);
|
||||
}
|
||||
}
|
||||
else if (cmd == CTAP_VENDOR_UNLOCK) {
|
||||
if (mse.init == false)
|
||||
if (mse.init == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
|
||||
}
|
||||
|
||||
mbedtls_chachapoly_context chatx;
|
||||
int ret = mse_decrypt_ct(vendorParam.data, vendorParam.len);
|
||||
@@ -212,8 +185,9 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
if (!file_has_data(ef_keydev_enc))
|
||||
if (!file_has_data(ef_keydev_enc)) {
|
||||
CBOR_ERROR(CTAP2_ERR_INTEGRITY_FAILURE);
|
||||
}
|
||||
|
||||
uint8_t *keyenc = file_get_data(ef_keydev_enc);
|
||||
size_t keyenc_len = file_get_size(ef_keydev_enc);
|
||||
@@ -221,7 +195,7 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
mbedtls_chachapoly_setkey(&chatx, vendorParam.data);
|
||||
ret = mbedtls_chachapoly_auth_decrypt(&chatx, sizeof(keydev_dec), keyenc, NULL, 0, keyenc + keyenc_len - 16, keyenc + 12, keydev_dec);
|
||||
mbedtls_chachapoly_free(&chatx);
|
||||
if (ret != 0){
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
has_keydev_dec = true;
|
||||
@@ -242,12 +216,10 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
CBOR_ERROR(CTAP2_ERR_PROCESSING);
|
||||
}
|
||||
pico_unique_board_id_t rpiid;
|
||||
pico_get_unique_board_id(&rpiid);
|
||||
mbedtls_x509write_csr ctx;
|
||||
mbedtls_x509write_csr_init(&ctx);
|
||||
snprintf((char *)buffer, sizeof(buffer), "C=ES,O=Pico Keys,OU=Authenticator Attestation,CN=Pico Fido EE Serial %llu", ((uint64_t)rpiid.id[0] << 56) | ((uint64_t)rpiid.id[1] << 48) | ((uint64_t)rpiid.id[2] << 40) | ((uint64_t)rpiid.id[3] << 32) | (rpiid.id[4] << 24) | (rpiid.id[5] << 16) | (rpiid.id[6] << 8) | rpiid.id[7]);
|
||||
mbedtls_x509write_csr_set_subject_name(&ctx, (char *)buffer);
|
||||
snprintf((char *) buffer, sizeof(buffer), "C=ES,O=Pico Keys,OU=Authenticator Attestation,CN=Pico Fido EE Serial %s", pico_serial_str);
|
||||
mbedtls_x509write_csr_set_subject_name(&ctx, (char *) buffer);
|
||||
mbedtls_pk_context key;
|
||||
mbedtls_pk_init(&key);
|
||||
mbedtls_pk_setup(&key, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY));
|
||||
@@ -266,37 +238,78 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, buffer + sizeof(buffer) - ret, ret));
|
||||
}
|
||||
else if (vendorCmd == 0x02) {
|
||||
if (vendorParam.present == false)
|
||||
if (vendorParam.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
file_t *ef_ee_ea = search_by_fid(EF_EE_DEV_EA, NULL, SPECIFY_EF);
|
||||
if (ef_ee_ea)
|
||||
flash_write_data_to_file(ef_ee_ea, vendorParam.data, vendorParam.len);
|
||||
if (ef_ee_ea) {
|
||||
file_put_data(ef_ee_ea, vendorParam.data, (uint16_t)vendorParam.len);
|
||||
}
|
||||
low_flash_available();
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
else
|
||||
#ifndef ENABLE_EMULATION
|
||||
else if (cmd == CTAP_VENDOR_PHY_OPTS) {
|
||||
if (vendorCmd == 0x01) {
|
||||
uint16_t opts = 0;
|
||||
if (file_has_data(ef_phy)) {
|
||||
uint8_t *data = file_get_data(ef_phy);
|
||||
opts = get_uint16_t_be(data + PHY_OPTS);
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, opts));
|
||||
}
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if (cmd == CTAP_VENDOR_MEMORY) {
|
||||
if (vendorCmd == 0x01) {
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 5));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, flash_free_space()));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x02));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, flash_used_space()));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, flash_total_space()));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, flash_num_files()));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x05));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, flash_size()));
|
||||
}
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION);
|
||||
}
|
||||
}
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION);
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
resp_size = cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1);
|
||||
|
||||
err:
|
||||
err:
|
||||
CBOR_FREE_BYTE_STRING(pinUvAuthParam);
|
||||
CBOR_FREE_BYTE_STRING(vendorParam);
|
||||
|
||||
if (error != CborNoError) {
|
||||
if (error == CborErrorImproperValue)
|
||||
if (error == CborErrorImproperValue) {
|
||||
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
res_APDU_size = resp_size;
|
||||
res_APDU_size = (uint16_t)resp_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cbor_vendor(const uint8_t *data, size_t len) {
|
||||
if (len == 0)
|
||||
if (len == 0) {
|
||||
return CTAP1_ERR_INVALID_LEN;
|
||||
if (data[0] >= CTAP_VENDOR_BACKUP)
|
||||
}
|
||||
if (data[0] >= CTAP_VENDOR_BACKUP) {
|
||||
return cbor_vendor_generic(data[0], data + 1, len - 1);
|
||||
}
|
||||
return CTAP2_ERR_INVALID_CBOR;
|
||||
}
|
||||
|
||||
@@ -16,76 +16,77 @@
|
||||
*/
|
||||
|
||||
#include "fido.h"
|
||||
#include "hsm.h"
|
||||
#include "pico_keys.h"
|
||||
#include "apdu.h"
|
||||
#include "ctap.h"
|
||||
#include "mbedtls/ecdsa.h"
|
||||
#include "random.h"
|
||||
#include "files.h"
|
||||
#include "credential.h"
|
||||
|
||||
int cmd_authenticate() {
|
||||
CTAP_AUTHENTICATE_REQ *req = (CTAP_AUTHENTICATE_REQ *)apdu.data;
|
||||
CTAP_AUTHENTICATE_RESP *resp = (CTAP_AUTHENTICATE_RESP *)res_APDU;
|
||||
//if (scan_files(true) != CCID_OK)
|
||||
CTAP_AUTHENTICATE_REQ *req = (CTAP_AUTHENTICATE_REQ *) apdu.data;
|
||||
CTAP_AUTHENTICATE_RESP *resp = (CTAP_AUTHENTICATE_RESP *) res_APDU;
|
||||
//if (scan_files(true) != PICOKEY_OK)
|
||||
// return SW_EXEC_ERROR();
|
||||
if (apdu.nc < CTAP_CHAL_SIZE+CTAP_APPID_SIZE+1+1)
|
||||
if (apdu.nc < CTAP_CHAL_SIZE + CTAP_APPID_SIZE + 1 + 1) {
|
||||
return SW_WRONG_DATA();
|
||||
if (req->keyHandleLen < KEY_HANDLE_LEN)
|
||||
}
|
||||
if (req->keyHandleLen < KEY_HANDLE_LEN) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
if (P1(apdu) == CTAP_AUTH_ENFORCE && wait_button_pressed() == true)
|
||||
}
|
||||
if (P1(apdu) == CTAP_AUTH_ENFORCE && wait_button_pressed() == true) {
|
||||
return SW_CONDITIONS_NOT_SATISFIED();
|
||||
}
|
||||
|
||||
mbedtls_ecdsa_context key;
|
||||
mbedtls_ecdsa_init(&key);
|
||||
mbedtls_ecp_keypair key;
|
||||
mbedtls_ecp_keypair_init(&key);
|
||||
int ret = 0;
|
||||
uint8_t *tmp_kh = (uint8_t *)calloc(1, req->keyHandleLen);
|
||||
uint8_t *tmp_kh = (uint8_t *) calloc(1, req->keyHandleLen);
|
||||
memcpy(tmp_kh, req->keyHandle, req->keyHandleLen);
|
||||
if (credential_verify(tmp_kh, req->keyHandleLen, req->appId) == 0) {
|
||||
if (credential_verify(tmp_kh, req->keyHandleLen, req->appId, false) == 0) {
|
||||
ret = fido_load_key(FIDO2_CURVE_P256, req->keyHandle, &key);
|
||||
}
|
||||
else {
|
||||
ret = derive_key(req->appId, false, req->keyHandle, MBEDTLS_ECP_DP_SECP256R1, &key);
|
||||
if (verify_key(req->appId, req->keyHandle, &key) != 0) {
|
||||
mbedtls_ecdsa_free(&key);
|
||||
mbedtls_ecp_keypair_free(&key);
|
||||
free(tmp_kh);
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
}
|
||||
free(tmp_kh);
|
||||
if (ret != CCID_OK) {
|
||||
mbedtls_ecdsa_free(&key);
|
||||
if (ret != PICOKEY_OK) {
|
||||
mbedtls_ecp_keypair_free(&key);
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
if (P1(apdu) == CTAP_AUTH_CHECK_ONLY) {
|
||||
mbedtls_ecdsa_free(&key);
|
||||
mbedtls_ecp_keypair_free(&key);
|
||||
return SW_CONDITIONS_NOT_SATISFIED();
|
||||
}
|
||||
resp->flags = 0;
|
||||
resp->flags |= P1(apdu) == CTAP_AUTH_ENFORCE ? CTAP_AUTH_FLAG_TUP : 0x0;
|
||||
uint32_t ctr = get_sign_counter();
|
||||
resp->ctr[0] = ctr >> 24;
|
||||
resp->ctr[1] = ctr >> 16;
|
||||
resp->ctr[2] = ctr >> 8;
|
||||
resp->ctr[3] = ctr & 0xff;
|
||||
put_uint32_t_be(ctr, resp->ctr);
|
||||
uint8_t hash[32], sig_base[CTAP_APPID_SIZE + 1 + 4 + CTAP_CHAL_SIZE];
|
||||
memcpy(sig_base, req->appId, CTAP_APPID_SIZE);
|
||||
memcpy(sig_base+CTAP_APPID_SIZE, &resp->flags, sizeof(uint8_t));
|
||||
memcpy(sig_base + CTAP_APPID_SIZE, &resp->flags, sizeof(uint8_t));
|
||||
memcpy(sig_base + CTAP_APPID_SIZE + 1, resp->ctr, 4);
|
||||
memcpy(sig_base + CTAP_APPID_SIZE + 1 + 4, req->chal, CTAP_CHAL_SIZE);
|
||||
ret = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), sig_base, sizeof(sig_base), hash);
|
||||
if (ret != 0) {
|
||||
mbedtls_ecdsa_free(&key);
|
||||
mbedtls_ecp_keypair_free(&key);
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
size_t olen = 0;
|
||||
ret = mbedtls_ecdsa_write_signature(&key, MBEDTLS_MD_SHA256, hash, 32, (uint8_t *)resp->sig, CTAP_MAX_EC_SIG_SIZE, &olen, random_gen, NULL);
|
||||
mbedtls_ecdsa_free(&key);
|
||||
if (ret != 0)
|
||||
ret = mbedtls_ecdsa_write_signature(&key, MBEDTLS_MD_SHA256, hash, 32, (uint8_t *) resp->sig, CTAP_MAX_EC_SIG_SIZE, &olen, random_gen, NULL);
|
||||
mbedtls_ecp_keypair_free(&key);
|
||||
if (ret != 0) {
|
||||
return SW_EXEC_ERROR();
|
||||
res_APDU_size = 1 + 4 + olen;
|
||||
}
|
||||
res_APDU_size = 1 + 4 + (uint16_t)olen;
|
||||
|
||||
ctr++;
|
||||
flash_write_data_to_file(ef_counter, (uint8_t *)&ctr, sizeof(ctr));
|
||||
file_put_data(ef_counter, (uint8_t *) &ctr, sizeof(ctr));
|
||||
low_flash_available();
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
@@ -16,64 +16,132 @@
|
||||
*/
|
||||
|
||||
#include "fido.h"
|
||||
#include "hsm.h"
|
||||
#include "pico_keys.h"
|
||||
#include "apdu.h"
|
||||
#include "ctap.h"
|
||||
#include "mbedtls/ecdsa.h"
|
||||
#include "random.h"
|
||||
#include "files.h"
|
||||
#include "hid/ctap_hid.h"
|
||||
#include "management.h"
|
||||
|
||||
const uint8_t *bogus_firefox = (const uint8_t *)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
|
||||
const uint8_t *bogus_chrome = (const uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||
const uint8_t u2f_aid[] = {
|
||||
7,
|
||||
0xA0, 0x00, 0x00, 0x05, 0x27, 0x10, 0x02
|
||||
};
|
||||
|
||||
int u2f_unload();
|
||||
int u2f_process_apdu();
|
||||
|
||||
int u2f_select(app_t *a, uint8_t force) {
|
||||
(void) force;
|
||||
if (cap_supported(CAP_U2F)) {
|
||||
a->process_apdu = u2f_process_apdu;
|
||||
a->unload = u2f_unload;
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
return PICOKEY_ERR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
INITIALIZER ( u2f_ctor ) {
|
||||
register_app(u2f_select, u2f_aid);
|
||||
}
|
||||
|
||||
int u2f_unload() {
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
const uint8_t *bogus_firefox = (const uint8_t *) "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
|
||||
const uint8_t *bogus_chrome = (const uint8_t *) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||
|
||||
extern int ctap_error(uint8_t error);
|
||||
int cmd_register() {
|
||||
CTAP_REGISTER_REQ *req = (CTAP_REGISTER_REQ *)apdu.data;
|
||||
CTAP_REGISTER_RESP *resp = (CTAP_REGISTER_RESP *)res_APDU;
|
||||
CTAP_REGISTER_REQ *req = (CTAP_REGISTER_REQ *) apdu.data;
|
||||
CTAP_REGISTER_RESP *resp = (CTAP_REGISTER_RESP *) res_APDU;
|
||||
resp->registerId = CTAP_REGISTER_ID;
|
||||
resp->keyHandleLen = KEY_HANDLE_LEN;
|
||||
//if (scan_files(true) != CCID_OK)
|
||||
//if (scan_files(true) != PICOKEY_OK)
|
||||
// return SW_EXEC_ERROR();
|
||||
if (apdu.nc != CTAP_APPID_SIZE + CTAP_CHAL_SIZE)
|
||||
if (apdu.nc != CTAP_APPID_SIZE + CTAP_CHAL_SIZE) {
|
||||
return SW_WRONG_LENGTH();
|
||||
if (wait_button_pressed() == true)
|
||||
}
|
||||
if (wait_button_pressed() == true) {
|
||||
return SW_CONDITIONS_NOT_SATISFIED();
|
||||
if (memcmp(req->appId, bogus_firefox, CTAP_APPID_SIZE) == 0 || memcmp(req->appId, bogus_chrome, CTAP_APPID_SIZE) == 0)
|
||||
return ctap_error(CTAP1_ERR_CHANNEL_BUSY);
|
||||
}
|
||||
if (memcmp(req->appId, bogus_firefox,
|
||||
CTAP_APPID_SIZE) == 0 || memcmp(req->appId, bogus_chrome, CTAP_APPID_SIZE) == 0)
|
||||
#ifndef ENABLE_EMULATION
|
||||
{ return ctap_error(CTAP1_ERR_CHANNEL_BUSY); }
|
||||
#else
|
||||
{ return SW_DATA_INVALID(); }
|
||||
#endif
|
||||
mbedtls_ecdsa_context key;
|
||||
mbedtls_ecdsa_init(&key);
|
||||
int ret = derive_key(req->appId, true, resp->keyHandleCertSig, MBEDTLS_ECP_DP_SECP256R1, &key);
|
||||
if (ret != CCID_OK) {
|
||||
if (ret != PICOKEY_OK) {
|
||||
mbedtls_ecdsa_free(&key);
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
size_t olen = 0;
|
||||
ret = mbedtls_ecp_point_write_binary(&key.grp, &key.Q, MBEDTLS_ECP_PF_UNCOMPRESSED, &olen, (uint8_t *)&resp->pubKey, CTAP_EC_POINT_SIZE);
|
||||
ret = mbedtls_ecp_point_write_binary(&key.grp, &key.Q, MBEDTLS_ECP_PF_UNCOMPRESSED, &olen, (uint8_t *) &resp->pubKey, CTAP_EC_POINT_SIZE);
|
||||
mbedtls_ecdsa_free(&key);
|
||||
if (ret != 0) {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
size_t ef_certdev_size = file_get_size(ef_certdev);
|
||||
uint16_t ef_certdev_size = file_get_size(ef_certdev);
|
||||
memcpy(resp->keyHandleCertSig + KEY_HANDLE_LEN, file_get_data(ef_certdev), ef_certdev_size);
|
||||
uint8_t hash[32], sign_base[1 + CTAP_APPID_SIZE + CTAP_CHAL_SIZE + KEY_HANDLE_LEN + CTAP_EC_POINT_SIZE];
|
||||
sign_base[0] = CTAP_REGISTER_HASH_ID;
|
||||
memcpy(sign_base + 1, req->appId, CTAP_APPID_SIZE);
|
||||
memcpy(sign_base + 1 + CTAP_APPID_SIZE, req->chal, CTAP_CHAL_SIZE);
|
||||
memcpy(sign_base + 1 + CTAP_APPID_SIZE + CTAP_CHAL_SIZE, resp->keyHandleCertSig, KEY_HANDLE_LEN);
|
||||
memcpy(sign_base + 1 + CTAP_APPID_SIZE + CTAP_CHAL_SIZE + KEY_HANDLE_LEN, (uint8_t *)&resp->pubKey, CTAP_EC_POINT_SIZE);
|
||||
memcpy(sign_base + 1 + CTAP_APPID_SIZE + CTAP_CHAL_SIZE + KEY_HANDLE_LEN, (uint8_t *) &resp->pubKey, CTAP_EC_POINT_SIZE);
|
||||
ret = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), sign_base, sizeof(sign_base), hash);
|
||||
if (ret != 0)
|
||||
if (ret != 0) {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
mbedtls_ecdsa_init(&key);
|
||||
ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &key, file_get_data(ef_keydev), 32);
|
||||
if (ret != CCID_OK) {
|
||||
uint8_t key_dev[32] = {0};
|
||||
ret = load_keydev(key_dev);
|
||||
if (ret != PICOKEY_OK) {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &key, key_dev, 32);
|
||||
mbedtls_platform_zeroize(key_dev, sizeof(key_dev));
|
||||
if (ret != PICOKEY_OK) {
|
||||
mbedtls_ecdsa_free(&key);
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
ret = mbedtls_ecdsa_write_signature(&key, MBEDTLS_MD_SHA256, hash, 32, (uint8_t *)resp->keyHandleCertSig + KEY_HANDLE_LEN + ef_certdev_size, CTAP_MAX_EC_SIG_SIZE, &olen, random_gen, NULL);
|
||||
ret = mbedtls_ecdsa_write_signature(&key,MBEDTLS_MD_SHA256, hash, 32, (uint8_t *) resp->keyHandleCertSig + KEY_HANDLE_LEN + ef_certdev_size, CTAP_MAX_EC_SIG_SIZE, &olen, random_gen, NULL);
|
||||
mbedtls_ecdsa_free(&key);
|
||||
if (ret != 0)
|
||||
if (ret != 0) {
|
||||
return SW_EXEC_ERROR();
|
||||
res_APDU_size = sizeof(CTAP_REGISTER_RESP) - sizeof(resp->keyHandleCertSig) + KEY_HANDLE_LEN + ef_certdev_size + olen;
|
||||
}
|
||||
res_APDU_size = sizeof(CTAP_REGISTER_RESP) - sizeof(resp->keyHandleCertSig) + KEY_HANDLE_LEN + ef_certdev_size + (uint16_t)olen;
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
extern int cmd_register();
|
||||
extern int cmd_authenticate();
|
||||
extern int cmd_version();
|
||||
|
||||
static const cmd_t cmds[] = {
|
||||
{ CTAP_REGISTER, cmd_register },
|
||||
{ CTAP_AUTHENTICATE, cmd_authenticate },
|
||||
{ CTAP_VERSION, cmd_version },
|
||||
{ 0x00, 0x0 }
|
||||
};
|
||||
|
||||
int u2f_process_apdu() {
|
||||
if (CLA(apdu) != 0x00) {
|
||||
return SW_CLA_NOT_SUPPORTED();
|
||||
}
|
||||
if (cap_supported(CAP_U2F)) {
|
||||
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
|
||||
if (cmd->ins == INS(apdu)) {
|
||||
int r = cmd->cmd_handler();
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
return SW_INS_NOT_SUPPORTED();
|
||||
}
|
||||
|
||||
@@ -15,12 +15,11 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "apdu.h"
|
||||
#include "hsm.h"
|
||||
#include "pico_keys.h"
|
||||
|
||||
int cmd_version() {
|
||||
memcpy(res_APDU, "U2F_V2", strlen("U2F_V2"));
|
||||
res_APDU_size = strlen("U2F_V2");
|
||||
res_APDU_size = (uint16_t)strlen("U2F_V2");
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
@@ -15,40 +15,90 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "mbedtls/chachapoly.h"
|
||||
#include "mbedtls/sha256.h"
|
||||
#include "credential.h"
|
||||
#if !defined(ENABLE_EMULATION) && !defined(ESP_PLATFORM)
|
||||
#include "bsp/board.h"
|
||||
#endif
|
||||
#include "hid/ctap_hid.h"
|
||||
#include "fido.h"
|
||||
#include "ctap.h"
|
||||
#include "random.h"
|
||||
#include "files.h"
|
||||
#include "file.h"
|
||||
#include "hsm.h"
|
||||
#include "pico_keys.h"
|
||||
#include "otp.h"
|
||||
|
||||
int credential_derive_chacha_key(uint8_t *outk);
|
||||
int credential_derive_chacha_key(uint8_t *outk, const uint8_t *);
|
||||
|
||||
int credential_verify(uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash) {
|
||||
if (cred_id_len < 4+12+16)
|
||||
static int credential_silent_tag(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash, uint8_t *outk) {
|
||||
mbedtls_sha256_context ctx;
|
||||
mbedtls_sha256_init(&ctx);
|
||||
mbedtls_sha256_starts(&ctx, 0);
|
||||
if (otp_key_1) {
|
||||
mbedtls_sha256_update(&ctx, otp_key_1, 32);
|
||||
}
|
||||
else {
|
||||
mbedtls_sha256_update(&ctx, pico_serial.id, sizeof(pico_serial.id));
|
||||
}
|
||||
mbedtls_sha256_update(&ctx, rp_id_hash, 32);
|
||||
mbedtls_sha256_finish(&ctx, outk);
|
||||
mbedtls_sha256_free(&ctx);
|
||||
|
||||
return mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), outk, 32, cred_id, cred_id_len - CRED_SILENT_TAG_LEN, outk);
|
||||
}
|
||||
|
||||
int credential_verify(uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash, bool silent) {
|
||||
if (cred_id_len < 4 + 12 + 16) {
|
||||
return -1;
|
||||
}
|
||||
uint8_t key[32] = {0}, *iv = cred_id + CRED_PROTO_LEN, *cipher = cred_id + CRED_PROTO_LEN + CRED_IV_LEN,
|
||||
*tag = cred_id + cred_id_len - CRED_TAG_LEN;
|
||||
cred_proto_t proto = CRED_PROTO_21;
|
||||
if (memcmp(cred_id, CRED_PROTO_22_S, CRED_PROTO_LEN) == 0) { // New format
|
||||
tag = cred_id + cred_id_len - CRED_SILENT_TAG_LEN - CRED_TAG_LEN;
|
||||
proto = CRED_PROTO_22;
|
||||
}
|
||||
int ret = 0;
|
||||
if (!silent) {
|
||||
int hdr_len = CRED_PROTO_LEN + CRED_IV_LEN + CRED_TAG_LEN;
|
||||
if (proto == CRED_PROTO_22) {
|
||||
hdr_len += CRED_SILENT_TAG_LEN;
|
||||
}
|
||||
credential_derive_chacha_key(key, cred_id);
|
||||
mbedtls_chachapoly_context chatx;
|
||||
mbedtls_chachapoly_init(&chatx);
|
||||
mbedtls_chachapoly_setkey(&chatx, key);
|
||||
ret = mbedtls_chachapoly_auth_decrypt(&chatx, cred_id_len - hdr_len, iv, rp_id_hash, 32, tag, cipher, cipher);
|
||||
mbedtls_chachapoly_free(&chatx);
|
||||
}
|
||||
else {
|
||||
if (proto <= CRED_PROTO_21) {
|
||||
return -1;
|
||||
uint8_t key[32], *iv = cred_id + 4, *cipher = cred_id + 4 + 12, *tag = cred_id + cred_id_len - 16;
|
||||
memset(key, 0, sizeof(key));
|
||||
credential_derive_chacha_key(key);
|
||||
mbedtls_chachapoly_context chatx;
|
||||
mbedtls_chachapoly_init(&chatx);
|
||||
mbedtls_chachapoly_setkey(&chatx, key);
|
||||
int ret = mbedtls_chachapoly_auth_decrypt(&chatx, cred_id_len - (4 + 12 + 16), iv, rp_id_hash, 32, tag, cipher, cipher);
|
||||
mbedtls_chachapoly_free(&chatx);
|
||||
}
|
||||
uint8_t outk[32];
|
||||
ret = credential_silent_tag(cred_id, cred_id_len, rp_id_hash, outk);
|
||||
ret = memcmp(outk, cred_id + cred_id_len - CRED_SILENT_TAG_LEN, CRED_SILENT_TAG_LEN);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int credential_create(CborCharString *rpId, CborByteString *userId, CborCharString *userName, CborCharString *userDisplayName, CredOptions *opts, CredExtensions *extensions, bool use_sign_count, int alg, int curve, uint8_t *cred_id, size_t *cred_id_len) {
|
||||
int credential_create(CborCharString *rpId,
|
||||
CborByteString *userId,
|
||||
CborCharString *userName,
|
||||
CborCharString *userDisplayName,
|
||||
CredOptions *opts,
|
||||
CredExtensions *extensions,
|
||||
bool use_sign_count,
|
||||
int alg,
|
||||
int curve,
|
||||
uint8_t *cred_id,
|
||||
size_t *cred_id_len) {
|
||||
CborEncoder encoder, mapEncoder, mapEncoder2;
|
||||
CborError error = CborNoError;
|
||||
uint8_t rp_id_hash[32];
|
||||
mbedtls_sha256((uint8_t *)rpId->data, rpId->len, rp_id_hash, 0);
|
||||
cbor_encoder_init(&encoder, cred_id+4+12, MAX_CRED_ID_LENGTH-(4+12+16), 0);
|
||||
mbedtls_sha256((uint8_t *) rpId->data, rpId->len, rp_id_hash, 0);
|
||||
cbor_encoder_init(&encoder, cred_id + 4 + 12, MAX_CRED_ID_LENGTH - (4 + 12 + 16), 0);
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, CborIndefiniteLength));
|
||||
CBOR_APPEND_KEY_UINT_VAL_STRING(mapEncoder, 0x01, *rpId);
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x02));
|
||||
@@ -60,7 +110,8 @@ int credential_create(CborCharString *rpId, CborByteString *userId, CborCharStri
|
||||
if (extensions->present == true) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x07));
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, CborIndefiniteLength));
|
||||
if (extensions->credBlob.present == true && extensions->credBlob.len < MAX_CREDBLOB_LENGTH) {
|
||||
if (extensions->credBlob.present == true &&
|
||||
extensions->credBlob.len < MAX_CREDBLOB_LENGTH) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "credBlob"));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, extensions->credBlob.data, extensions->credBlob.len));
|
||||
}
|
||||
@@ -76,6 +127,10 @@ int credential_create(CborCharString *rpId, CborByteString *userId, CborCharStri
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "largeBlobKey"));
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder2, true));
|
||||
}
|
||||
if (extensions->thirdPartyPayment == ptrue) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "thirdPartyPayment"));
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder2, true));
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
|
||||
}
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x08));
|
||||
@@ -95,27 +150,31 @@ int credential_create(CborCharString *rpId, CborByteString *userId, CborCharStri
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
size_t rs = cbor_encoder_get_buffer_size(&encoder, cred_id);
|
||||
*cred_id_len = 4 + 12 + rs + 16;
|
||||
uint8_t key[32];
|
||||
memset(key, 0, sizeof(key));
|
||||
credential_derive_chacha_key(key);
|
||||
uint8_t iv[12];
|
||||
*cred_id_len = CRED_PROTO_LEN + CRED_IV_LEN + rs + CRED_TAG_LEN + CRED_SILENT_TAG_LEN;
|
||||
uint8_t key[32] = {0};
|
||||
credential_derive_chacha_key(key, (const uint8_t *)CRED_PROTO);
|
||||
uint8_t iv[CRED_IV_LEN] = {0};
|
||||
random_gen(NULL, iv, sizeof(iv));
|
||||
mbedtls_chachapoly_context chatx;
|
||||
mbedtls_chachapoly_init(&chatx);
|
||||
mbedtls_chachapoly_setkey(&chatx, key);
|
||||
int ret = mbedtls_chachapoly_encrypt_and_tag(&chatx, rs, iv, rp_id_hash, 32, cred_id + 4 + 12, cred_id + 4 + 12, cred_id + 4 + 12 + rs);
|
||||
int ret = mbedtls_chachapoly_encrypt_and_tag(&chatx, rs, iv, rp_id_hash, 32,
|
||||
cred_id + CRED_PROTO_LEN + CRED_IV_LEN,
|
||||
cred_id + CRED_PROTO_LEN + CRED_IV_LEN,
|
||||
cred_id + CRED_PROTO_LEN + CRED_IV_LEN + rs);
|
||||
mbedtls_chachapoly_free(&chatx);
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
}
|
||||
memcpy(cred_id, CRED_PROTO, 4);
|
||||
memcpy(cred_id + 4, iv, 12);
|
||||
memcpy(cred_id, CRED_PROTO, CRED_PROTO_LEN);
|
||||
memcpy(cred_id + CRED_PROTO_LEN, iv, CRED_IV_LEN);
|
||||
credential_silent_tag(cred_id, *cred_id_len, rp_id_hash, cred_id + CRED_PROTO_LEN + CRED_IV_LEN + rs + CRED_TAG_LEN);
|
||||
|
||||
err:
|
||||
err:
|
||||
if (error != CborNoError) {
|
||||
if (error == CborErrorImproperValue)
|
||||
if (error == CborErrorImproperValue) {
|
||||
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
return 0;
|
||||
@@ -123,13 +182,18 @@ int credential_create(CborCharString *rpId, CborByteString *userId, CborCharStri
|
||||
|
||||
int credential_load(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash, Credential *cred) {
|
||||
int ret = 0;
|
||||
CborError error;
|
||||
uint8_t *copy_cred_id = (uint8_t *)calloc(1, cred_id_len);
|
||||
CborError error = CborNoError;
|
||||
uint8_t *copy_cred_id = (uint8_t *) calloc(1, cred_id_len);
|
||||
if (!cred) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_CREDENTIAL);
|
||||
}
|
||||
memset(cred, 0, sizeof(Credential));
|
||||
memcpy(copy_cred_id, cred_id, cred_id_len);
|
||||
ret = credential_verify(copy_cred_id, cred_id_len, rp_id_hash);
|
||||
ret = credential_verify(copy_cred_id, cred_id_len, rp_id_hash, false);
|
||||
if (ret != 0) { // U2F?
|
||||
if (cred_id_len != KEY_HANDLE_LEN || verify_key(rp_id_hash, cred_id, NULL) != 0)
|
||||
if (cred_id_len != KEY_HANDLE_LEN || verify_key(rp_id_hash, cred_id, NULL) != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_CREDENTIAL);
|
||||
}
|
||||
}
|
||||
else {
|
||||
CborParser parser;
|
||||
@@ -137,8 +201,10 @@ int credential_load(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *r
|
||||
memset(cred, 0, sizeof(Credential));
|
||||
cred->curve = FIDO2_CURVE_P256;
|
||||
cred->alg = FIDO2_ALG_ES256;
|
||||
CBOR_CHECK(cbor_parser_init(copy_cred_id + 4 + 12, cred_id_len - (4 + 12 + 16), 0, &parser, &map));
|
||||
CBOR_PARSE_MAP_START(map, 1) {
|
||||
CBOR_CHECK(cbor_parser_init(copy_cred_id + 4 + 12, cred_id_len - (4 + 12 + 16), 0, &parser,
|
||||
&map));
|
||||
CBOR_PARSE_MAP_START(map, 1)
|
||||
{
|
||||
uint64_t val_u = 0;
|
||||
CBOR_FIELD_GET_UINT(val_u, 1);
|
||||
if (val_u == 0x01) {
|
||||
@@ -165,6 +231,7 @@ int credential_load(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *r
|
||||
CBOR_FIELD_KEY_TEXT_VAL_UINT(2, "credProtect", cred->extensions.credProtect);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BYTES(2, "credBlob", cred->extensions.credBlob);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "largeBlobKey", cred->extensions.largeBlobKey);
|
||||
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "thirdPartyPayment", cred->extensions.thirdPartyPayment);
|
||||
CBOR_ADVANCE(2);
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f1, 2);
|
||||
@@ -194,34 +261,40 @@ int credential_load(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *r
|
||||
}
|
||||
}
|
||||
cred->id.present = true;
|
||||
cred->id.data = (uint8_t *)calloc(1, cred_id_len);
|
||||
cred->id.data = (uint8_t *) calloc(1, cred_id_len);
|
||||
memcpy(cred->id.data, cred_id, cred_id_len);
|
||||
cred->id.len = cred_id_len;
|
||||
cred->present = true;
|
||||
err:
|
||||
err:
|
||||
free(copy_cred_id);
|
||||
if (error != CborNoError) {
|
||||
if (error == CborErrorImproperValue)
|
||||
if (error == CborErrorImproperValue) {
|
||||
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void credential_free(Credential *cred) {
|
||||
CBOR_FREE_BYTE_STRING(cred->rpId);
|
||||
CBOR_FREE_BYTE_STRING(cred->userId);
|
||||
CBOR_FREE_BYTE_STRING(cred->userName);
|
||||
CBOR_FREE_BYTE_STRING(cred->userDisplayName);
|
||||
CBOR_FREE_BYTE_STRING(cred->id);
|
||||
cred->present = false;
|
||||
cred->extensions.present = false;
|
||||
cred->opts.present = false;
|
||||
if (cred) {
|
||||
CBOR_FREE_BYTE_STRING(cred->rpId);
|
||||
CBOR_FREE_BYTE_STRING(cred->userId);
|
||||
CBOR_FREE_BYTE_STRING(cred->userName);
|
||||
CBOR_FREE_BYTE_STRING(cred->userDisplayName);
|
||||
CBOR_FREE_BYTE_STRING(cred->id);
|
||||
if (cred->extensions.present) {
|
||||
CBOR_FREE_BYTE_STRING(cred->extensions.credBlob);
|
||||
}
|
||||
cred->present = false;
|
||||
cred->extensions.present = false;
|
||||
cred->opts.present = false;
|
||||
}
|
||||
}
|
||||
|
||||
int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash) {
|
||||
int sloti = -1;
|
||||
Credential cred = {0};
|
||||
Credential cred = { 0 };
|
||||
int ret = 0;
|
||||
bool new_record = true;
|
||||
ret = credential_load(cred_id, cred_id_len, rp_id_hash, &cred);
|
||||
@@ -229,17 +302,19 @@ int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *
|
||||
credential_free(&cred);
|
||||
return ret;
|
||||
}
|
||||
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
|
||||
for (uint16_t i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
|
||||
file_t *ef = search_dynamic_file(EF_CRED + i);
|
||||
Credential rcred = {0};
|
||||
Credential rcred = { 0 };
|
||||
if (!file_has_data(ef)) {
|
||||
if (sloti == -1)
|
||||
if (sloti == -1) {
|
||||
sloti = i;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (memcmp(file_get_data(ef), rp_id_hash, 32) != 0)
|
||||
if (memcmp(file_get_data(ef), rp_id_hash, 32) != 0) {
|
||||
continue;
|
||||
ret = credential_load(file_get_data(ef) + 32, file_get_size(ef)-32, rp_id_hash, &rcred);
|
||||
}
|
||||
ret = credential_load(file_get_data(ef) + 32, file_get_size(ef) - 32, rp_id_hash, &rcred);
|
||||
if (ret != 0) {
|
||||
credential_free(&rcred);
|
||||
continue;
|
||||
@@ -252,46 +327,49 @@ int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *
|
||||
}
|
||||
credential_free(&rcred);
|
||||
}
|
||||
if (sloti == -1)
|
||||
if (sloti == -1) {
|
||||
return -1;
|
||||
uint8_t *data = (uint8_t *)calloc(1, cred_id_len+32);
|
||||
}
|
||||
uint8_t *data = (uint8_t *) calloc(1, cred_id_len + 32);
|
||||
memcpy(data, rp_id_hash, 32);
|
||||
memcpy(data + 32, cred_id, cred_id_len);
|
||||
file_t *ef = file_new(EF_CRED+sloti);
|
||||
flash_write_data_to_file(ef, data, cred_id_len + 32);
|
||||
file_t *ef = file_new((uint16_t)(EF_CRED + sloti));
|
||||
file_put_data(ef, data, (uint16_t)cred_id_len + 32);
|
||||
free(data);
|
||||
|
||||
if (new_record == true) { //increase rps
|
||||
sloti = -1;
|
||||
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
|
||||
for (uint16_t i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
|
||||
ef = search_dynamic_file(EF_RP + i);
|
||||
if (!file_has_data(ef)) {
|
||||
if (sloti == -1)
|
||||
if (sloti == -1) {
|
||||
sloti = i;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (memcmp(file_get_data(ef)+1, rp_id_hash, 32) == 0) {
|
||||
if (memcmp(file_get_data(ef) + 1, rp_id_hash, 32) == 0) {
|
||||
sloti = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sloti == -1)
|
||||
if (sloti == -1) {
|
||||
return -1;
|
||||
ef = search_dynamic_file(EF_RP + sloti);
|
||||
}
|
||||
ef = search_dynamic_file((uint16_t)(EF_RP + sloti));
|
||||
if (file_has_data(ef)) {
|
||||
data = (uint8_t *)calloc(1, file_get_size(ef));
|
||||
data = (uint8_t *) calloc(1, file_get_size(ef));
|
||||
memcpy(data, file_get_data(ef), file_get_size(ef));
|
||||
data[0] += 1;
|
||||
flash_write_data_to_file(ef, data, file_get_size(ef));
|
||||
file_put_data(ef, data, file_get_size(ef));
|
||||
free(data);
|
||||
}
|
||||
else {
|
||||
ef = file_new(EF_RP+sloti);
|
||||
data = (uint8_t *)calloc(1, 1 + 32 + cred.rpId.len);
|
||||
ef = file_new((uint16_t)(EF_RP + sloti));
|
||||
data = (uint8_t *) calloc(1, 1 + 32 + cred.rpId.len);
|
||||
data[0] = 1;
|
||||
memcpy(data+1, rp_id_hash, 32);
|
||||
memcpy(data + 1, rp_id_hash, 32);
|
||||
memcpy(data + 1 + 32, cred.rpId.data, cred.rpId.len);
|
||||
flash_write_data_to_file(ef, data, 1 + 32 + cred.rpId.len);
|
||||
file_put_data(ef, data, (uint16_t)(1 + 32 + cred.rpId.len));
|
||||
free(data);
|
||||
}
|
||||
}
|
||||
@@ -303,40 +381,43 @@ int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *
|
||||
int credential_derive_hmac_key(const uint8_t *cred_id, size_t cred_id_len, uint8_t *outk) {
|
||||
memset(outk, 0, 64);
|
||||
int r = 0;
|
||||
if ((r = load_keydev(outk)) != 0)
|
||||
if ((r = load_keydev(outk)) != 0) {
|
||||
return r;
|
||||
}
|
||||
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
|
||||
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *)"SLIP-0022", 9, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *)CRED_PROTO, 4, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *)"hmac-secret", 11, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "SLIP-0022", 9, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) cred_id, CRED_PROTO_LEN, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "hmac-secret", 11, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, cred_id, cred_id_len, outk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int credential_derive_chacha_key(uint8_t *outk) {
|
||||
int credential_derive_chacha_key(uint8_t *outk, const uint8_t *proto) {
|
||||
memset(outk, 0, 32);
|
||||
int r = 0;
|
||||
if ((r = load_keydev(outk)) != 0)
|
||||
if ((r = load_keydev(outk)) != 0) {
|
||||
return r;
|
||||
}
|
||||
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
|
||||
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *)"SLIP-0022", 9, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *)CRED_PROTO, 4, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *)"Encryption key", 14, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "SLIP-0022", 9, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) (proto ? proto : (const uint8_t *)CRED_PROTO), CRED_PROTO_LEN, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "Encryption key", 14, outk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int credential_derive_large_blob_key(const uint8_t *cred_id, size_t cred_id_len, uint8_t *outk) {
|
||||
memset(outk, 0, 32);
|
||||
int r = 0;
|
||||
if ((r = load_keydev(outk)) != 0)
|
||||
if ((r = load_keydev(outk)) != 0) {
|
||||
return r;
|
||||
}
|
||||
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
|
||||
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *)"SLIP-0022", 9, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *)CRED_PROTO, 4, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *)"largeBlobKey", 12, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "SLIP-0022", 9, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) cred_id, CRED_PROTO_LEN, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "largeBlobKey", 12, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, cred_id, cred_id_len, outk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -33,11 +33,11 @@ typedef struct CredExtensions {
|
||||
const bool *minPinLength;
|
||||
CborByteString credBlob;
|
||||
const bool *largeBlobKey;
|
||||
const bool *thirdPartyPayment;
|
||||
bool present;
|
||||
} CredExtensions;
|
||||
|
||||
typedef struct Credential
|
||||
{
|
||||
typedef struct Credential {
|
||||
CborCharString rpId;
|
||||
CborByteString userId;
|
||||
CborCharString userName;
|
||||
@@ -56,14 +56,43 @@ typedef struct Credential
|
||||
#define CRED_PROT_UV_OPTIONAL_WITH_LIST 0x02
|
||||
#define CRED_PROT_UV_REQUIRED 0x03
|
||||
|
||||
#define CRED_PROTO "\xf1\xd0\x02\x01"
|
||||
#define CRED_PROTO_21_S "\xf1\xd0\x02\x01"
|
||||
#define CRED_PROTO_22_S "\xf1\xd0\x02\x02"
|
||||
|
||||
extern int credential_verify(uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash);
|
||||
extern int credential_create(CborCharString *rpId, CborByteString *userId, CborCharString *userName, CborCharString *userDisplayName, CredOptions *opts, CredExtensions *extensions, bool use_sign_count, int alg, int curve, uint8_t *cred_id, size_t *cred_id_len);
|
||||
#define CRED_PROTO CRED_PROTO_22_S
|
||||
|
||||
#define CRED_PROTO_LEN 4
|
||||
#define CRED_IV_LEN 12
|
||||
#define CRED_TAG_LEN 16
|
||||
#define CRED_SILENT_TAG_LEN 16
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CRED_PROTO_21 = 0x01,
|
||||
CRED_PROTO_22 = 0x02,
|
||||
} cred_proto_t;
|
||||
|
||||
extern int credential_verify(uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash, bool silent);
|
||||
extern int credential_create(CborCharString *rpId,
|
||||
CborByteString *userId,
|
||||
CborCharString *userName,
|
||||
CborCharString *userDisplayName,
|
||||
CredOptions *opts,
|
||||
CredExtensions *extensions,
|
||||
bool use_sign_count,
|
||||
int alg,
|
||||
int curve,
|
||||
uint8_t *cred_id,
|
||||
size_t *cred_id_len);
|
||||
extern void credential_free(Credential *cred);
|
||||
extern int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash);
|
||||
extern int credential_load(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash, Credential *cred);
|
||||
extern int credential_load(const uint8_t *cred_id,
|
||||
size_t cred_id_len,
|
||||
const uint8_t *rp_id_hash,
|
||||
Credential *cred);
|
||||
extern int credential_derive_hmac_key(const uint8_t *cred_id, size_t cred_id_len, uint8_t *outk);
|
||||
extern int credential_derive_large_blob_key(const uint8_t *cred_id, size_t cred_id_len, uint8_t *outk);
|
||||
extern int credential_derive_large_blob_key(const uint8_t *cred_id,
|
||||
size_t cred_id_len,
|
||||
uint8_t *outk);
|
||||
|
||||
#endif // _CREDENTIAL_H_
|
||||
|
||||
@@ -18,14 +18,9 @@
|
||||
#ifndef _CTAP_H_
|
||||
#define _CTAP_H_
|
||||
|
||||
#ifdef _MSC_VER // Windows
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned long int uint64_t;
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -119,6 +114,10 @@ typedef struct {
|
||||
|
||||
#define CTAP_CONFIG_AUT_ENABLE 0x03e43f56b34285e2
|
||||
#define CTAP_CONFIG_AUT_DISABLE 0x1831a40f04a25ed9
|
||||
#define CTAP_CONFIG_PHY_VIDPID 0x6fcb19b0cbe3acfa
|
||||
#define CTAP_CONFIG_PHY_LED_GPIO 0x7b392a394de9f948
|
||||
#define CTAP_CONFIG_PHY_LED_BTNESS 0x76a85945985d02fd
|
||||
#define CTAP_CONFIG_PHY_OPTS 0x969f3b09eceb805f
|
||||
|
||||
#define CTAP_VENDOR_CBOR (CTAPHID_VENDOR_FIRST + 1)
|
||||
|
||||
@@ -126,6 +125,8 @@ typedef struct {
|
||||
#define CTAP_VENDOR_MSE 0x02
|
||||
#define CTAP_VENDOR_UNLOCK 0x03
|
||||
#define CTAP_VENDOR_EA 0x04
|
||||
#define CTAP_VENDOR_PHY_OPTS 0x05
|
||||
#define CTAP_VENDOR_MEMORY 0x06
|
||||
|
||||
#define CTAP_PERMISSION_MC 0x01 // MakeCredential
|
||||
#define CTAP_PERMISSION_GA 0x02 // GetAssertion
|
||||
|
||||
@@ -18,10 +18,14 @@
|
||||
#ifndef _CTAP2_CBOR_H_
|
||||
#define _CTAP2_CBOR_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include <stdio.h>
|
||||
#include "cbor.h"
|
||||
#ifndef ESP_PLATFORM
|
||||
#include "common.h"
|
||||
#else
|
||||
#define MBEDTLS_ALLOW_PRIVATE_ACCESS
|
||||
#endif
|
||||
#include "mbedtls/ecp.h"
|
||||
#include "mbedtls/ecdh.h"
|
||||
|
||||
extern uint8_t *driver_prepare_response();
|
||||
extern void driver_exec_finished(size_t size_next);
|
||||
@@ -49,17 +53,17 @@ extern const bool _btrue, _bfalse;
|
||||
if (x) \
|
||||
{ \
|
||||
free(x); \
|
||||
x = NULL;\
|
||||
x = NULL; \
|
||||
} \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define CBOR_ERROR(e) \
|
||||
do \
|
||||
{ \
|
||||
error = e; \
|
||||
printf("Cbor ERROR [%s:%d]: %d\n", __FILE__, __LINE__, e); \
|
||||
printf("Cbor ERROR [%s:%d]: %x\n", __FILE__, __LINE__, e); \
|
||||
goto err; \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define CBOR_ASSERT(c) \
|
||||
do \
|
||||
@@ -70,7 +74,7 @@ extern const bool _btrue, _bfalse;
|
||||
printf("Cbor ASSERT [%s:%d]: %s\n", __FILE__, __LINE__, #c); \
|
||||
goto err; \
|
||||
} \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define PINUVAUTHTOKEN_MC 0x1
|
||||
#define PINUVAUTHTOKEN_GA 0x2
|
||||
@@ -97,20 +101,20 @@ typedef struct CborCharString {
|
||||
do \
|
||||
{ \
|
||||
if ((v).nofree != true) \
|
||||
CBOR_FREE((v).data); \
|
||||
CBOR_FREE((v).data); \
|
||||
else \
|
||||
(v).data = NULL; \
|
||||
(v).data = NULL; \
|
||||
(v).len = 0; \
|
||||
(v).present = false; \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define CBOR_PARSE_MAP_START(_p,_n) \
|
||||
#define CBOR_PARSE_MAP_START(_p, _n) \
|
||||
CBOR_ASSERT(cbor_value_is_map(&(_p)) == true); \
|
||||
CborValue _f##_n; \
|
||||
CBOR_CHECK(cbor_value_enter_container(&(_p), &(_f##_n))); \
|
||||
while (cbor_value_at_end(&(_f##_n)) == false)
|
||||
|
||||
#define CBOR_PARSE_ARRAY_START(_p,_n) \
|
||||
#define CBOR_PARSE_ARRAY_START(_p, _n) \
|
||||
CBOR_ASSERT(cbor_value_is_array(&(_p)) == true); \
|
||||
CborValue _f##_n; \
|
||||
CBOR_CHECK(cbor_value_enter_container(&(_p), &(_f##_n))); \
|
||||
@@ -121,14 +125,14 @@ typedef struct CborCharString {
|
||||
CBOR_ASSERT(cbor_value_is_unsigned_integer(&(_f##_n)) == true); \
|
||||
CBOR_CHECK(cbor_value_get_uint64(&(_f##_n), &(v))); \
|
||||
CBOR_CHECK(cbor_value_advance_fixed(&(_f##_n))); \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define CBOR_FIELD_GET_INT(v, _n) \
|
||||
do { \
|
||||
CBOR_ASSERT(cbor_value_is_integer(&(_f##_n)) == true); \
|
||||
CBOR_CHECK(cbor_value_get_int64(&(_f##_n), &(v))); \
|
||||
CBOR_CHECK(cbor_value_advance_fixed(&(_f##_n))); \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define CBOR_FIELD_GET_BYTES(v, _n) \
|
||||
do { \
|
||||
@@ -151,11 +155,11 @@ typedef struct CborCharString {
|
||||
CBOR_CHECK(cbor_value_get_boolean(&(_f##_n), &val)); \
|
||||
v = (val == true ? ptrue : pfalse); \
|
||||
CBOR_CHECK(cbor_value_advance_fixed(&(_f##_n))); \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define CBOR_FIELD_GET_KEY_TEXT(_n) \
|
||||
CBOR_ASSERT(cbor_value_is_text_string(&(_f##_n)) == true); \
|
||||
char _fd##_n[64]; \
|
||||
char _fd##_n[64] = {0}; \
|
||||
size_t _fdl##_n = sizeof(_fd##_n); \
|
||||
CBOR_CHECK(cbor_value_copy_text_string(&(_f##_n), _fd##_n, &_fdl##_n, &(_f##_n)))
|
||||
|
||||
@@ -177,26 +181,26 @@ typedef struct CborCharString {
|
||||
|
||||
#define CBOR_FIELD_KEY_TEXT_VAL_INT(_n, _t, _v) \
|
||||
if (strcmp(_fd##_n, _t) == 0) { \
|
||||
CBOR_FIELD_GET_INT(_v, _n);\
|
||||
CBOR_FIELD_GET_INT(_v, _n); \
|
||||
continue; \
|
||||
}
|
||||
|
||||
#define CBOR_FIELD_KEY_TEXT_VAL_UINT(_n, _t, _v) \
|
||||
if (strcmp(_fd##_n, _t) == 0) { \
|
||||
CBOR_FIELD_GET_UINT(_v, _n);\
|
||||
CBOR_FIELD_GET_UINT(_v, _n); \
|
||||
continue; \
|
||||
}
|
||||
|
||||
#define CBOR_FIELD_KEY_TEXT_VAL_BOOL(_n, _t, _v) \
|
||||
if (strcmp(_fd##_n, _t) == 0) { \
|
||||
CBOR_FIELD_GET_BOOL(_v, _n);\
|
||||
CBOR_FIELD_GET_BOOL(_v, _n); \
|
||||
continue; \
|
||||
}
|
||||
|
||||
#define CBOR_PARSE_MAP_END(_p,_n) \
|
||||
#define CBOR_PARSE_MAP_END(_p, _n) \
|
||||
CBOR_CHECK(cbor_value_leave_container(&(_p), &(_f##_n)))
|
||||
|
||||
#define CBOR_PARSE_ARRAY_END(_p,_n) CBOR_PARSE_MAP_END(_p, _n)
|
||||
#define CBOR_PARSE_ARRAY_END(_p, _n) CBOR_PARSE_MAP_END(_p, _n)
|
||||
|
||||
#define CBOR_ADVANCE(_n) CBOR_CHECK(cbor_value_advance(&_f##_n));
|
||||
|
||||
@@ -205,39 +209,51 @@ typedef struct CborCharString {
|
||||
if ((v).data && (v).len > 0) { \
|
||||
CBOR_CHECK(cbor_encode_uint(&(p), (k))); \
|
||||
CBOR_CHECK(cbor_encode_byte_string(&(p), (v).data, (v).len)); \
|
||||
} } while(0)
|
||||
} } while (0)
|
||||
|
||||
#define CBOR_APPEND_KEY_UINT_VAL_STRING(p, k, v) \
|
||||
do { \
|
||||
if ((v).data && (v).len > 0) { \
|
||||
CBOR_CHECK(cbor_encode_uint(&(p), (k))); \
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&(p), (v).data)); \
|
||||
} } while(0)
|
||||
} } while (0)
|
||||
|
||||
|
||||
#define CBOR_APPEND_KEY_UINT_VAL_UINT(p, k, v) \
|
||||
do { \
|
||||
CBOR_CHECK(cbor_encode_uint(&(p), (k))); \
|
||||
CBOR_CHECK(cbor_encode_uint(&(p), (v))); \
|
||||
} while(0)
|
||||
CBOR_CHECK(cbor_encode_uint(&(p), (k))); \
|
||||
CBOR_CHECK(cbor_encode_uint(&(p), (v))); \
|
||||
} while (0)
|
||||
|
||||
#define CBOR_APPEND_KEY_UINT_VAL_INT(p, k, v) \
|
||||
do { \
|
||||
CBOR_CHECK(cbor_encode_int(&(p), (k))); \
|
||||
CBOR_CHECK(cbor_encode_int(&(p), (v))); \
|
||||
} while(0)
|
||||
CBOR_CHECK(cbor_encode_int(&(p), (k))); \
|
||||
CBOR_CHECK(cbor_encode_int(&(p), (v))); \
|
||||
} while (0)
|
||||
|
||||
#define CBOR_APPEND_KEY_UINT_VAL_BOOL(p, k, v) \
|
||||
do { \
|
||||
CBOR_CHECK(cbor_encode_uint(&(p), (k))); \
|
||||
CBOR_CHECK(cbor_encode_boolean(&(p), (v))); \
|
||||
} while(0)
|
||||
CBOR_CHECK(cbor_encode_uint(&(p), (k))); \
|
||||
CBOR_CHECK(cbor_encode_boolean(&(p), (v))); \
|
||||
} while (0)
|
||||
|
||||
#define CBOR_APPEND_KEY_UINT_VAL_PBOOL(p, k, v) \
|
||||
do { \
|
||||
if (v != NULL) {\
|
||||
if (v != NULL) { \
|
||||
CBOR_CHECK(cbor_encode_uint(&(p), (k))); \
|
||||
CBOR_CHECK(cbor_encode_boolean(&(p), v == ptrue ? true : false)); \
|
||||
} } while(0)
|
||||
} } while (0)
|
||||
|
||||
extern CborError COSE_key(mbedtls_ecp_keypair *, CborEncoder *, CborEncoder *);
|
||||
extern CborError COSE_key_shared(mbedtls_ecdh_context *key,
|
||||
CborEncoder *mapEncoderParent,
|
||||
CborEncoder *mapEncoder);
|
||||
extern CborError COSE_public_key(int alg, CborEncoder *mapEncoderParent, CborEncoder *mapEncoder);
|
||||
extern CborError COSE_read_key(CborValue *f,
|
||||
int64_t *kty,
|
||||
int64_t *alg,
|
||||
int64_t *crv,
|
||||
CborByteString *kax,
|
||||
CborByteString *kay);
|
||||
|
||||
#endif //_CTAP2_CBOR_H_
|
||||
|
||||
363
src/fido/fido.c
363
src/fido/fido.c
@@ -15,95 +15,167 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "fido.h"
|
||||
#include "hsm.h"
|
||||
#include "kek.h"
|
||||
#include "pico_keys.h"
|
||||
#include "apdu.h"
|
||||
#include "ctap.h"
|
||||
#include "files.h"
|
||||
#include "file.h"
|
||||
#include "usb.h"
|
||||
#include "random.h"
|
||||
#include "bsp/board.h"
|
||||
#include "mbedtls/ecdsa.h"
|
||||
#include "mbedtls/x509_crt.h"
|
||||
#include "mbedtls/hkdf.h"
|
||||
#include "pk_wrap.h"
|
||||
#include "crypto_utils.h"
|
||||
#if defined(USB_ITF_CCID) || defined(ENABLE_EMULATION)
|
||||
#include "ccid/ccid.h"
|
||||
#endif
|
||||
#if !defined(ENABLE_EMULATION) && !defined(ESP_PLATFORM)
|
||||
#include "bsp/board.h"
|
||||
#endif
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include "management.h"
|
||||
#include "hid/ctap_hid.h"
|
||||
#include "version.h"
|
||||
#include "crypto_utils.h"
|
||||
#include "otp.h"
|
||||
|
||||
void init_fido(bool);
|
||||
int fido_process_apdu();
|
||||
int fido_unload();
|
||||
|
||||
pinUvAuthToken_t paut = {0};
|
||||
uint8_t PICO_PRODUCT = 2; // Pico FIDO
|
||||
|
||||
pinUvAuthToken_t paut = { 0 };
|
||||
|
||||
uint8_t keydev_dec[32];
|
||||
bool has_keydev_dec = false;
|
||||
uint8_t session_pin[32] = { 0 };
|
||||
|
||||
const uint8_t fido_aid[] = {
|
||||
8,
|
||||
0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01
|
||||
};
|
||||
|
||||
app_t *fido_select(app_t *a) {
|
||||
a->aid = fido_aid;
|
||||
a->process_apdu = fido_process_apdu;
|
||||
a->unload = fido_unload;
|
||||
current_app = a;
|
||||
//init_fido(false);
|
||||
return a;
|
||||
const uint8_t atr_fido[] = {
|
||||
23,
|
||||
0x3b, 0xfd, 0x13, 0x00, 0x00, 0x81, 0x31, 0xfe, 0x15, 0x80, 0x73, 0xc0, 0x21, 0xc0, 0x57, 0x59,
|
||||
0x75, 0x62, 0x69, 0x4b, 0x65, 0x79, 0x40
|
||||
};
|
||||
|
||||
uint8_t fido_get_version_major() {
|
||||
return PICO_FIDO_VERSION_MAJOR;
|
||||
}
|
||||
uint8_t fido_get_version_minor() {
|
||||
return PICO_FIDO_VERSION_MINOR;
|
||||
}
|
||||
|
||||
void __attribute__ ((constructor)) fido_ctor() {
|
||||
register_app(fido_select);
|
||||
//fido_select(&apps[0]);
|
||||
int fido_select(app_t *a, uint8_t force) {
|
||||
(void) force;
|
||||
if (cap_supported(CAP_FIDO2)) {
|
||||
a->process_apdu = fido_process_apdu;
|
||||
a->unload = fido_unload;
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
return PICOKEY_ERR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
extern uint8_t (*get_version_major)();
|
||||
extern uint8_t (*get_version_minor)();
|
||||
|
||||
INITIALIZER ( fido_ctor ) {
|
||||
#if defined(USB_ITF_CCID) || defined(ENABLE_EMULATION)
|
||||
ccid_atr = atr_fido;
|
||||
#endif
|
||||
get_version_major = fido_get_version_major;
|
||||
get_version_minor = fido_get_version_minor;
|
||||
register_app(fido_select, fido_aid);
|
||||
}
|
||||
|
||||
int fido_unload() {
|
||||
return CCID_OK;
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
mbedtls_ecp_group_id fido_curve_to_mbedtls(int curve) {
|
||||
if (curve == FIDO2_CURVE_P256)
|
||||
if (curve == FIDO2_CURVE_P256) {
|
||||
return MBEDTLS_ECP_DP_SECP256R1;
|
||||
else if (curve == FIDO2_CURVE_P384)
|
||||
}
|
||||
else if (curve == FIDO2_CURVE_P384) {
|
||||
return MBEDTLS_ECP_DP_SECP384R1;
|
||||
else if (curve == FIDO2_CURVE_P521)
|
||||
}
|
||||
else if (curve == FIDO2_CURVE_P521) {
|
||||
return MBEDTLS_ECP_DP_SECP521R1;
|
||||
else if (curve == FIDO2_CURVE_P256K1)
|
||||
}
|
||||
else if (curve == FIDO2_CURVE_P256K1) {
|
||||
return MBEDTLS_ECP_DP_SECP256K1;
|
||||
else if (curve == FIDO2_CURVE_X25519)
|
||||
}
|
||||
else if (curve == FIDO2_CURVE_X25519) {
|
||||
return MBEDTLS_ECP_DP_CURVE25519;
|
||||
else if (curve == FIDO2_CURVE_X448)
|
||||
}
|
||||
else if (curve == FIDO2_CURVE_X448) {
|
||||
return MBEDTLS_ECP_DP_CURVE448;
|
||||
}
|
||||
#ifdef MBEDTLS_EDDSA_C
|
||||
else if (curve == FIDO2_CURVE_ED25519) {
|
||||
return MBEDTLS_ECP_DP_ED25519;
|
||||
}
|
||||
else if (curve == FIDO2_CURVE_ED448) {
|
||||
return MBEDTLS_ECP_DP_ED448;
|
||||
}
|
||||
#endif
|
||||
return MBEDTLS_ECP_DP_NONE;
|
||||
}
|
||||
int mbedtls_curve_to_fido(mbedtls_ecp_group_id id) {
|
||||
if (id == MBEDTLS_ECP_DP_SECP256R1) {
|
||||
return FIDO2_CURVE_P256;
|
||||
}
|
||||
else if (id == MBEDTLS_ECP_DP_SECP384R1) {
|
||||
return FIDO2_CURVE_P384;
|
||||
}
|
||||
else if (id == MBEDTLS_ECP_DP_SECP521R1) {
|
||||
return FIDO2_CURVE_P521;
|
||||
}
|
||||
else if (id == MBEDTLS_ECP_DP_SECP256K1) {
|
||||
return FIDO2_CURVE_P256K1;
|
||||
}
|
||||
else if (id == MBEDTLS_ECP_DP_CURVE25519) {
|
||||
return MBEDTLS_ECP_DP_CURVE25519;
|
||||
}
|
||||
else if (id == MBEDTLS_ECP_DP_CURVE448) {
|
||||
return FIDO2_CURVE_X448;
|
||||
}
|
||||
#ifdef MBEDTLS_EDDSA_C
|
||||
else if (id == MBEDTLS_ECP_DP_ED25519) {
|
||||
return FIDO2_CURVE_ED25519;
|
||||
}
|
||||
else if (id == MBEDTLS_ECP_DP_ED448) {
|
||||
return FIDO2_CURVE_ED448;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fido_load_key(int curve, const uint8_t *cred_id, mbedtls_ecdsa_context *key) {
|
||||
int fido_load_key(int curve, const uint8_t *cred_id, mbedtls_ecp_keypair *key) {
|
||||
mbedtls_ecp_group_id mbedtls_curve = fido_curve_to_mbedtls(curve);
|
||||
if (mbedtls_curve == MBEDTLS_ECP_DP_NONE)
|
||||
if (mbedtls_curve == MBEDTLS_ECP_DP_NONE) {
|
||||
return CTAP2_ERR_UNSUPPORTED_ALGORITHM;
|
||||
}
|
||||
uint8_t key_path[KEY_PATH_LEN];
|
||||
memcpy(key_path, cred_id, KEY_PATH_LEN);
|
||||
*(uint32_t *)key_path = 0x80000000 | 10022;
|
||||
for (int i = 1; i < KEY_PATH_ENTRIES; i++)
|
||||
*(uint32_t *)(key_path+i*sizeof(uint32_t)) |= 0x80000000;
|
||||
*(uint32_t *) key_path = 0x80000000 | 10022;
|
||||
for (int i = 1; i < KEY_PATH_ENTRIES; i++) {
|
||||
*(uint32_t *) (key_path + i * sizeof(uint32_t)) |= 0x80000000;
|
||||
}
|
||||
return derive_key(NULL, false, key_path, mbedtls_curve, key);
|
||||
}
|
||||
|
||||
int x509_create_cert(mbedtls_ecdsa_context *ecdsa, uint8_t *buffer, size_t buffer_size, bool core1) {
|
||||
int x509_create_cert(mbedtls_ecdsa_context *ecdsa, uint8_t *buffer, size_t buffer_size) {
|
||||
mbedtls_x509write_cert ctx;
|
||||
mbedtls_x509write_crt_init(&ctx);
|
||||
mbedtls_x509write_crt_set_version(&ctx, MBEDTLS_X509_CRT_VERSION_3);
|
||||
mbedtls_x509write_crt_set_validity(&ctx, "20220901000000", "20320831235959" );
|
||||
mbedtls_x509write_crt_set_validity(&ctx, "20220901000000", "20720831235959");
|
||||
mbedtls_x509write_crt_set_issuer_name(&ctx, "C=ES,O=Pico HSM,CN=Pico FIDO");
|
||||
mbedtls_x509write_crt_set_subject_name(&ctx, "C=ES,O=Pico HSM,CN=Pico FIDO");
|
||||
mbedtls_mpi serial;
|
||||
mbedtls_mpi_init(&serial);
|
||||
mbedtls_mpi_fill_random(&serial, 32, core1 ? random_gen : random_gen_core0, NULL);
|
||||
mbedtls_x509write_crt_set_serial(&ctx, &serial);
|
||||
uint8_t serial[16];
|
||||
random_gen(NULL, serial, sizeof(serial));
|
||||
mbedtls_x509write_crt_set_serial_raw(&ctx, serial, sizeof(serial));
|
||||
mbedtls_pk_context key;
|
||||
mbedtls_pk_init(&key);
|
||||
mbedtls_pk_setup(&key, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY));
|
||||
@@ -114,27 +186,41 @@ int x509_create_cert(mbedtls_ecdsa_context *ecdsa, uint8_t *buffer, size_t buffe
|
||||
mbedtls_x509write_crt_set_basic_constraints(&ctx, 0, 0);
|
||||
mbedtls_x509write_crt_set_subject_key_identifier(&ctx);
|
||||
mbedtls_x509write_crt_set_authority_key_identifier(&ctx);
|
||||
mbedtls_x509write_crt_set_key_usage(&ctx, MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_KEY_CERT_SIGN);
|
||||
int ret = mbedtls_x509write_crt_der(&ctx, buffer, buffer_size, core1 ? random_gen : random_gen_core0, NULL);
|
||||
mbedtls_x509write_crt_set_key_usage(&ctx,
|
||||
MBEDTLS_X509_KU_DIGITAL_SIGNATURE |
|
||||
MBEDTLS_X509_KU_KEY_CERT_SIGN);
|
||||
int ret = mbedtls_x509write_crt_der(&ctx, buffer, buffer_size, random_gen, NULL);
|
||||
mbedtls_x509write_crt_free(&ctx);
|
||||
/* pk cannot be freed, as it is freed later */
|
||||
//mbedtls_pk_free(&key);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int load_keydev(uint8_t *key) {
|
||||
if (has_keydev_dec == false && !file_has_data(ef_keydev))
|
||||
return CCID_ERR_MEMORY_FATAL;
|
||||
if (has_keydev_dec == true)
|
||||
if (has_keydev_dec == false && !file_has_data(ef_keydev)) {
|
||||
return PICOKEY_ERR_MEMORY_FATAL;
|
||||
}
|
||||
|
||||
if (has_keydev_dec == true) {
|
||||
memcpy(key, keydev_dec, sizeof(keydev_dec));
|
||||
else
|
||||
}
|
||||
else {
|
||||
memcpy(key, file_get_data(ef_keydev), file_get_size(ef_keydev));
|
||||
//return mkek_decrypt(key, file_get_size(ef_keydev));
|
||||
return CCID_OK;
|
||||
|
||||
if (mkek_decrypt(key, 32) != PICOKEY_OK) {
|
||||
return PICOKEY_EXEC_ERROR;
|
||||
}
|
||||
if (otp_key_1 && aes_decrypt(otp_key_1, NULL, 32 * 8, PICO_KEYS_AES_MODE_CBC, key, 32) != PICOKEY_OK) {
|
||||
return PICOKEY_EXEC_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
int verify_key(const uint8_t *appId, const uint8_t *keyHandle, mbedtls_ecdsa_context *key) {
|
||||
int verify_key(const uint8_t *appId, const uint8_t *keyHandle, mbedtls_ecp_keypair *key) {
|
||||
for (int i = 0; i < KEY_PATH_ENTRIES; i++) {
|
||||
uint32_t k = *(uint32_t *)&keyHandle[i*sizeof(uint32_t)];
|
||||
uint32_t k = *(uint32_t *) &keyHandle[i * sizeof(uint32_t)];
|
||||
if (!(k & 0x80000000)) {
|
||||
return -1;
|
||||
}
|
||||
@@ -143,17 +229,20 @@ int verify_key(const uint8_t *appId, const uint8_t *keyHandle, mbedtls_ecdsa_con
|
||||
if (key == NULL) {
|
||||
mbedtls_ecdsa_init(&ctx);
|
||||
key = &ctx;
|
||||
if (derive_key(appId, false, (uint8_t *)keyHandle, MBEDTLS_ECP_DP_SECP256R1, &ctx) != 0) {
|
||||
if (derive_key(appId, false, (uint8_t *) keyHandle, MBEDTLS_ECP_DP_SECP256R1, &ctx) != 0) {
|
||||
mbedtls_ecdsa_free(&ctx);
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
uint8_t hmac[32], d[32];
|
||||
int ret = mbedtls_ecp_write_key(key, d, sizeof(d));
|
||||
if (key == NULL)
|
||||
size_t olen = 0;
|
||||
int ret = mbedtls_ecp_write_key_ext(key, &olen, d, sizeof(d));
|
||||
if (key == &ctx) {
|
||||
mbedtls_ecdsa_free(&ctx);
|
||||
if (ret != 0)
|
||||
}
|
||||
if (ret != 0) {
|
||||
return -2;
|
||||
}
|
||||
uint8_t key_base[CTAP_APPID_SIZE + KEY_PATH_LEN];
|
||||
memcpy(key_base, appId, CTAP_APPID_SIZE);
|
||||
memcpy(key_base + CTAP_APPID_SIZE, keyHandle, KEY_PATH_LEN);
|
||||
@@ -162,19 +251,21 @@ int verify_key(const uint8_t *appId, const uint8_t *keyHandle, mbedtls_ecdsa_con
|
||||
return memcmp(keyHandle + KEY_PATH_LEN, hmac, sizeof(hmac));
|
||||
}
|
||||
|
||||
int derive_key(const uint8_t *app_id, bool new_key, uint8_t *key_handle, int curve, mbedtls_ecdsa_context *key) {
|
||||
uint8_t outk[64] = {0};
|
||||
int derive_key(const uint8_t *app_id, bool new_key, uint8_t *key_handle, int curve, mbedtls_ecp_keypair *key) {
|
||||
uint8_t outk[67] = { 0 }; //SECP521R1 key is 66 bytes length
|
||||
int r = 0;
|
||||
memset(outk, 0, sizeof(outk));
|
||||
if ((r = load_keydev(outk)) != CCID_OK)
|
||||
if ((r = load_keydev(outk)) != PICOKEY_OK) {
|
||||
printf("Error loading keydev: %d\n", r);
|
||||
return r;
|
||||
}
|
||||
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
|
||||
for (int i = 0; i < KEY_PATH_ENTRIES; i++) {
|
||||
if (new_key == true) {
|
||||
uint32_t val = 0;
|
||||
random_gen(NULL, (uint8_t *) &val, sizeof(val));
|
||||
val |= 0x80000000;
|
||||
memcpy(&key_handle[i*sizeof(uint32_t)], &val, sizeof(uint32_t));
|
||||
memcpy(&key_handle[i * sizeof(uint32_t)], &val, sizeof(uint32_t));
|
||||
}
|
||||
r = mbedtls_hkdf(md_info, &key_handle[i * sizeof(uint32_t)], sizeof(uint32_t), outk, 32, outk + 32, 32, outk, sizeof(outk));
|
||||
if (r != 0) {
|
||||
@@ -186,8 +277,7 @@ int derive_key(const uint8_t *app_id, bool new_key, uint8_t *key_handle, int cur
|
||||
uint8_t key_base[CTAP_APPID_SIZE + KEY_PATH_LEN];
|
||||
memcpy(key_base, app_id, CTAP_APPID_SIZE);
|
||||
memcpy(key_base + CTAP_APPID_SIZE, key_handle, KEY_PATH_LEN);
|
||||
if ((r = mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), outk, 32, key_base, sizeof(key_base), key_handle + 32)) != 0)
|
||||
{
|
||||
if ((r = mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), outk, 32, key_base, sizeof(key_base), key_handle + 32)) != 0) {
|
||||
mbedtls_platform_zeroize(outk, sizeof(outk));
|
||||
return r;
|
||||
}
|
||||
@@ -195,39 +285,56 @@ int derive_key(const uint8_t *app_id, bool new_key, uint8_t *key_handle, int cur
|
||||
if (key != NULL) {
|
||||
mbedtls_ecp_group_load(&key->grp, curve);
|
||||
const mbedtls_ecp_curve_info *cinfo = mbedtls_ecp_curve_info_from_grp_id(curve);
|
||||
if (cinfo == NULL)
|
||||
if (cinfo == NULL) {
|
||||
return 1;
|
||||
r = mbedtls_ecp_read_key(curve, key, outk, ceil((float)cinfo->bit_size/8));
|
||||
}
|
||||
if (cinfo->bit_size % 8 != 0) {
|
||||
outk[0] >>= 8 - (cinfo->bit_size % 8);
|
||||
}
|
||||
r = mbedtls_ecp_read_key(curve, key, outk, (size_t)ceil((float) cinfo->bit_size / 8));
|
||||
mbedtls_platform_zeroize(outk, sizeof(outk));
|
||||
if (r != 0)
|
||||
if (r != 0) {
|
||||
return r;
|
||||
}
|
||||
#ifdef MBEDTLS_EDDSA_C
|
||||
if (curve == MBEDTLS_ECP_DP_ED25519) {
|
||||
return mbedtls_ecp_point_edwards(&key->grp, &key->Q, &key->d, random_gen, NULL);
|
||||
}
|
||||
#endif
|
||||
return mbedtls_ecp_mul(&key->grp, &key->Q, &key->d, &key->grp.G, random_gen, NULL);
|
||||
}
|
||||
mbedtls_platform_zeroize(outk, sizeof(outk));
|
||||
return r;
|
||||
}
|
||||
|
||||
int scan_files(bool core1) {
|
||||
int scan_files() {
|
||||
ef_keydev = search_by_fid(EF_KEY_DEV, NULL, SPECIFY_EF);
|
||||
ef_keydev_enc = search_by_fid(EF_KEY_DEV_ENC, NULL, SPECIFY_EF);
|
||||
ef_mkek = search_by_fid(EF_MKEK, NULL, SPECIFY_EF);
|
||||
if (ef_keydev) {
|
||||
if (!file_has_data(ef_keydev) && !file_has_data(ef_keydev_enc)) {
|
||||
printf("KEY DEVICE is empty. Generating SECP256R1 curve...");
|
||||
mbedtls_ecdsa_context ecdsa;
|
||||
mbedtls_ecdsa_init(&ecdsa);
|
||||
uint8_t index = 0;
|
||||
int ret = mbedtls_ecdsa_genkey(&ecdsa, MBEDTLS_ECP_DP_SECP256R1, core1 ? random_gen : random_gen_core0, &index);
|
||||
int ret = mbedtls_ecdsa_genkey(&ecdsa, MBEDTLS_ECP_DP_SECP256R1, random_gen, &index);
|
||||
if (ret != 0) {
|
||||
mbedtls_ecdsa_free(&ecdsa);
|
||||
return ret;
|
||||
}
|
||||
uint8_t kdata[32];
|
||||
int key_size = mbedtls_mpi_size(&ecdsa.d);
|
||||
mbedtls_mpi_write_binary(&ecdsa.d, kdata, key_size);
|
||||
ret = flash_write_data_to_file(ef_keydev, kdata, key_size);
|
||||
uint8_t kdata[64];
|
||||
size_t key_size = 0;
|
||||
ret = mbedtls_ecp_write_key_ext(&ecdsa, &key_size, kdata, sizeof(kdata));
|
||||
if (ret != PICOKEY_OK) {
|
||||
return ret;
|
||||
}
|
||||
if (otp_key_1) {
|
||||
ret = aes_encrypt(otp_key_1, NULL, 32 * 8, PICO_KEYS_AES_MODE_CBC, kdata, 32);
|
||||
}
|
||||
ret = file_put_data(ef_keydev, kdata, (uint16_t)key_size);
|
||||
mbedtls_platform_zeroize(kdata, sizeof(kdata));
|
||||
mbedtls_ecdsa_free(&ecdsa);
|
||||
if (ret != CCID_OK) {
|
||||
if (ret != PICOKEY_OK) {
|
||||
return ret;
|
||||
}
|
||||
printf(" done!\n");
|
||||
@@ -236,27 +343,48 @@ int scan_files(bool core1) {
|
||||
else {
|
||||
printf("FATAL ERROR: KEY DEV not found in memory!\r\n");
|
||||
}
|
||||
if (ef_mkek) { // No encrypted MKEK
|
||||
if (!file_has_data(ef_mkek)) {
|
||||
uint8_t mkek[MKEK_IV_SIZE + MKEK_KEY_SIZE];
|
||||
random_gen(NULL, mkek, sizeof(mkek));
|
||||
file_put_data(ef_mkek, mkek, sizeof(mkek));
|
||||
int ret = aes_encrypt_cfb_256(MKEK_KEY(mkek), MKEK_IV(mkek), file_get_data(ef_keydev), 32);
|
||||
mbedtls_platform_zeroize(mkek, sizeof(mkek));
|
||||
if (ret != 0) {
|
||||
printf("FATAL ERROR: MKEK encryption failed!\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("FATAL ERROR: MKEK not found in memory!\r\n");
|
||||
}
|
||||
ef_certdev = search_by_fid(EF_EE_DEV, NULL, SPECIFY_EF);
|
||||
if (ef_certdev) {
|
||||
if (!file_has_data(ef_certdev)) {
|
||||
uint8_t cert[4096];
|
||||
uint8_t cert[2048], outk[32];
|
||||
memset(outk, 0, sizeof(outk));
|
||||
int ret = 0;
|
||||
if ((ret = load_keydev(outk)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
mbedtls_ecdsa_context key;
|
||||
mbedtls_ecdsa_init(&key);
|
||||
int ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &key, file_get_data(ef_keydev), file_get_size(ef_keydev));
|
||||
ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &key, outk, sizeof(outk));
|
||||
if (ret != 0) {
|
||||
mbedtls_ecdsa_free(&key);
|
||||
return ret;
|
||||
}
|
||||
ret = mbedtls_ecp_mul(&key.grp, &key.Q, &key.d, &key.grp.G, core1 ? random_gen : random_gen_core0, NULL);
|
||||
ret = mbedtls_ecp_mul(&key.grp, &key.Q, &key.d, &key.grp.G, random_gen, NULL);
|
||||
if (ret != 0) {
|
||||
mbedtls_ecdsa_free(&key);
|
||||
return ret;
|
||||
}
|
||||
ret = x509_create_cert(&key, cert, sizeof(cert), core1);
|
||||
ret = x509_create_cert(&key, cert, sizeof(cert));
|
||||
mbedtls_ecdsa_free(&key);
|
||||
if (ret <= 0)
|
||||
if (ret <= 0) {
|
||||
return ret;
|
||||
flash_write_data_to_file(ef_certdev, cert + sizeof(cert) - ret, ret);
|
||||
}
|
||||
file_put_data(ef_certdev, cert + sizeof(cert) - ret, (uint16_t)ret);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -266,22 +394,26 @@ int scan_files(bool core1) {
|
||||
if (ef_counter) {
|
||||
if (!file_has_data(ef_counter)) {
|
||||
uint32_t v = 0;
|
||||
flash_write_data_to_file(ef_counter, (uint8_t *)&v, sizeof(v));
|
||||
file_put_data(ef_counter, (uint8_t *) &v, sizeof(v));
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("FATAL ERROR: Global counter not found in memory!\r\n");
|
||||
}
|
||||
ef_pin = search_by_fid(EF_PIN, NULL, SPECIFY_EF);
|
||||
if (file_get_size(ef_pin) == 18) { // Upgrade PIN storage
|
||||
uint8_t pin_data[34] = { 0 }, dhash[32];
|
||||
memcpy(pin_data, file_get_data(ef_pin), 18);
|
||||
double_hash_pin(pin_data + 2, 16, dhash);
|
||||
memcpy(pin_data + 2, dhash, 32);
|
||||
file_put_data(ef_pin, pin_data, 34);
|
||||
}
|
||||
ef_authtoken = search_by_fid(EF_AUTHTOKEN, NULL, SPECIFY_EF);
|
||||
if (ef_authtoken) {
|
||||
if (!file_has_data(ef_authtoken)) {
|
||||
uint8_t t[32];
|
||||
if (core1)
|
||||
random_gen(NULL, t, sizeof(t));
|
||||
else
|
||||
random_gen_core0(NULL, t, sizeof(t));
|
||||
flash_write_data_to_file(ef_authtoken, t, sizeof(t));
|
||||
random_gen(NULL, t, sizeof(t));
|
||||
file_put_data(ef_authtoken, t, sizeof(t));
|
||||
}
|
||||
paut.data = file_get_data(ef_authtoken);
|
||||
paut.len = file_get_size(ef_authtoken);
|
||||
@@ -291,88 +423,103 @@ int scan_files(bool core1) {
|
||||
}
|
||||
ef_largeblob = search_by_fid(EF_LARGEBLOB, NULL, SPECIFY_EF);
|
||||
if (!file_has_data(ef_largeblob)) {
|
||||
flash_write_data_to_file(ef_largeblob, (const uint8_t *)"\x80\x76\xbe\x8b\x52\x8d\x00\x75\xf7\xaa\xe9\x8d\x6f\xa5\x7a\x6d\x3c", 17);
|
||||
file_put_data(ef_largeblob, (const uint8_t *) "\x80\x76\xbe\x8b\x52\x8d\x00\x75\xf7\xaa\xe9\x8d\x6f\xa5\x7a\x6d\x3c", 17);
|
||||
}
|
||||
|
||||
low_flash_available();
|
||||
return CCID_OK;
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
void scan_all(bool core1) {
|
||||
void scan_all() {
|
||||
scan_flash();
|
||||
scan_files(core1);
|
||||
scan_files();
|
||||
}
|
||||
|
||||
void init_fido(bool core1) {
|
||||
scan_all(core1);
|
||||
extern void init_otp();
|
||||
void init_fido() {
|
||||
scan_all();
|
||||
init_otp();
|
||||
}
|
||||
|
||||
bool wait_button_pressed() {
|
||||
uint32_t val = EV_PRESS_BUTTON;
|
||||
#if defined(ENABLE_UP_BUTTON) && ENABLE_UP_BUTTON==1
|
||||
#ifndef ENABLE_EMULATION
|
||||
queue_try_add(&card_to_usb_q, &val);
|
||||
do {
|
||||
queue_remove_blocking(&usb_to_card_q, &val);
|
||||
} while (val != EV_BUTTON_PRESSED && val != EV_BUTTON_TIMEOUT);
|
||||
#endif
|
||||
return (val == EV_BUTTON_TIMEOUT);
|
||||
return val == EV_BUTTON_TIMEOUT;
|
||||
}
|
||||
|
||||
uint32_t user_present_time_limit = 0;
|
||||
|
||||
bool check_user_presence() {
|
||||
#if defined(ENABLE_UP_BUTTON) && ENABLE_UP_BUTTON==1
|
||||
if (user_present_time_limit == 0 || user_present_time_limit+TRANSPORT_TIME_LIMIT < board_millis()) {
|
||||
if (wait_button_pressed() == true) //timeout
|
||||
if (user_present_time_limit == 0 || user_present_time_limit + TRANSPORT_TIME_LIMIT < board_millis()) {
|
||||
if (wait_button_pressed() == true) { //timeout
|
||||
return false;
|
||||
}
|
||||
//user_present_time_limit = board_millis();
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t get_sign_counter() {
|
||||
uint8_t *caddr = file_get_data(ef_counter);
|
||||
return (*caddr) | (*(caddr + 1) << 8) | (*(caddr + 2) << 16) | (*(caddr + 3) << 24);
|
||||
return get_uint32_t_le(caddr);
|
||||
}
|
||||
|
||||
uint8_t get_opts() {
|
||||
file_t *ef = search_by_fid(EF_OPTS, NULL, SPECIFY_EF);
|
||||
if (file_has_data(ef))
|
||||
if (file_has_data(ef)) {
|
||||
return *file_get_data(ef);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void set_opts(uint8_t opts) {
|
||||
file_t *ef = search_by_fid(EF_OPTS, NULL, SPECIFY_EF);
|
||||
flash_write_data_to_file(ef, &opts, sizeof(uint8_t));
|
||||
file_put_data(ef, &opts, sizeof(uint8_t));
|
||||
low_flash_available();
|
||||
}
|
||||
|
||||
typedef struct cmd
|
||||
{
|
||||
uint8_t ins;
|
||||
int (*cmd_handler)();
|
||||
} cmd_t;
|
||||
|
||||
extern int cmd_register();
|
||||
extern int cmd_authenticate();
|
||||
extern int cmd_version();
|
||||
extern int cbor_parse(int, uint8_t *, size_t);
|
||||
|
||||
#define CTAP_CBOR 0x10
|
||||
|
||||
int cmd_cbor() {
|
||||
uint8_t *old_buf = res_APDU;
|
||||
int ret = cbor_parse(0x90, apdu.data, apdu.nc);
|
||||
if (ret != 0) {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
res_APDU = old_buf;
|
||||
res_APDU_size += 1;
|
||||
memcpy(res_APDU, ctap_resp->init.data, res_APDU_size);
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
static const cmd_t cmds[] = {
|
||||
{ CTAP_REGISTER, cmd_register },
|
||||
{ CTAP_AUTHENTICATE, cmd_authenticate },
|
||||
{ CTAP_VERSION, cmd_version },
|
||||
{ 0x00, 0x0}
|
||||
{ CTAP_CBOR, cmd_cbor },
|
||||
{ 0x00, 0x0 }
|
||||
};
|
||||
|
||||
int fido_process_apdu() {
|
||||
if (CLA(apdu) != 0x00)
|
||||
if (CLA(apdu) != 0x00 && CLA(apdu) != 0x80) {
|
||||
return SW_CLA_NOT_SUPPORTED();
|
||||
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++)
|
||||
{
|
||||
if (cmd->ins == INS(apdu)) {
|
||||
int r = cmd->cmd_handler();
|
||||
return r;
|
||||
}
|
||||
if (cap_supported(CAP_U2F)) {
|
||||
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
|
||||
if (cmd->ins == INS(apdu)) {
|
||||
int r = cmd->cmd_handler();
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
return SW_INS_NOT_SUPPORTED();
|
||||
|
||||
@@ -18,11 +18,24 @@
|
||||
#ifndef _FIDO_H_
|
||||
#define _FIDO_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#if !defined(ENABLE_EMULATION) && !defined(ESP_PLATFORM)
|
||||
#include "pico/stdlib.h"
|
||||
#endif
|
||||
#ifndef ESP_PLATFORM
|
||||
#include "common.h"
|
||||
#else
|
||||
#define MBEDTLS_ALLOW_PRIVATE_ACCESS
|
||||
#endif
|
||||
|
||||
#include "mbedtls/ecdsa.h"
|
||||
#include "ctap_hid.h"
|
||||
#ifdef MBEDTLS_EDDSA_C
|
||||
#include "mbedtls/eddsa.h"
|
||||
#endif
|
||||
#ifndef ENABLE_EMULATION
|
||||
#include "hid/ctap_hid.h"
|
||||
#else
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
#define CTAP_PUBKEY_LEN (65)
|
||||
#define KEY_PATH_LEN (32)
|
||||
@@ -30,17 +43,21 @@
|
||||
#define SHA256_DIGEST_LENGTH (32)
|
||||
#define KEY_HANDLE_LEN (KEY_PATH_LEN + SHA256_DIGEST_LENGTH)
|
||||
|
||||
extern int scan_files(bool);
|
||||
extern int derive_key(const uint8_t *app_id, bool new_key, uint8_t *key_handle, int, mbedtls_ecdsa_context *key);
|
||||
extern int verify_key(const uint8_t *appId, const uint8_t *keyHandle, mbedtls_ecdsa_context *);
|
||||
extern int scan_files();
|
||||
extern int derive_key(const uint8_t *app_id,
|
||||
bool new_key,
|
||||
uint8_t *key_handle,
|
||||
int,
|
||||
mbedtls_ecp_keypair *key);
|
||||
extern int verify_key(const uint8_t *appId, const uint8_t *keyHandle, mbedtls_ecp_keypair *);
|
||||
extern bool wait_button_pressed();
|
||||
extern CTAPHID_FRAME *ctap_req, *ctap_resp;
|
||||
extern void init_fido(bool);
|
||||
extern void init_fido();
|
||||
extern mbedtls_ecp_group_id fido_curve_to_mbedtls(int curve);
|
||||
extern int fido_load_key(int curve, const uint8_t *cred_id, mbedtls_ecdsa_context *key);
|
||||
extern int mbedtls_curve_to_fido(mbedtls_ecp_group_id id);
|
||||
extern int fido_load_key(int curve, const uint8_t *cred_id, mbedtls_ecp_keypair *key);
|
||||
extern int load_keydev(uint8_t *key);
|
||||
extern int encrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, size_t in_len, uint8_t *out);
|
||||
extern int decrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, size_t in_len, uint8_t *out);
|
||||
extern int encrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, uint16_t in_len, uint8_t *out);
|
||||
extern int decrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, uint16_t in_len, uint8_t *out);
|
||||
extern int ecdh(uint8_t protocol, const mbedtls_ecp_point *Q, uint8_t *sharedSecret);
|
||||
|
||||
#define FIDO2_ALG_ES256 -7 //ECDSA-SHA256 P256
|
||||
@@ -48,6 +65,10 @@ extern int ecdh(uint8_t protocol, const mbedtls_ecp_point *Q, uint8_t *sharedSec
|
||||
#define FIDO2_ALG_ES384 -35 //ECDSA-SHA384 P384
|
||||
#define FIDO2_ALG_ES512 -36 //ECDSA-SHA512 P521
|
||||
#define FIDO2_ALG_ECDH_ES_HKDF_256 -25 //ECDH-ES + HKDF-256
|
||||
#define FIDO2_ALG_ES256K -47
|
||||
#define FIDO2_ALG_RS256 -257
|
||||
#define FIDO2_ALG_RS384 -258
|
||||
#define FIDO2_ALG_RS512 -259
|
||||
|
||||
#define FIDO2_CURVE_P256 1
|
||||
#define FIDO2_CURVE_P384 2
|
||||
@@ -64,6 +85,7 @@ extern int ecdh(uint8_t protocol, const mbedtls_ecp_point *Q, uint8_t *sharedSec
|
||||
#define FIDO2_AUT_FLAG_ED 0x80
|
||||
|
||||
#define FIDO2_OPT_EA 0x01 // Enterprise Attestation
|
||||
#define FIDO2_OPT_AUV 0x02 // User Verification
|
||||
|
||||
#define MAX_PIN_RETRIES 8
|
||||
extern bool getUserPresentFlagValue();
|
||||
@@ -92,7 +114,7 @@ typedef struct known_app {
|
||||
|
||||
extern const known_app_t *find_app_by_rp_id_hash(const uint8_t *rp_id_hash);
|
||||
|
||||
#define TRANSPORT_TIME_LIMIT (30*1000) //USB
|
||||
#define TRANSPORT_TIME_LIMIT (30 * 1000) //USB
|
||||
|
||||
bool check_user_presence();
|
||||
|
||||
@@ -110,6 +132,8 @@ typedef struct pinUvAuthToken {
|
||||
extern uint32_t user_present_time_limit;
|
||||
|
||||
extern pinUvAuthToken_t paut;
|
||||
extern int verify(uint8_t protocol, const uint8_t *key, const uint8_t *data, size_t len, uint8_t *sign);
|
||||
extern int verify(uint8_t protocol, const uint8_t *key, const uint8_t *data, uint16_t len, uint8_t *sign);
|
||||
|
||||
extern uint8_t session_pin[32];
|
||||
|
||||
#endif //_FIDO_H
|
||||
|
||||
@@ -15,26 +15,27 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "file.h"
|
||||
#include "files.h"
|
||||
|
||||
file_t file_entries[] = {
|
||||
{.fid = 0x3f00, .parent = 0xff, .name = NULL, .type = FILE_TYPE_DF, .data = NULL, .ef_structure = 0, .acl = {0}}, // MF
|
||||
{.fid = EF_KEY_DEV, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // Device Key
|
||||
{.fid = EF_KEY_DEV_ENC, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // Device Key Enc
|
||||
{.fid = EF_EE_DEV, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // End Entity Certificate Device
|
||||
{.fid = EF_EE_DEV_EA, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // End Entity Enterprise Attestation Certificate
|
||||
{.fid = EF_COUNTER, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // Global counter
|
||||
{.fid = EF_PIN, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // PIN
|
||||
{.fid = EF_AUTHTOKEN, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // AUTH TOKEN
|
||||
{.fid = EF_MINPINLEN, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // MIN PIN LENGTH
|
||||
{.fid = EF_OPTS, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // Global options
|
||||
{.fid = EF_LARGEBLOB, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // Large Blob
|
||||
{ .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_UNKNOWN, .data = NULL, .ef_structure = 0, .acl = {0} } //end
|
||||
{ .fid = 0x3f00, .parent = 0xff, .name = NULL, .type = FILE_TYPE_DF, .data = NULL, .ef_structure = 0, .acl = { 0 } }, // MF
|
||||
{ .fid = EF_KEY_DEV, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // Device Key
|
||||
{ .fid = EF_KEY_DEV_ENC, .parent = 0, .name = NULL,.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // Device Key Enc
|
||||
{ .fid = EF_MKEK, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // MKEK
|
||||
{ .fid = EF_EE_DEV, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // End Entity Certificate Device
|
||||
{ .fid = EF_EE_DEV_EA, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // End Entity Enterprise Attestation Certificate
|
||||
{ .fid = EF_COUNTER, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // Global counter
|
||||
{ .fid = EF_PIN, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // PIN
|
||||
{ .fid = EF_AUTHTOKEN, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // AUTH TOKEN
|
||||
{ .fid = EF_MINPINLEN, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // MIN PIN LENGTH
|
||||
{ .fid = EF_OPTS, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // Global options
|
||||
{ .fid = EF_LARGEBLOB, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // Large Blob
|
||||
{ .fid = EF_OTP_PIN, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } },
|
||||
{ .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_NOT_KNOWN, .data = NULL, .ef_structure = 0, .acl = { 0 } } //end
|
||||
};
|
||||
|
||||
const file_t *MF = &file_entries[0];
|
||||
const file_t *file_last = &file_entries[sizeof(file_entries)/sizeof(file_t)-1];
|
||||
const file_t *file_last = &file_entries[sizeof(file_entries) / sizeof(file_t) - 1];
|
||||
file_t *ef_keydev = NULL;
|
||||
file_t *ef_certdev = NULL;
|
||||
file_t *ef_counter = NULL;
|
||||
@@ -42,3 +43,4 @@ file_t *ef_pin = NULL;
|
||||
file_t *ef_authtoken = NULL;
|
||||
file_t *ef_keydev_enc = NULL;
|
||||
file_t *ef_largeblob = NULL;
|
||||
file_t *ef_mkek = NULL;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#define EF_KEY_DEV 0xCC00
|
||||
#define EF_KEY_DEV_ENC 0xCC01
|
||||
#define EF_MKEK 0xCC0F
|
||||
#define EF_EE_DEV 0xCE00
|
||||
#define EF_EE_DEV_EA 0xCE01
|
||||
#define EF_COUNTER 0xC000
|
||||
@@ -29,9 +30,15 @@
|
||||
#define EF_PIN 0x1080
|
||||
#define EF_AUTHTOKEN 0x1090
|
||||
#define EF_MINPINLEN 0x1100
|
||||
#define EF_DEV_CONF 0x1122
|
||||
#define EF_CRED 0xCF00 // Creds at 0xCF00 - 0xCFFF
|
||||
#define EF_RP 0xD000 // RPs at 0xD000 - 0xD0FF
|
||||
#define EF_LARGEBLOB 0x1101 // Large Blob Array
|
||||
#define EF_OATH_CRED 0xBA00 // OATH Creds at 0xBA00 - 0xBAFE
|
||||
#define EF_OATH_CODE 0xBAFF
|
||||
#define EF_OTP_SLOT1 0xBB00
|
||||
#define EF_OTP_SLOT2 0xBB01
|
||||
#define EF_OTP_PIN 0x10A0 // Nitrokey OTP PIN
|
||||
|
||||
extern file_t *ef_keydev;
|
||||
extern file_t *ef_certdev;
|
||||
@@ -40,5 +47,6 @@ extern file_t *ef_pin;
|
||||
extern file_t *ef_authtoken;
|
||||
extern file_t *ef_keydev_enc;
|
||||
extern file_t *ef_largeblob;
|
||||
extern file_t *ef_mkek;
|
||||
|
||||
#endif //_FILES_H_
|
||||
|
||||
137
src/fido/kek.c
Normal file
137
src/fido/kek.c
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* This file is part of the Pico Fido distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "fido.h"
|
||||
#include "pico_keys.h"
|
||||
#include "stdlib.h"
|
||||
#if !defined(ENABLE_EMULATION) && !defined(ESP_PLATFORM)
|
||||
#include "pico/stdlib.h"
|
||||
#endif
|
||||
#include "kek.h"
|
||||
#include "crypto_utils.h"
|
||||
#include "random.h"
|
||||
#include "mbedtls/md.h"
|
||||
#include "mbedtls/cmac.h"
|
||||
#include "mbedtls/rsa.h"
|
||||
#include "mbedtls/ecdsa.h"
|
||||
#include "mbedtls/chachapoly.h"
|
||||
#include "files.h"
|
||||
#include "otp.h"
|
||||
|
||||
extern uint8_t session_pin[32];
|
||||
uint8_t mkek_mask[MKEK_KEY_SIZE];
|
||||
bool has_mkek_mask = false;
|
||||
|
||||
#define POLY 0xedb88320
|
||||
|
||||
uint32_t crc32c(const uint8_t *buf, size_t len) {
|
||||
uint32_t crc = 0xffffffff;
|
||||
while (len--) {
|
||||
crc ^= *buf++;
|
||||
for (int k = 0; k < 8; k++) {
|
||||
crc = (crc >> 1) ^ (POLY & (0 - (crc & 1)));
|
||||
}
|
||||
}
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
void mkek_masked(uint8_t *mkek, const uint8_t *mask) {
|
||||
if (mask) {
|
||||
for (int i = 0; i < MKEK_KEY_SIZE; i++) {
|
||||
MKEK_KEY(mkek)[i] ^= mask[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int load_mkek(uint8_t *mkek) {
|
||||
file_t *tf = search_file(EF_MKEK);
|
||||
if (file_has_data(tf)) {
|
||||
memcpy(mkek, file_get_data(tf), MKEK_SIZE);
|
||||
}
|
||||
|
||||
if (has_mkek_mask) {
|
||||
mkek_masked(mkek, mkek_mask);
|
||||
}
|
||||
if (file_get_size(tf) == MKEK_SIZE) {
|
||||
int ret = aes_decrypt_cfb_256(session_pin, MKEK_IV(mkek), MKEK_KEY(mkek), MKEK_KEY_SIZE + MKEK_KEY_CS_SIZE);
|
||||
if (ret != 0) {
|
||||
return PICOKEY_EXEC_ERROR;
|
||||
}
|
||||
if (crc32c(MKEK_KEY(mkek), MKEK_KEY_SIZE) != *(uint32_t *) MKEK_CHECKSUM(mkek)) {
|
||||
return PICOKEY_WRONG_DKEK;
|
||||
}
|
||||
if (otp_key_1) {
|
||||
mkek_masked(mkek, otp_key_1);
|
||||
}
|
||||
}
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
void release_mkek(uint8_t *mkek) {
|
||||
mbedtls_platform_zeroize(mkek, MKEK_SIZE);
|
||||
}
|
||||
|
||||
int store_mkek(const uint8_t *mkek) {
|
||||
uint8_t tmp_mkek[MKEK_SIZE];
|
||||
if (mkek == NULL) {
|
||||
const uint8_t *rd = random_bytes_get(MKEK_IV_SIZE + MKEK_KEY_SIZE);
|
||||
memcpy(tmp_mkek, rd, MKEK_IV_SIZE + MKEK_KEY_SIZE);
|
||||
}
|
||||
else {
|
||||
memcpy(tmp_mkek, mkek, MKEK_SIZE);
|
||||
}
|
||||
if (otp_key_1) {
|
||||
mkek_masked(tmp_mkek, otp_key_1);
|
||||
}
|
||||
*(uint32_t *) MKEK_CHECKSUM(tmp_mkek) = crc32c(MKEK_KEY(tmp_mkek), MKEK_KEY_SIZE);
|
||||
uint8_t tmp_mkek_pin[MKEK_SIZE];
|
||||
memcpy(tmp_mkek_pin, tmp_mkek, MKEK_SIZE);
|
||||
file_t *tf = search_file(EF_MKEK);
|
||||
if (!tf) {
|
||||
release_mkek(tmp_mkek);
|
||||
release_mkek(tmp_mkek_pin);
|
||||
return PICOKEY_ERR_FILE_NOT_FOUND;
|
||||
}
|
||||
aes_encrypt_cfb_256(session_pin, MKEK_IV(tmp_mkek_pin), MKEK_KEY(tmp_mkek_pin), MKEK_KEY_SIZE + MKEK_KEY_CS_SIZE);
|
||||
file_put_data(tf, tmp_mkek_pin, MKEK_SIZE);
|
||||
release_mkek(tmp_mkek_pin);
|
||||
low_flash_available();
|
||||
release_mkek(tmp_mkek);
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
int mkek_encrypt(uint8_t *data, uint16_t len) {
|
||||
int r;
|
||||
uint8_t mkek[MKEK_SIZE + 4];
|
||||
if ((r = load_mkek(mkek)) != PICOKEY_OK) {
|
||||
return r;
|
||||
}
|
||||
r = aes_encrypt_cfb_256(MKEK_KEY(mkek), MKEK_IV(mkek), data, len);
|
||||
release_mkek(mkek);
|
||||
return r;
|
||||
}
|
||||
|
||||
int mkek_decrypt(uint8_t *data, uint16_t len) {
|
||||
int r;
|
||||
uint8_t mkek[MKEK_SIZE + 4];
|
||||
if ((r = load_mkek(mkek)) != PICOKEY_OK) {
|
||||
return r;
|
||||
}
|
||||
r = aes_decrypt_cfb_256(MKEK_KEY(mkek), MKEK_IV(mkek), data, len);
|
||||
release_mkek(mkek);
|
||||
return r;
|
||||
}
|
||||
46
src/fido/kek.h
Normal file
46
src/fido/kek.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* This file is part of the Pico Fido distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _KEK_H_
|
||||
#define _KEK_H_
|
||||
|
||||
#include "crypto_utils.h"
|
||||
#if defined(ENABLE_EMULATION) || defined(ESP_PLATFORM)
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
|
||||
extern int load_mkek(uint8_t *);
|
||||
extern int store_mkek(const uint8_t *);
|
||||
extern void init_mkek();
|
||||
extern void release_mkek(uint8_t *);
|
||||
extern int mkek_encrypt(uint8_t *data, uint16_t len);
|
||||
extern int mkek_decrypt(uint8_t *data, uint16_t len);
|
||||
|
||||
#define MKEK_IV_SIZE (IV_SIZE)
|
||||
#define MKEK_KEY_SIZE (32)
|
||||
#define MKEK_KEY_CS_SIZE (4)
|
||||
#define MKEK_SIZE (MKEK_IV_SIZE + MKEK_KEY_SIZE + MKEK_KEY_CS_SIZE)
|
||||
#define MKEK_IV(p) (p)
|
||||
#define MKEK_KEY(p) (MKEK_IV(p) + MKEK_IV_SIZE)
|
||||
#define MKEK_CHECKSUM(p) (MKEK_KEY(p) + MKEK_KEY_SIZE)
|
||||
#define DKEK_KEY_SIZE (32)
|
||||
|
||||
extern uint8_t mkek_mask[MKEK_KEY_SIZE];
|
||||
extern bool has_mkek_mask;
|
||||
|
||||
#endif
|
||||
@@ -15,280 +15,359 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "fido.h"
|
||||
#include "ctap2_cbor.h"
|
||||
|
||||
static const known_app_t kapps[] = {
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x96\x89\x78\xa2\x99\x53\xde\x52\xd3\xef\x0f\x0c\x71\xb7\xb7\xb6\xb1\xaf\x9f\x08\xe2\x57\x89\x6a\x8d\x81\x26\x91\x85\x30\x29\x3b",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x96\x89\x78\xa2\x99\x53\xde\x52\xd3\xef\x0f\x0c\x71\xb7\xb7\xb6\xb1\xaf\x9f\x08\xe2\x57\x89\x6a\x8d\x81\x26\x91\x85\x30\x29\x3b",
|
||||
.label = "aws.amazon.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xc3\x40\x8c\x04\x47\x88\xae\xa5\xb3\xdf\x30\x89\x52\xfd\x8c\xa3\xc7\x0e\x21\xfe\xf4\xf6\xc1\xc2\x37\x4c\xaa\x1d\xf9\xb2\x8d\xdd",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xc3\x40\x8c\x04\x47\x88\xae\xa5\xb3\xdf\x30\x89\x52\xfd\x8c\xa3\xc7\x0e\x21\xfe\xf4\xf6\xc1\xc2\x37\x4c\xaa\x1d\xf9\xb2\x8d\xdd",
|
||||
.label = "www.binance.com",
|
||||
.use_sign_count = pfalse,
|
||||
.use_self_attestation = ptrue,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x20\xf6\x61\xb1\x94\x0c\x34\x70\xac\x54\xfa\x2e\xb4\x99\x90\xfd\x33\xb5\x6d\xe8\xde\x60\x18\x70\xff\x02\xa8\x06\x0f\x3b\x7c\x58",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x20\xf6\x61\xb1\x94\x0c\x34\x70\xac\x54\xfa\x2e\xb4\x99\x90\xfd\x33\xb5\x6d\xe8\xde\x60\x18\x70\xff\x02\xa8\x06\x0f\x3b\x7c\x58",
|
||||
.label = "binance.com",
|
||||
.use_sign_count = pfalse,
|
||||
.use_self_attestation = ptrue,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x12\x74\x3b\x92\x12\x97\xb7\x7f\x11\x35\xe4\x1f\xde\xdd\x4a\x84\x6a\xfe\x82\xe1\xf3\x69\x32\xa9\x91\x2f\x3b\x0d\x8d\xfb\x7d\x0e",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x12\x74\x3b\x92\x12\x97\xb7\x7f\x11\x35\xe4\x1f\xde\xdd\x4a\x84\x6a\xfe\x82\xe1\xf3\x69\x32\xa9\x91\x2f\x3b\x0d\x8d\xfb\x7d\x0e",
|
||||
//U2F key for Bitbucket
|
||||
.label = "bitbucket.org",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x30\x2f\xd5\xb4\x49\x2a\x07\xb9\xfe\xbb\x30\xe7\x32\x69\xec\xa5\x01\x20\x5c\xcf\xe0\xc2\x0b\xf7\xb4\x72\xfa\x2d\x31\xe2\x1e\x63",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x30\x2f\xd5\xb4\x49\x2a\x07\xb9\xfe\xbb\x30\xe7\x32\x69\xec\xa5\x01\x20\x5c\xcf\xe0\xc2\x0b\xf7\xb4\x72\xfa\x2d\x31\xe2\x1e\x63",
|
||||
//U2F key for Bitfinex
|
||||
.label = "www.bitfinex.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xa3\x4d\x30\x9f\xfa\x28\xc1\x24\x14\xb8\xba\x6c\x07\xee\x1e\xfa\xe1\xa8\x5e\x8a\x04\x61\x48\x59\xa6\x7c\x04\x93\xb6\x95\x61\x90",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xa3\x4d\x30\x9f\xfa\x28\xc1\x24\x14\xb8\xba\x6c\x07\xee\x1e\xfa\xe1\xa8\x5e\x8a\x04\x61\x48\x59\xa6\x7c\x04\x93\xb6\x95\x61\x90",
|
||||
//U2F key for Bitwarden
|
||||
.label = "vault.bitwarden.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x19\x81\x5c\xb9\xa5\xfb\x25\xd8\x05\xde\xbd\x7b\x32\x53\x7e\xd5\x78\x63\x9b\x3e\xd1\x08\xec\x7c\x5b\xb9\xe8\xf0\xdf\xb1\x68\x73",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x19\x81\x5c\xb9\xa5\xfb\x25\xd8\x05\xde\xbd\x7b\x32\x53\x7e\xd5\x78\x63\x9b\x3e\xd1\x08\xec\x7c\x5b\xb9\xe8\xf0\xdf\xb1\x68\x73",
|
||||
//WebAuthn key for Cloudflare
|
||||
.label = "dash.cloudflare.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xe2\x7d\x61\xb4\xe9\x9d\xe0\xed\x98\x16\x3c\xb3\x8b\x7a\xf9\x33\xc6\x66\x5e\x55\x09\xe8\x49\x08\x37\x05\x58\x13\x77\x8e\x23\x6a",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xe2\x7d\x61\xb4\xe9\x9d\xe0\xed\x98\x16\x3c\xb3\x8b\x7a\xf9\x33\xc6\x66\x5e\x55\x09\xe8\x49\x08\x37\x05\x58\x13\x77\x8e\x23\x6a",
|
||||
//WebAuthn key for Coinbase
|
||||
.label = "coinbase.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x68\x20\x19\x15\xd7\x4c\xb4\x2a\xf5\xb3\xcc\x5c\x95\xb9\x55\x3e\x3e\x3a\x83\xb4\xd2\xa9\x3b\x45\xfb\xad\xaa\x84\x69\xff\x8e\x6e",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x68\x20\x19\x15\xd7\x4c\xb4\x2a\xf5\xb3\xcc\x5c\x95\xb9\x55\x3e\x3e\x3a\x83\xb4\xd2\xa9\x3b\x45\xfb\xad\xaa\x84\x69\xff\x8e\x6e",
|
||||
//U2F key for Dashlane
|
||||
.label = "www.dashlane.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xc5\x0f\x8a\x7b\x70\x8e\x92\xf8\x2e\x7a\x50\xe2\xbd\xc5\x5d\x8f\xd9\x1a\x22\xfe\x6b\x29\xc0\xcd\xf7\x80\x55\x30\x84\x2a\xf5\x81",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xc5\x0f\x8a\x7b\x70\x8e\x92\xf8\x2e\x7a\x50\xe2\xbd\xc5\x5d\x8f\xd9\x1a\x22\xfe\x6b\x29\xc0\xcd\xf7\x80\x55\x30\x84\x2a\xf5\x81",
|
||||
//U2F key for Dropbox
|
||||
.label = "www.dropbox.com",
|
||||
.use_sign_count = pfalse,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x82\xf4\xa8\xc9\x5f\xec\x94\xb2\x6b\xaf\x9e\x37\x25\x0e\x95\x63\xd9\xa3\x66\xc7\xbe\x26\x1c\xa4\xdd\x01\x01\xf4\xd5\xef\xcb\x83",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x82\xf4\xa8\xc9\x5f\xec\x94\xb2\x6b\xaf\x9e\x37\x25\x0e\x95\x63\xd9\xa3\x66\xc7\xbe\x26\x1c\xa4\xdd\x01\x01\xf4\xd5\xef\xcb\x83",
|
||||
//WebAuthn key for Dropbox
|
||||
.label = "www.dropbox.com",
|
||||
.use_sign_count = pfalse,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xf3\xe2\x04\x2f\x94\x60\x7d\xa0\xa9\xc1\xf3\xb9\x5e\x0d\x2f\x2b\xb2\xe0\x69\xc5\xbb\x4f\xa7\x64\xaf\xfa\x64\x7d\x84\x7b\x7e\xd6",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xf3\xe2\x04\x2f\x94\x60\x7d\xa0\xa9\xc1\xf3\xb9\x5e\x0d\x2f\x2b\xb2\xe0\x69\xc5\xbb\x4f\xa7\x64\xaf\xfa\x64\x7d\x84\x7b\x7e\xd6",
|
||||
//U2F key for Duo
|
||||
.label = "duosecurity.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x31\x19\x33\x28\xf8\xe2\x1d\xfb\x6c\x99\xf3\x22\xd2\x2d\x7b\x0b\x50\x87\x78\xe6\x4f\xfb\xba\x86\xe5\x22\x93\x37\x90\x31\xb8\x74",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x31\x19\x33\x28\xf8\xe2\x1d\xfb\x6c\x99\xf3\x22\xd2\x2d\x7b\x0b\x50\x87\x78\xe6\x4f\xfb\xba\x86\xe5\x22\x93\x37\x90\x31\xb8\x74",
|
||||
//WebAuthn key for Facebook
|
||||
.label = "facebook.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x69\x66\xab\xe3\x67\x4e\xa2\xf5\x30\x79\xeb\x71\x01\x97\x84\x8c\x9b\xe6\xf3\x63\x99\x2f\xd0\x29\xe9\x89\x84\x47\xcb\x9f\x00\x84",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x69\x66\xab\xe3\x67\x4e\xa2\xf5\x30\x79\xeb\x71\x01\x97\x84\x8c\x9b\xe6\xf3\x63\x99\x2f\xd0\x29\xe9\x89\x84\x47\xcb\x9f\x00\x84",
|
||||
//U2F key for FastMail
|
||||
.label = "www.fastmail.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x3f\xcb\x82\x82\xb8\x46\x76\xeb\xee\x71\x40\xe3\x9e\xca\xe1\x6e\xeb\x19\x90\x64\xc7\xc7\xe4\x43\x2e\x28\xc9\xb5\x7e\x4b\x60\x39",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x3f\xcb\x82\x82\xb8\x46\x76\xeb\xee\x71\x40\xe3\x9e\xca\xe1\x6e\xeb\x19\x90\x64\xc7\xc7\xe4\x43\x2e\x28\xc9\xb5\x7e\x4b\x60\x39",
|
||||
.label = "fastmail.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x9d\x61\x44\x2f\x5c\xe1\x33\xbd\x46\x54\x4f\xc4\x2f\x0a\x6d\x54\xc0\xde\xb8\x88\x40\xca\xc2\xb6\xae\xfa\x65\x14\xf8\x93\x49\xe9",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x9d\x61\x44\x2f\x5c\xe1\x33\xbd\x46\x54\x4f\xc4\x2f\x0a\x6d\x54\xc0\xde\xb8\x88\x40\xca\xc2\xb6\xae\xfa\x65\x14\xf8\x93\x49\xe9",
|
||||
.label = "fedoraproject.org",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xa4\xe2\x2d\xca\xfe\xa7\xe9\x0e\x12\x89\x50\x11\x39\x89\xfc\x45\x97\x8d\xc9\xfb\x87\x76\x75\x60\x51\x6c\x1c\x69\xdf\xdf\xd1\x96",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xa4\xe2\x2d\xca\xfe\xa7\xe9\x0e\x12\x89\x50\x11\x39\x89\xfc\x45\x97\x8d\xc9\xfb\x87\x76\x75\x60\x51\x6c\x1c\x69\xdf\xdf\xd1\x96",
|
||||
.label = "gandi.net",
|
||||
.use_sign_count = pfalse,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x54\xce\x65\x1e\xd7\x15\xb4\xaa\xa7\x55\xee\xce\xbd\x4e\xa0\x95\x08\x15\xb3\x34\xbd\x07\xd1\x09\x89\x3e\x96\x30\x18\xcd\xdb\xd9",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x54\xce\x65\x1e\xd7\x15\xb4\xaa\xa7\x55\xee\xce\xbd\x4e\xa0\x95\x08\x15\xb3\x34\xbd\x07\xd1\x09\x89\x3e\x96\x30\x18\xcd\xdb\xd9",
|
||||
//WebAuthn key for Gandi
|
||||
.label = "gandi.net",
|
||||
.use_sign_count = pfalse,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x86\x06\xc1\x68\xe5\x1f\xc1\x31\xe5\x46\xad\x57\xa1\x9f\x32\x97\xb1\x1e\x0e\x5c\xe8\x3e\x8e\x89\x31\xb2\x85\x08\x11\xcf\xa8\x81",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x86\x06\xc1\x68\xe5\x1f\xc1\x31\xe5\x46\xad\x57\xa1\x9f\x32\x97\xb1\x1e\x0e\x5c\xe8\x3e\x8e\x89\x31\xb2\x85\x08\x11\xcf\xa8\x81",
|
||||
//WebAuthn key for Gemini
|
||||
.label = "gemini.com",
|
||||
.use_sign_count = pfalse,
|
||||
.use_self_attestation = ptrue,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x70\x61\x7d\xfe\xd0\x65\x86\x3a\xf4\x7c\x15\x55\x6c\x91\x79\x88\x80\x82\x8c\xc4\x07\xfd\xf7\x0a\xe8\x50\x11\x56\x94\x65\xa0\x75",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x70\x61\x7d\xfe\xd0\x65\x86\x3a\xf4\x7c\x15\x55\x6c\x91\x79\x88\x80\x82\x8c\xc4\x07\xfd\xf7\x0a\xe8\x50\x11\x56\x94\x65\xa0\x75",
|
||||
//U2F key for GitHub
|
||||
.label = "github.com",
|
||||
.use_sign_count = ptrue,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x3a\xeb\x00\x24\x60\x38\x1c\x6f\x25\x8e\x83\x95\xd3\x02\x6f\x57\x1f\x0d\x9a\x76\x48\x8d\xcd\x83\x76\x39\xb1\x3a\xed\x31\x65\x60",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x3a\xeb\x00\x24\x60\x38\x1c\x6f\x25\x8e\x83\x95\xd3\x02\x6f\x57\x1f\x0d\x9a\x76\x48\x8d\xcd\x83\x76\x39\xb1\x3a\xed\x31\x65\x60",
|
||||
//WebAuthn key for GitHub
|
||||
.label = "github.com",
|
||||
.use_sign_count = ptrue,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xe7\xbe\x96\xa5\x1b\xd0\x19\x2a\x72\x84\x0d\x2e\x59\x09\xf7\x2b\xa8\x2a\x2f\xe9\x3f\xaa\x62\x4f\x03\x39\x6b\x30\xe4\x94\xc8\x04",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xe7\xbe\x96\xa5\x1b\xd0\x19\x2a\x72\x84\x0d\x2e\x59\x09\xf7\x2b\xa8\x2a\x2f\xe9\x3f\xaa\x62\x4f\x03\x39\x6b\x30\xe4\x94\xc8\x04",
|
||||
//U2F key for GitLab
|
||||
.label = "gitlab.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xa5\x46\x72\xb2\x22\xc4\xcf\x95\xe1\x51\xed\x8d\x4d\x3c\x76\x7a\x6c\xc3\x49\x43\x59\x43\x79\x4e\x88\x4f\x3d\x02\x3a\x82\x29\xfd",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xa5\x46\x72\xb2\x22\xc4\xcf\x95\xe1\x51\xed\x8d\x4d\x3c\x76\x7a\x6c\xc3\x49\x43\x59\x43\x79\x4e\x88\x4f\x3d\x02\x3a\x82\x29\xfd",
|
||||
//U2F key for Google
|
||||
.label = "google.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xd4\xc9\xd9\x02\x73\x26\x27\x1a\x89\xce\x51\xfc\xaf\x32\x8e\xd6\x73\xf1\x7b\xe3\x34\x69\xff\x97\x9e\x8a\xb8\xdd\x50\x1e\x66\x4f",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xd4\xc9\xd9\x02\x73\x26\x27\x1a\x89\xce\x51\xfc\xaf\x32\x8e\xd6\x73\xf1\x7b\xe3\x34\x69\xff\x97\x9e\x8a\xb8\xdd\x50\x1e\x66\x4f",
|
||||
//WebAuthn key for Google
|
||||
.label = "google.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x53\xa1\x5b\xa4\x2a\x7c\x03\x25\xb8\xdb\xee\x28\x96\x34\xa4\x8f\x58\xae\xa3\x24\x66\x45\xd5\xff\x41\x8f\x9b\xb8\x81\x98\x85\xa9",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x53\xa1\x5b\xa4\x2a\x7c\x03\x25\xb8\xdb\xee\x28\x96\x34\xa4\x8f\x58\xae\xa3\x24\x66\x45\xd5\xff\x41\x8f\x9b\xb8\x81\x98\x85\xa9",
|
||||
//U2F key for Keeper
|
||||
.label = "keepersecurity.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xd6\x5f\x00\x5e\xf4\xde\xa9\x32\x0c\x99\x73\x05\x3c\x95\xff\x60\x20\x11\x5d\x5f\xec\x1b\x7f\xee\x41\xa5\x78\xe1\x8d\xf9\xca\x8c",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xd6\x5f\x00\x5e\xf4\xde\xa9\x32\x0c\x99\x73\x05\x3c\x95\xff\x60\x20\x11\x5d\x5f\xec\x1b\x7f\xee\x41\xa5\x78\xe1\x8d\xf9\xca\x8c",
|
||||
//U2F key for Keeper
|
||||
.label = "keepersecurity.eu",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x3f\x37\x50\x85\x33\x2c\xac\x4f\xad\xf9\xe5\xdd\x28\xcd\x54\x69\x8f\xab\x98\x4b\x75\xd9\xc3\x6a\x07\x2c\xb1\x60\x77\x3f\x91\x52",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x3f\x37\x50\x85\x33\x2c\xac\x4f\xad\xf9\xe5\xdd\x28\xcd\x54\x69\x8f\xab\x98\x4b\x75\xd9\xc3\x6a\x07\x2c\xb1\x60\x77\x3f\x91\x52",
|
||||
//WebAuthn key for Kraken
|
||||
.label = "kraken.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xf8\x3f\xc3\xa1\xb2\x89\xa0\xde\xc5\xc1\xc8\xaa\x07\xe9\xb5\xdd\x9c\xbb\x76\xf6\xb2\xf5\x60\x60\x17\x66\x72\x68\xe5\xb9\xc4\x5e",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xf8\x3f\xc3\xa1\xb2\x89\xa0\xde\xc5\xc1\xc8\xaa\x07\xe9\xb5\xdd\x9c\xbb\x76\xf6\xb2\xf5\x60\x60\x17\x66\x72\x68\xe5\xb9\xc4\x5e",
|
||||
//WebAuthn key for login.gov
|
||||
.label = "secure.login.gov",
|
||||
.use_sign_count = pfalse,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x35\x6c\x9e\xd4\xa0\x93\x21\xb9\x69\x5f\x1e\xaf\x91\x82\x03\xf1\xb5\x5f\x68\x9d\xa6\x1f\xbc\x96\x18\x4c\x15\x7d\xda\x68\x0c\x81",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x35\x6c\x9e\xd4\xa0\x93\x21\xb9\x69\x5f\x1e\xaf\x91\x82\x03\xf1\xb5\x5f\x68\x9d\xa6\x1f\xbc\x96\x18\x4c\x15\x7d\xda\x68\x0c\x81",
|
||||
//WebAuthn key for Microsoft
|
||||
.label = "login.microsoft.com",
|
||||
.use_sign_count = pfalse,
|
||||
.use_self_attestation = pfalse,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xab\x2d\xaf\x07\x43\xde\x78\x2a\x70\x18\x9a\x0f\x5e\xfc\x30\x90\x2f\x92\x5b\x9f\x9a\x18\xc5\xd7\x14\x1b\x7b\x12\xf8\xa0\x10\x0c",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xab\x2d\xaf\x07\x43\xde\x78\x2a\x70\x18\x9a\x0f\x5e\xfc\x30\x90\x2f\x92\x5b\x9f\x9a\x18\xc5\xd7\x14\x1b\x7b\x12\xf8\xa0\x10\x0c",
|
||||
//WebAuthn key for mojeID
|
||||
.label = "mojeid.cz",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x85\x71\x01\x36\x1b\x20\xa9\x54\x4c\xdb\x9b\xef\x65\x85\x8b\x6b\xac\x70\x13\x55\x0d\x8f\x84\xf7\xef\xee\x25\x2b\x96\xfa\x7c\x1e",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x85\x71\x01\x36\x1b\x20\xa9\x54\x4c\xdb\x9b\xef\x65\x85\x8b\x6b\xac\x70\x13\x55\x0d\x8f\x84\xf7\xef\xee\x25\x2b\x96\xfa\x7c\x1e",
|
||||
//WebAuthn key for Namecheap
|
||||
.label = "www.namecheap.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x08\xb2\xa3\xd4\x19\x39\xaa\x31\x66\x84\x93\xcb\x36\xcd\xcc\x4f\x16\xc4\xd9\xb4\xc8\x23\x8b\x73\xc2\xf6\x72\xc0\x33\x00\x71\x97",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x08\xb2\xa3\xd4\x19\x39\xaa\x31\x66\x84\x93\xcb\x36\xcd\xcc\x4f\x16\xc4\xd9\xb4\xc8\x23\x8b\x73\xc2\xf6\x72\xc0\x33\x00\x71\x97",
|
||||
//U2F key for Slush Pool
|
||||
.label = "slushpool.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x38\x80\x4f\x2e\xff\x74\xf2\x28\xb7\x41\x51\xc2\x01\xaa\x82\xe7\xe8\xee\xfc\xac\xfe\xcf\x23\xfa\x14\x6b\x13\xa3\x76\x66\x31\x4f",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x38\x80\x4f\x2e\xff\x74\xf2\x28\xb7\x41\x51\xc2\x01\xaa\x82\xe7\xe8\xee\xfc\xac\xfe\xcf\x23\xfa\x14\x6b\x13\xa3\x76\x66\x31\x4f",
|
||||
//U2F key for Slush Pool
|
||||
.label = "slushpool.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x2a\xc6\xad\x09\xa6\xd0\x77\x2c\x44\xda\x73\xa6\x07\x2f\x9d\x24\x0f\xc6\x85\x4a\x70\xd7\x9c\x10\x24\xff\x7c\x75\x59\x59\x32\x92",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x2a\xc6\xad\x09\xa6\xd0\x77\x2c\x44\xda\x73\xa6\x07\x2f\x9d\x24\x0f\xc6\x85\x4a\x70\xd7\x9c\x10\x24\xff\x7c\x75\x59\x59\x32\x92",
|
||||
//U2F key for Stripe
|
||||
.label = "stripe.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xfa\xbe\xec\xe3\x98\x2f\xad\x9d\xdc\xc9\x8f\x91\xbd\x2e\x75\xaf\xc7\xd1\xf4\xca\x54\x49\x29\xb2\xd0\xd0\x42\x12\xdf\xfa\x30\xfa",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xfa\xbe\xec\xe3\x98\x2f\xad\x9d\xdc\xc9\x8f\x91\xbd\x2e\x75\xaf\xc7\xd1\xf4\xca\x54\x49\x29\xb2\xd0\xd0\x42\x12\xdf\xfa\x30\xfa",
|
||||
//U2F key for Tutanota
|
||||
.label = "tutanota.com",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x1b\x3c\x16\xdd\x2f\x7c\x46\xe2\xb4\xc2\x89\xdc\x16\x74\x6b\xcc\x60\xdf\xcf\x0f\xb8\x18\xe1\x32\x15\x52\x6e\x14\x08\xe7\xf4\x68",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x1b\x3c\x16\xdd\x2f\x7c\x46\xe2\xb4\xc2\x89\xdc\x16\x74\x6b\xcc\x60\xdf\xcf\x0f\xb8\x18\xe1\x32\x15\x52\x6e\x14\x08\xe7\xf4\x68",
|
||||
//U2F key for u2f.bin.coffee
|
||||
.label = "u2f.bin.coffee",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xa6\x42\xd2\x1b\x7c\x6d\x55\xe1\xce\x23\xc5\x39\x98\x28\xd2\xc7\x49\xbf\x6a\x6e\xf2\xfe\x03\xcc\x9e\x10\xcd\xf4\xed\x53\x08\x8b",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xa6\x42\xd2\x1b\x7c\x6d\x55\xe1\xce\x23\xc5\x39\x98\x28\xd2\xc7\x49\xbf\x6a\x6e\xf2\xfe\x03\xcc\x9e\x10\xcd\xf4\xed\x53\x08\x8b",
|
||||
//WebAuthn key for webauthn.bin.coffee
|
||||
.label = "webauthn.bin.coffee",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\x74\xa6\xea\x92\x13\xc9\x9c\x2f\x74\xb2\x24\x92\xb3\x20\xcf\x40\x26\x2a\x94\xc1\xa9\x50\xa0\x39\x7f\x29\x25\x0b\x60\x84\x1e\xf0",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\x74\xa6\xea\x92\x13\xc9\x9c\x2f\x74\xb2\x24\x92\xb3\x20\xcf\x40\x26\x2a\x94\xc1\xa9\x50\xa0\x39\x7f\x29\x25\x0b\x60\x84\x1e\xf0",
|
||||
//WebAuthn key for WebAuthn.io
|
||||
.label = "webauthn.io",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xf9\x5b\xc7\x38\x28\xee\x21\x0f\x9f\xd3\xbb\xe7\x2d\x97\x90\x80\x13\xb0\xa3\x75\x9e\x9a\xea\x3d\x0a\xe3\x18\x76\x6c\xd2\xe1\xad",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xf9\x5b\xc7\x38\x28\xee\x21\x0f\x9f\xd3\xbb\xe7\x2d\x97\x90\x80\x13\xb0\xa3\x75\x9e\x9a\xea\x3d\x0a\xe3\x18\x76\x6c\xd2\xe1\xad",
|
||||
//WebAuthn key for WebAuthn.me
|
||||
.label = "webauthn.me",
|
||||
.use_sign_count = NULL,
|
||||
.use_self_attestation = NULL,
|
||||
},
|
||||
{
|
||||
.rp_id_hash = (const uint8_t *)"\xc4\x6c\xef\x82\xad\x1b\x54\x64\x77\x59\x1d\x00\x8b\x08\x75\x9e\xc3\xe6\xd2\xec\xb4\xf3\x94\x74\xbf\xea\x69\x69\x92\x5d\x03\xb7",
|
||||
.rp_id_hash =
|
||||
(const uint8_t *)
|
||||
"\xc4\x6c\xef\x82\xad\x1b\x54\x64\x77\x59\x1d\x00\x8b\x08\x75\x9e\xc3\xe6\xd2\xec\xb4\xf3\x94\x74\xbf\xea\x69\x69\x92\x5d\x03\xb7",
|
||||
//WebAuthn key for demo.yubico.com
|
||||
.label = "demo.yubico.com",
|
||||
.use_sign_count = NULL,
|
||||
@@ -304,8 +383,9 @@ static const known_app_t kapps[] = {
|
||||
|
||||
const known_app_t *find_app_by_rp_id_hash(const uint8_t *rp_id_hash) {
|
||||
for (const known_app_t *ka = &kapps[0]; ka->rp_id_hash != NULL; ka++) {
|
||||
if (memcmp(rp_id_hash, ka->rp_id_hash, 32) == 0)
|
||||
if (memcmp(rp_id_hash, ka->rp_id_hash, 32) == 0) {
|
||||
return ka;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
182
src/fido/management.c
Normal file
182
src/fido/management.c
Normal file
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* This file is part of the Pico FIDO distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "fido.h"
|
||||
#include "pico_keys.h"
|
||||
#include "apdu.h"
|
||||
#include "version.h"
|
||||
#include "files.h"
|
||||
#include "asn1.h"
|
||||
#include "management.h"
|
||||
|
||||
int man_process_apdu();
|
||||
int man_unload();
|
||||
|
||||
const uint8_t man_aid[] = {
|
||||
8,
|
||||
0xa0, 0x00, 0x00, 0x05, 0x27, 0x47, 0x11, 0x17
|
||||
};
|
||||
extern void scan_all();
|
||||
extern void init_otp();
|
||||
int man_select(app_t *a, uint8_t force) {
|
||||
a->process_apdu = man_process_apdu;
|
||||
a->unload = man_unload;
|
||||
sprintf((char *) res_APDU, "%d.%d.0", PICO_FIDO_VERSION_MAJOR, PICO_FIDO_VERSION_MINOR);
|
||||
res_APDU_size = (uint16_t)strlen((char *) res_APDU);
|
||||
apdu.ne = res_APDU_size;
|
||||
if (force) {
|
||||
scan_all();
|
||||
init_otp();
|
||||
}
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
INITIALIZER ( man_ctor ) {
|
||||
register_app(man_select, man_aid);
|
||||
}
|
||||
|
||||
int man_unload() {
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
bool cap_supported(uint16_t cap) {
|
||||
file_t *ef = search_dynamic_file(EF_DEV_CONF);
|
||||
if (file_has_data(ef)) {
|
||||
uint16_t tag = 0x0;
|
||||
uint8_t *tag_data = NULL, *p = NULL;
|
||||
uint16_t tag_len = 0;
|
||||
asn1_ctx_t ctxi;
|
||||
asn1_ctx_init(file_get_data(ef), file_get_size(ef), &ctxi);
|
||||
while (walk_tlv(&ctxi, &p, &tag, &tag_len, &tag_data)) {
|
||||
if (tag == TAG_USB_ENABLED) {
|
||||
uint16_t ecaps = tag_data[0];
|
||||
if (tag_len == 2) {
|
||||
ecaps = get_uint16_t_be(tag_data);
|
||||
}
|
||||
return ecaps & cap;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int man_get_config() {
|
||||
file_t *ef = search_dynamic_file(EF_DEV_CONF);
|
||||
res_APDU_size = 0;
|
||||
res_APDU[res_APDU_size++] = 0; // Overall length. Filled later
|
||||
res_APDU[res_APDU_size++] = TAG_USB_SUPPORTED;
|
||||
res_APDU[res_APDU_size++] = 2;
|
||||
res_APDU[res_APDU_size++] = CAP_FIDO2 >> 8;
|
||||
res_APDU[res_APDU_size++] = CAP_OTP | CAP_U2F | CAP_OATH;
|
||||
res_APDU[res_APDU_size++] = TAG_SERIAL;
|
||||
res_APDU[res_APDU_size++] = 4;
|
||||
memcpy(res_APDU + res_APDU_size, pico_serial.id, 4);
|
||||
res_APDU_size += 4;
|
||||
res_APDU[res_APDU_size++] = TAG_FORM_FACTOR;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = 0x01;
|
||||
res_APDU[res_APDU_size++] = TAG_VERSION;
|
||||
res_APDU[res_APDU_size++] = 3;
|
||||
res_APDU[res_APDU_size++] = PICO_FIDO_VERSION_MAJOR;
|
||||
res_APDU[res_APDU_size++] = PICO_FIDO_VERSION_MINOR;
|
||||
res_APDU[res_APDU_size++] = 0;
|
||||
if (!file_has_data(ef)) {
|
||||
res_APDU[res_APDU_size++] = TAG_USB_ENABLED;
|
||||
res_APDU[res_APDU_size++] = 2;
|
||||
uint16_t caps = 0;
|
||||
if (cap_supported(CAP_FIDO2)) {
|
||||
caps |= CAP_FIDO2;
|
||||
}
|
||||
if (cap_supported(CAP_OTP)) {
|
||||
caps |= CAP_OTP;
|
||||
}
|
||||
if (cap_supported(CAP_U2F)) {
|
||||
caps |= CAP_U2F;
|
||||
}
|
||||
if (cap_supported(CAP_OATH)) {
|
||||
caps |= CAP_OATH;
|
||||
}
|
||||
res_APDU[res_APDU_size++] = caps >> 8;
|
||||
res_APDU[res_APDU_size++] = caps & 0xFF;
|
||||
res_APDU[res_APDU_size++] = TAG_DEVICE_FLAGS;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = FLAG_EJECT;
|
||||
res_APDU[res_APDU_size++] = TAG_CONFIG_LOCK;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = 0x00;
|
||||
}
|
||||
else {
|
||||
memcpy(res_APDU + res_APDU_size, file_get_data(ef), file_get_size(ef));
|
||||
res_APDU_size += file_get_size(ef);
|
||||
}
|
||||
res_APDU[0] = (uint8_t)(res_APDU_size - 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_read_config() {
|
||||
man_get_config();
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int cmd_write_config() {
|
||||
if (apdu.data[0] != apdu.nc - 1) {
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
file_t *ef = file_new(EF_DEV_CONF);
|
||||
file_put_data(ef, apdu.data + 1, (uint16_t)(apdu.nc - 1));
|
||||
low_flash_available();
|
||||
#ifndef ENABLE_EMULATION
|
||||
if (cap_supported(CAP_OTP)) {
|
||||
phy_data.enabled_usb_itf |= PHY_USB_ITF_KB;
|
||||
}
|
||||
else {
|
||||
phy_data.enabled_usb_itf &= ~PHY_USB_ITF_KB;
|
||||
}
|
||||
phy_save();
|
||||
#endif
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
extern int cbor_reset();
|
||||
int cmd_factory_reset() {
|
||||
cbor_reset();
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
#define INS_READ_CONFIG 0x1D
|
||||
#define INS_WRITE_CONFIG 0x1C
|
||||
#define INS_RESET 0x1E // Reset device
|
||||
|
||||
static const cmd_t cmds[] = {
|
||||
{ INS_READ_CONFIG, cmd_read_config },
|
||||
{ INS_WRITE_CONFIG, cmd_write_config },
|
||||
{ INS_RESET, cmd_factory_reset },
|
||||
{ 0x00, 0x0 }
|
||||
};
|
||||
|
||||
int man_process_apdu() {
|
||||
if (CLA(apdu) != 0x00) {
|
||||
return SW_CLA_NOT_SUPPORTED();
|
||||
}
|
||||
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
|
||||
if (cmd->ins == INS(apdu)) {
|
||||
int r = cmd->cmd_handler();
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return SW_INS_NOT_SUPPORTED();
|
||||
}
|
||||
55
src/fido/management.h
Normal file
55
src/fido/management.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* This file is part of the Pico FIDO distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _MANAGEMENT_H_
|
||||
#define _MANAGEMENT_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#if !defined(ENABLE_EMULATION) && !defined(ESP_PLATFORM)
|
||||
#include "pico/stdlib.h"
|
||||
#endif
|
||||
|
||||
#define TAG_USB_SUPPORTED 0x01
|
||||
#define TAG_SERIAL 0x02
|
||||
#define TAG_USB_ENABLED 0x03
|
||||
#define TAG_FORM_FACTOR 0x04
|
||||
#define TAG_VERSION 0x05
|
||||
#define TAG_AUTO_EJECT_TIMEOUT 0x06
|
||||
#define TAG_CHALRESP_TIMEOUT 0x07
|
||||
#define TAG_DEVICE_FLAGS 0x08
|
||||
#define TAG_APP_VERSIONS 0x09
|
||||
#define TAG_CONFIG_LOCK 0x0A
|
||||
#define TAG_UNLOCK 0x0B
|
||||
#define TAG_REBOOT 0x0C
|
||||
#define TAG_NFC_SUPPORTED 0x0D
|
||||
#define TAG_NFC_ENABLED 0x0E
|
||||
|
||||
#define CAP_OTP 0x01
|
||||
#define CAP_U2F 0x02
|
||||
#define CAP_FIDO2 0x200
|
||||
#define CAP_OATH 0x20
|
||||
#define CAP_PIV 0x10
|
||||
#define CAP_OPENPGP 0x08
|
||||
#define CAP_HSMAUTH 0x100
|
||||
|
||||
#define FLAG_REMOTE_WAKEUP 0x40
|
||||
#define FLAG_EJECT 0x80
|
||||
|
||||
extern bool cap_supported(uint16_t cap);
|
||||
extern int man_get_config();
|
||||
|
||||
#endif //_MANAGEMENT_H
|
||||
675
src/fido/oath.c
Normal file
675
src/fido/oath.c
Normal file
@@ -0,0 +1,675 @@
|
||||
/*
|
||||
* This file is part of the Pico FIDO distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "fido.h"
|
||||
#include "pico_keys.h"
|
||||
#include "apdu.h"
|
||||
#include "files.h"
|
||||
#include "random.h"
|
||||
#include "version.h"
|
||||
#include "asn1.h"
|
||||
#include "crypto_utils.h"
|
||||
#include "management.h"
|
||||
|
||||
#define MAX_OATH_CRED 255
|
||||
#define CHALLENGE_LEN 8
|
||||
#define MAX_OTP_COUNTER 3
|
||||
|
||||
#define TAG_NAME 0x71
|
||||
#define TAG_NAME_LIST 0x72
|
||||
#define TAG_KEY 0x73
|
||||
#define TAG_CHALLENGE 0x74
|
||||
#define TAG_RESPONSE 0x75
|
||||
#define TAG_T_RESPONSE 0x76
|
||||
#define TAG_NO_RESPONSE 0x77
|
||||
#define TAG_PROPERTY 0x78
|
||||
#define TAG_T_VERSION 0x79
|
||||
#define TAG_IMF 0x7a
|
||||
#define TAG_ALGO 0x7b
|
||||
#define TAG_TOUCH_RESPONSE 0x7c
|
||||
#define TAG_PASSWORD 0x80
|
||||
#define TAG_NEW_PASSWORD 0x81
|
||||
#define TAG_PIN_COUNTER 0x82
|
||||
|
||||
#define ALG_HMAC_SHA1 0x01
|
||||
#define ALG_HMAC_SHA256 0x02
|
||||
#define ALG_HMAC_SHA512 0x03
|
||||
#define ALG_MASK 0x0f
|
||||
|
||||
#define OATH_TYPE_HOTP 0x10
|
||||
#define OATH_TYPE_TOTP 0x20
|
||||
#define OATH_TYPE_MASK 0xf0
|
||||
|
||||
#define PROP_INC 0x01
|
||||
#define PROP_TOUCH 0x02
|
||||
|
||||
int oath_process_apdu();
|
||||
int oath_unload();
|
||||
|
||||
static bool validated = true;
|
||||
static uint8_t challenge[CHALLENGE_LEN] = { 0 };
|
||||
|
||||
const uint8_t oath_aid[] = {
|
||||
7,
|
||||
0xa0, 0x00, 0x00, 0x05, 0x27, 0x21, 0x01
|
||||
};
|
||||
|
||||
int oath_select(app_t *a, uint8_t force) {
|
||||
(void) force;
|
||||
if (cap_supported(CAP_OATH)) {
|
||||
a->process_apdu = oath_process_apdu;
|
||||
a->unload = oath_unload;
|
||||
res_APDU_size = 0;
|
||||
res_APDU[res_APDU_size++] = TAG_T_VERSION;
|
||||
res_APDU[res_APDU_size++] = 3;
|
||||
res_APDU[res_APDU_size++] = PICO_FIDO_VERSION_MAJOR;
|
||||
res_APDU[res_APDU_size++] = PICO_FIDO_VERSION_MINOR;
|
||||
res_APDU[res_APDU_size++] = 0;
|
||||
res_APDU[res_APDU_size++] = TAG_NAME;
|
||||
res_APDU[res_APDU_size++] = 8;
|
||||
memcpy(res_APDU + res_APDU_size, pico_serial_str, 8); res_APDU_size += 8;
|
||||
if (file_has_data(search_dynamic_file(EF_OATH_CODE)) == true) {
|
||||
random_gen(NULL, challenge, sizeof(challenge));
|
||||
res_APDU[res_APDU_size++] = TAG_CHALLENGE;
|
||||
res_APDU[res_APDU_size++] = sizeof(challenge);
|
||||
memcpy(res_APDU + res_APDU_size, challenge, sizeof(challenge));
|
||||
res_APDU_size += sizeof(challenge);
|
||||
}
|
||||
file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
|
||||
if (file_has_data(ef_otp_pin)) {
|
||||
const uint8_t *pin_data = file_get_data(ef_otp_pin);
|
||||
res_APDU[res_APDU_size++] = TAG_PIN_COUNTER;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = *pin_data;
|
||||
}
|
||||
res_APDU[res_APDU_size++] = TAG_ALGO;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = ALG_HMAC_SHA1;
|
||||
apdu.ne = res_APDU_size;
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
return PICOKEY_ERR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
INITIALIZER ( oath_ctor ) {
|
||||
register_app(oath_select, oath_aid);
|
||||
}
|
||||
|
||||
int oath_unload() {
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
file_t *find_oath_cred(const uint8_t *name, size_t name_len) {
|
||||
for (int i = 0; i < MAX_OATH_CRED; i++) {
|
||||
file_t *ef = search_dynamic_file((uint16_t)(EF_OATH_CRED + i));
|
||||
asn1_ctx_t ctxi, ef_tag = { 0 };
|
||||
asn1_ctx_init(file_get_data(ef), file_get_size(ef), &ctxi);
|
||||
if (file_has_data(ef) && asn1_find_tag(&ctxi, TAG_NAME, &ef_tag) == true && ef_tag.len == name_len && memcmp(ef_tag.data, name, name_len) == 0) {
|
||||
return ef;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int cmd_put() {
|
||||
if (validated == false) {
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
asn1_ctx_t ctxi, key = { 0 }, name = { 0 }, imf = { 0 };
|
||||
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
|
||||
if (asn1_find_tag(&ctxi, TAG_KEY, &key) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
if (asn1_find_tag(&ctxi, TAG_NAME, &name) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
if ((key.data[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
|
||||
if (asn1_find_tag(&ctxi, TAG_IMF, &imf) == false) {
|
||||
memcpy(apdu.data + apdu.nc, "\x7a\x08\x00\x00\x00\x00\x00\x00\x00\x00", 10);
|
||||
apdu.nc += 10;
|
||||
}
|
||||
else { //prepend zero-valued bytes
|
||||
if (imf.len < 8) {
|
||||
memmove(imf.data + (8 - imf.len), imf.data, imf.len);
|
||||
memset(imf.data, 0, 8 - imf.len);
|
||||
*(imf.data - 1) = 8;
|
||||
apdu.nc += (8 - imf.len);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
file_t *ef = find_oath_cred(name.data, name.len);
|
||||
if (file_has_data(ef)) {
|
||||
file_put_data(ef, apdu.data, (uint16_t)apdu.nc);
|
||||
low_flash_available();
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < MAX_OATH_CRED; i++) {
|
||||
file_t *tef = search_dynamic_file((uint16_t)(EF_OATH_CRED + i));
|
||||
if (!file_has_data(tef)) {
|
||||
tef = file_new((uint16_t)(EF_OATH_CRED + i));
|
||||
file_put_data(tef, apdu.data, (uint16_t)apdu.nc);
|
||||
low_flash_available();
|
||||
return SW_OK();
|
||||
}
|
||||
}
|
||||
return SW_FILE_FULL();
|
||||
}
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
|
||||
int cmd_delete() {
|
||||
if (validated == false) {
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
asn1_ctx_t ctxi, ctxo = { 0 };
|
||||
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
|
||||
if (asn1_find_tag(&ctxi, TAG_NAME, &ctxo) == true) {
|
||||
file_t *ef = find_oath_cred(ctxo.data, ctxo.len);
|
||||
if (ef) {
|
||||
delete_file(ef);
|
||||
return SW_OK();
|
||||
}
|
||||
return SW_DATA_INVALID();
|
||||
}
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
|
||||
const mbedtls_md_info_t *get_oath_md_info(uint8_t alg) {
|
||||
if ((alg & ALG_MASK) == ALG_HMAC_SHA1) {
|
||||
return mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
|
||||
}
|
||||
else if ((alg & ALG_MASK) == ALG_HMAC_SHA256) {
|
||||
return mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
|
||||
}
|
||||
else if ((alg & ALG_MASK) == ALG_HMAC_SHA512) {
|
||||
return mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int cmd_set_code() {
|
||||
if (validated == false) {
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
if (apdu.nc == 0) {
|
||||
delete_file(search_dynamic_file(EF_OATH_CODE));
|
||||
validated = true;
|
||||
return SW_OK();
|
||||
}
|
||||
asn1_ctx_t ctxi, key = { 0 }, chal = { 0 }, resp = { 0 };
|
||||
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
|
||||
if (asn1_find_tag(&ctxi, TAG_KEY, &key) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
if (key.len == 0) {
|
||||
delete_file(search_dynamic_file(EF_OATH_CODE));
|
||||
validated = true;
|
||||
return SW_OK();
|
||||
}
|
||||
if (asn1_find_tag(&ctxi, TAG_CHALLENGE, &chal) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
if (asn1_find_tag(&ctxi, TAG_RESPONSE, &resp) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
|
||||
const mbedtls_md_info_t *md_info = get_oath_md_info(key.data[0]);
|
||||
if (md_info == NULL) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
uint8_t hmac[64];
|
||||
int r = mbedtls_md_hmac(md_info, key.data + 1, key.len - 1, chal.data, chal.len, hmac);
|
||||
if (r != 0) {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
if (memcmp(hmac, resp.data, resp.len) != 0) {
|
||||
return SW_DATA_INVALID();
|
||||
}
|
||||
random_gen(NULL, challenge, sizeof(challenge));
|
||||
file_t *ef = file_new(EF_OATH_CODE);
|
||||
file_put_data(ef, key.data, key.len);
|
||||
low_flash_available();
|
||||
validated = false;
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int cmd_reset() {
|
||||
if (P1(apdu) != 0xde || P2(apdu) != 0xad) {
|
||||
return SW_INCORRECT_P1P2();
|
||||
}
|
||||
for (int i = 0; i < MAX_OATH_CRED; i++) {
|
||||
file_t *ef = search_dynamic_file((uint16_t)(EF_OATH_CRED + i));
|
||||
if (file_has_data(ef)) {
|
||||
delete_file(ef);
|
||||
}
|
||||
}
|
||||
delete_file(search_dynamic_file(EF_OATH_CODE));
|
||||
flash_clear_file(search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF));
|
||||
low_flash_available();
|
||||
validated = true;
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int cmd_list() {
|
||||
if (validated == false) {
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
for (int i = 0; i < MAX_OATH_CRED; i++) {
|
||||
file_t *ef = search_dynamic_file((uint16_t)(EF_OATH_CRED + i));
|
||||
if (file_has_data(ef)) {
|
||||
asn1_ctx_t ctxi, key = { 0 }, name = { 0 };
|
||||
asn1_ctx_init(file_get_data(ef), file_get_size(ef), &ctxi);
|
||||
if (asn1_find_tag(&ctxi, TAG_NAME, &name) == true && asn1_find_tag(&ctxi, TAG_KEY, &key) == true) {
|
||||
res_APDU[res_APDU_size++] = TAG_NAME_LIST;
|
||||
res_APDU[res_APDU_size++] = (uint8_t)(name.len + 1);
|
||||
res_APDU[res_APDU_size++] = key.data[0];
|
||||
memcpy(res_APDU + res_APDU_size, name.data, name.len); res_APDU_size += name.len;
|
||||
}
|
||||
}
|
||||
}
|
||||
apdu.ne = res_APDU_size;
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int cmd_validate() {
|
||||
asn1_ctx_t ctxi, key = { 0 }, chal = { 0 }, resp = { 0 };
|
||||
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
|
||||
if (asn1_find_tag(&ctxi, TAG_CHALLENGE, &chal) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
if (asn1_find_tag(&ctxi, TAG_RESPONSE, &resp) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
file_t *ef = search_dynamic_file(EF_OATH_CODE);
|
||||
if (file_has_data(ef) == false) {
|
||||
validated = true;
|
||||
return SW_DATA_INVALID();
|
||||
}
|
||||
key.data = file_get_data(ef);
|
||||
key.len = file_get_size(ef);
|
||||
const mbedtls_md_info_t *md_info = get_oath_md_info(key.data[0]);
|
||||
if (md_info == NULL) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
uint8_t hmac[64];
|
||||
int ret = mbedtls_md_hmac(md_info, key.data + 1, key.len - 1, challenge, sizeof(challenge), hmac);
|
||||
if (ret != 0) {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
if (memcmp(hmac, resp.data, resp.len) != 0) {
|
||||
return SW_DATA_INVALID();
|
||||
}
|
||||
ret = mbedtls_md_hmac(md_info, key.data + 1, key.len - 1, chal.data, chal.len, hmac);
|
||||
if (ret != 0) {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
validated = true;
|
||||
res_APDU[res_APDU_size++] = TAG_RESPONSE;
|
||||
res_APDU[res_APDU_size++] = mbedtls_md_get_size(md_info);
|
||||
memcpy(res_APDU + res_APDU_size, hmac, mbedtls_md_get_size(md_info));
|
||||
res_APDU_size += mbedtls_md_get_size(md_info);
|
||||
apdu.ne = res_APDU_size;
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int calculate_oath(uint8_t truncate, const uint8_t *key, size_t key_len, const uint8_t *chal, size_t chal_len) {
|
||||
const mbedtls_md_info_t *md_info = get_oath_md_info(key[0]);
|
||||
if (md_info == NULL) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
uint8_t hmac[64];
|
||||
int r = mbedtls_md_hmac(md_info, key + 2, key_len - 2, chal, chal_len, hmac);
|
||||
size_t hmac_size = mbedtls_md_get_size(md_info);
|
||||
if (r != 0) {
|
||||
return PICOKEY_EXEC_ERROR;
|
||||
}
|
||||
if (truncate == 0x01) {
|
||||
res_APDU[res_APDU_size++] = 4 + 1;
|
||||
res_APDU[res_APDU_size++] = key[1];
|
||||
uint8_t offset = hmac[hmac_size - 1] & 0x0f;
|
||||
res_APDU[res_APDU_size++] = hmac[offset] & 0x7f;
|
||||
res_APDU[res_APDU_size++] = hmac[offset + 1];
|
||||
res_APDU[res_APDU_size++] = hmac[offset + 2];
|
||||
res_APDU[res_APDU_size++] = hmac[offset + 3];
|
||||
}
|
||||
else {
|
||||
res_APDU[res_APDU_size++] = (uint8_t)(hmac_size + 1);
|
||||
res_APDU[res_APDU_size++] = key[1];
|
||||
memcpy(res_APDU + res_APDU_size, hmac, hmac_size); res_APDU_size += (uint16_t)hmac_size;
|
||||
}
|
||||
apdu.ne = res_APDU_size;
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
int cmd_calculate() {
|
||||
if (P2(apdu) != 0x0 && P2(apdu) != 0x1) {
|
||||
return SW_INCORRECT_P1P2();
|
||||
}
|
||||
if (validated == false) {
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
asn1_ctx_t ctxi, key = { 0 }, chal = { 0 }, name = { 0 };
|
||||
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
|
||||
if (asn1_find_tag(&ctxi, TAG_CHALLENGE, &chal) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
if (asn1_find_tag(&ctxi, TAG_NAME, &name) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
file_t *ef = find_oath_cred(name.data, name.len);
|
||||
if (file_has_data(ef) == false) {
|
||||
return SW_DATA_INVALID();
|
||||
}
|
||||
asn1_ctx_t ctxe;
|
||||
asn1_ctx_init(file_get_data(ef), file_get_size(ef), &ctxe);
|
||||
if (asn1_find_tag(&ctxe, TAG_KEY, &key) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
|
||||
if ((key.data[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
|
||||
if (asn1_find_tag(&ctxe, TAG_IMF, &chal) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
}
|
||||
|
||||
res_APDU[res_APDU_size++] = TAG_RESPONSE + P2(apdu);
|
||||
|
||||
int ret = calculate_oath(P2(apdu), key.data, key.len, chal.data, chal.len);
|
||||
if (ret != PICOKEY_OK) {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
if ((key.data[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
|
||||
uint64_t v = get_uint64_t_be(chal.data);
|
||||
size_t ef_size = file_get_size(ef);
|
||||
v++;
|
||||
uint8_t *tmp = (uint8_t *) calloc(1, ef_size);
|
||||
memcpy(tmp, file_get_data(ef), ef_size);
|
||||
asn1_ctx_t ctxt;
|
||||
asn1_ctx_init(tmp, (uint16_t)ef_size, &ctxt);
|
||||
asn1_find_tag(&ctxt, TAG_IMF, &chal);
|
||||
put_uint64_t_be(v, chal.data);
|
||||
file_put_data(ef, tmp, (uint16_t)ef_size);
|
||||
low_flash_available();
|
||||
free(tmp);
|
||||
}
|
||||
apdu.ne = res_APDU_size;
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int cmd_calculate_all() {
|
||||
asn1_ctx_t ctxi, key = { 0 }, chal = { 0 }, name = { 0 }, prop = { 0 };
|
||||
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
|
||||
if (P2(apdu) != 0x0 && P2(apdu) != 0x1) {
|
||||
return SW_INCORRECT_P1P2();
|
||||
}
|
||||
if (validated == false) {
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
if (asn1_find_tag(&ctxi, TAG_CHALLENGE, &chal) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
res_APDU_size = 0;
|
||||
for (int i = 0; i < MAX_OATH_CRED; i++) {
|
||||
file_t *ef = search_dynamic_file((uint16_t)(EF_OATH_CRED + i));
|
||||
if (file_has_data(ef)) {
|
||||
const uint8_t *ef_data = file_get_data(ef);
|
||||
size_t ef_len = file_get_size(ef);
|
||||
asn1_ctx_t ctxe;
|
||||
asn1_ctx_init((uint8_t *)ef_data, (uint16_t)ef_len, &ctxe);
|
||||
if (asn1_find_tag(&ctxe, TAG_NAME, &name) == false || asn1_find_tag(&ctxe, TAG_KEY, &key) == false) {
|
||||
continue;
|
||||
}
|
||||
res_APDU[res_APDU_size++] = TAG_NAME;
|
||||
res_APDU[res_APDU_size++] = (uint8_t)name.len;
|
||||
memcpy(res_APDU + res_APDU_size, name.data, name.len); res_APDU_size += name.len;
|
||||
if ((key.data[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
|
||||
res_APDU[res_APDU_size++] = TAG_NO_RESPONSE;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = key.data[1];
|
||||
}
|
||||
else if (asn1_find_tag(&ctxe, TAG_PROPERTY, &prop) == true && (prop.data[0] & PROP_TOUCH)) {
|
||||
res_APDU[res_APDU_size++] = TAG_TOUCH_RESPONSE;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = key.data[1];
|
||||
}
|
||||
else {
|
||||
res_APDU[res_APDU_size++] = TAG_RESPONSE + P2(apdu);
|
||||
int ret = calculate_oath(P2(apdu), key.data, key.len, chal.data, chal.len);
|
||||
if (ret != PICOKEY_OK) {
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = key.data[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
apdu.ne = res_APDU_size;
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int cmd_send_remaining() {
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int cmd_set_otp_pin() {
|
||||
uint8_t hsh[33] = { 0 };
|
||||
file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
|
||||
if (file_has_data(ef_otp_pin)) {
|
||||
return SW_CONDITIONS_NOT_SATISFIED();
|
||||
}
|
||||
asn1_ctx_t ctxi, pw = { 0 };
|
||||
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
|
||||
if (asn1_find_tag(&ctxi, TAG_PASSWORD, &pw) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
hsh[0] = MAX_OTP_COUNTER;
|
||||
double_hash_pin(pw.data, pw.len, hsh + 1);
|
||||
file_put_data(ef_otp_pin, hsh, sizeof(hsh));
|
||||
low_flash_available();
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int cmd_change_otp_pin() {
|
||||
uint8_t hsh[33] = { 0 };
|
||||
file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
|
||||
if (!file_has_data(ef_otp_pin)) {
|
||||
return SW_CONDITIONS_NOT_SATISFIED();
|
||||
}
|
||||
asn1_ctx_t ctxi, pw = { 0 }, new_pw = { 0 };
|
||||
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
|
||||
if (asn1_find_tag(&ctxi, TAG_PASSWORD, &pw) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
double_hash_pin(pw.data, pw.len, hsh + 1);
|
||||
if (memcmp(file_get_data(ef_otp_pin) + 1, hsh + 1, 32) != 0) {
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
if (asn1_find_tag(&ctxi, TAG_NEW_PASSWORD, &new_pw) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
hsh[0] = MAX_OTP_COUNTER;
|
||||
double_hash_pin(new_pw.data, new_pw.len, hsh + 1);
|
||||
file_put_data(ef_otp_pin, hsh, sizeof(hsh));
|
||||
low_flash_available();
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int cmd_verify_otp_pin() {
|
||||
uint8_t hsh[33] = { 0 }, data_hsh[33];
|
||||
file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
|
||||
if (!file_has_data(ef_otp_pin)) {
|
||||
return SW_CONDITIONS_NOT_SATISFIED();
|
||||
}
|
||||
asn1_ctx_t ctxi, pw = { 0 };
|
||||
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
|
||||
if (asn1_find_tag(&ctxi, TAG_PASSWORD, &pw) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
double_hash_pin(pw.data, pw.len, hsh + 1);
|
||||
memcpy(data_hsh, file_get_data(ef_otp_pin), sizeof(data_hsh));
|
||||
if (data_hsh[0] == 0 || memcmp(data_hsh + 1, hsh + 1, 32) != 0) {
|
||||
if (data_hsh[0] > 0) {
|
||||
data_hsh[0] -= 1;
|
||||
}
|
||||
file_put_data(ef_otp_pin, data_hsh, sizeof(data_hsh));
|
||||
low_flash_available();
|
||||
validated = false;
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
data_hsh[0] = MAX_OTP_COUNTER;
|
||||
file_put_data(ef_otp_pin, data_hsh, sizeof(data_hsh));
|
||||
low_flash_available();
|
||||
validated = true;
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int cmd_verify_hotp() {
|
||||
asn1_ctx_t ctxi, key = { 0 }, chal = { 0 }, name = { 0 }, code = { 0 };
|
||||
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
|
||||
uint32_t code_int = 0;
|
||||
if (asn1_find_tag(&ctxi, TAG_NAME, &name) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
file_t *ef = find_oath_cred(name.data, name.len);
|
||||
if (file_has_data(ef) == false) {
|
||||
return SW_DATA_INVALID();
|
||||
}
|
||||
asn1_ctx_t ctxe;
|
||||
asn1_ctx_init(file_get_data(ef), file_get_size(ef), &ctxe);
|
||||
if (asn1_find_tag(&ctxe, TAG_KEY, &key) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
|
||||
if ((key.data[0] & OATH_TYPE_MASK) != OATH_TYPE_HOTP) {
|
||||
return SW_DATA_INVALID();
|
||||
}
|
||||
if (asn1_find_tag(&ctxe, TAG_IMF, &chal) == false) {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
if (asn1_find_tag(&ctxi, TAG_RESPONSE, &code) == true) {
|
||||
code_int = get_uint32_t_be(code.data);
|
||||
}
|
||||
|
||||
int ret = calculate_oath(0x01, key.data, key.len, chal.data, chal.len);
|
||||
if (ret != PICOKEY_OK) {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
uint32_t res_int = get_uint32_t_be(res_APDU + 2);
|
||||
if (res_APDU[1] == 6) {
|
||||
res_int %= (uint32_t) 1e6;
|
||||
}
|
||||
else {
|
||||
res_int %= (uint32_t) 1e8;
|
||||
}
|
||||
if (res_int != code_int) {
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
res_APDU_size = 0;
|
||||
apdu.ne = 0;
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int cmd_rename() {
|
||||
asn1_ctx_t ctxi, name = { 0 }, new_name = { 0 };
|
||||
|
||||
if (validated == false) {
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
if (apdu.data[0] != TAG_NAME) {
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
|
||||
if (asn1_find_tag(&ctxi, TAG_NAME, &name) == false) {
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
|
||||
asn1_ctx_init(name.data + name.len, (uint16_t)(apdu.nc - (name.data + name.len - apdu.data)), &ctxi);
|
||||
if (asn1_find_tag(&ctxi, TAG_NAME, &new_name) == false) {
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
if (memcmp(name.data, new_name.data, name.len) == 0) {
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
file_t *ef = find_oath_cred(name.data, name.len);
|
||||
if (file_has_data(ef) == false) {
|
||||
return SW_DATA_INVALID();
|
||||
}
|
||||
uint8_t *fdata = file_get_data(ef);
|
||||
uint16_t fsize = file_get_size(ef);
|
||||
asn1_ctx_init(fdata, fsize, &ctxi);
|
||||
if (asn1_find_tag(&ctxi, TAG_NAME, &name) == false) {
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
uint8_t *new_data = (uint8_t *) calloc(sizeof(uint8_t), fsize + new_name.len - name.len);
|
||||
memcpy(new_data, fdata, name.data - fdata);
|
||||
*(new_data + (name.data - fdata) - 1) = new_name.len;
|
||||
memcpy(new_data + (name.data - fdata), new_name.data, new_name.len);
|
||||
memcpy(new_data + (name.data - fdata) + new_name.len, name.data + name.len, fsize - (name.data + name.len - fdata));
|
||||
file_put_data(ef, new_data, fsize + new_name.len - name.len);
|
||||
low_flash_available();
|
||||
free(new_data);
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
#define INS_PUT 0x01
|
||||
#define INS_DELETE 0x02
|
||||
#define INS_SET_CODE 0x03
|
||||
#define INS_RESET 0x04
|
||||
#define INS_RENAME 0x05
|
||||
#define INS_LIST 0xa1
|
||||
#define INS_CALCULATE 0xa2
|
||||
#define INS_VALIDATE 0xa3
|
||||
#define INS_CALC_ALL 0xa4
|
||||
#define INS_SEND_REMAINING 0xa5
|
||||
#define INS_VERIFY_CODE 0xb1
|
||||
#define INS_VERIFY_PIN 0xb2
|
||||
#define INS_CHANGE_PIN 0xb3
|
||||
#define INS_SET_PIN 0xb4
|
||||
|
||||
static const cmd_t cmds[] = {
|
||||
{ INS_PUT, cmd_put },
|
||||
{ INS_DELETE, cmd_delete },
|
||||
{ INS_SET_CODE, cmd_set_code },
|
||||
{ INS_RESET, cmd_reset },
|
||||
{ INS_RENAME, cmd_rename },
|
||||
{ INS_LIST, cmd_list },
|
||||
{ INS_VALIDATE, cmd_validate },
|
||||
{ INS_CALCULATE, cmd_calculate },
|
||||
{ INS_CALC_ALL, cmd_calculate_all },
|
||||
{ INS_SEND_REMAINING, cmd_send_remaining },
|
||||
{ INS_SET_PIN, cmd_set_otp_pin },
|
||||
{ INS_CHANGE_PIN, cmd_change_otp_pin },
|
||||
{ INS_VERIFY_PIN, cmd_verify_otp_pin },
|
||||
{ INS_VERIFY_CODE, cmd_verify_hotp },
|
||||
{ 0x00, 0x0 }
|
||||
};
|
||||
|
||||
int oath_process_apdu() {
|
||||
if (CLA(apdu) != 0x00) {
|
||||
return SW_CLA_NOT_SUPPORTED();
|
||||
}
|
||||
if (cap_supported(CAP_OATH)) {
|
||||
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
|
||||
if (cmd->ins == INS(apdu)) {
|
||||
int r = cmd->cmd_handler();
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
return SW_INS_NOT_SUPPORTED();
|
||||
}
|
||||
674
src/fido/otp.c
Normal file
674
src/fido/otp.c
Normal file
@@ -0,0 +1,674 @@
|
||||
/*
|
||||
* This file is part of the Pico FIDO distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "fido.h"
|
||||
#include "pico_keys.h"
|
||||
#include "apdu.h"
|
||||
#include "files.h"
|
||||
#include "random.h"
|
||||
#include "version.h"
|
||||
#include "asn1.h"
|
||||
#include "hid/ctap_hid.h"
|
||||
#include "usb.h"
|
||||
#if !defined(ENABLE_EMULATION) && !defined(ESP_PLATFORM)
|
||||
#include "bsp/board.h"
|
||||
#endif
|
||||
#include "mbedtls/aes.h"
|
||||
#include "management.h"
|
||||
#ifndef ENABLE_EMULATION
|
||||
#include "tusb.h"
|
||||
#endif
|
||||
|
||||
#define FIXED_SIZE 16
|
||||
#define KEY_SIZE 16
|
||||
#define UID_SIZE 6
|
||||
#define KEY_SIZE_OATH 20
|
||||
#define ACC_CODE_SIZE 6
|
||||
|
||||
#define CONFIG1_VALID 0x01
|
||||
#define CONFIG2_VALID 0x02
|
||||
#define CONFIG1_TOUCH 0x04
|
||||
#define CONFIG2_TOUCH 0x08
|
||||
#define CONFIG_LED_INV 0x10
|
||||
#define CONFIG_STATUS_MASK 0x1f
|
||||
|
||||
/* EXT Flags */
|
||||
#define SERIAL_BTN_VISIBLE 0x01 // Serial number visible at startup (button press)
|
||||
#define SERIAL_USB_VISIBLE 0x02 // Serial number visible in USB iSerial field
|
||||
#define SERIAL_API_VISIBLE 0x04 // Serial number visible via API call
|
||||
#define USE_NUMERIC_KEYPAD 0x08 // Use numeric keypad for digits
|
||||
#define FAST_TRIG 0x10 // Use fast trig if only cfg1 set
|
||||
#define ALLOW_UPDATE 0x20 // Allow update of existing configuration (selected flags + access code)
|
||||
#define DORMANT 0x40 // Dormant config (woken up, flag removed, requires update flag)
|
||||
#define LED_INV 0x80 // LED idle state is off rather than on
|
||||
#define EXTFLAG_UPDATE_MASK (SERIAL_BTN_VISIBLE | SERIAL_USB_VISIBLE | SERIAL_API_VISIBLE | \
|
||||
USE_NUMERIC_KEYPAD | FAST_TRIG | ALLOW_UPDATE | DORMANT | LED_INV)
|
||||
|
||||
/* TKT Flags */
|
||||
#define TAB_FIRST 0x01 // Send TAB before first part
|
||||
#define APPEND_TAB1 0x02 // Send TAB after first part
|
||||
#define APPEND_TAB2 0x04 // Send TAB after second part
|
||||
#define APPEND_DELAY1 0x08 // Add 0.5s delay after first part
|
||||
#define APPEND_DELAY2 0x10 // Add 0.5s delay after second part
|
||||
#define APPEND_CR 0x20 // Append CR as final character
|
||||
#define OATH_HOTP 0x40 // OATH HOTP mode
|
||||
#define CHAL_RESP 0x40 // Challenge-response enabled (both must be set)
|
||||
#define PROTECT_CFG2 0x80 // Block update of config 2 unless config 2 is configured and has this bit set
|
||||
#define TKTFLAG_UPDATE_MASK (TAB_FIRST | APPEND_TAB1 | APPEND_TAB2 | APPEND_DELAY1 | APPEND_DELAY2 | \
|
||||
APPEND_CR)
|
||||
|
||||
/* CFG Flags */
|
||||
#define SEND_REF 0x01 // Send reference string (0..F) before data
|
||||
#define PACING_10MS 0x04 // Add 10ms intra-key pacing
|
||||
#define PACING_20MS 0x08 // Add 20ms intra-key pacing
|
||||
#define STATIC_TICKET 0x20 // Static ticket generation
|
||||
// Static
|
||||
#define SHORT_TICKET 0x02 // Send truncated ticket (half length)
|
||||
#define STRONG_PW1 0x10 // Strong password policy flag #1 (mixed case)
|
||||
#define STRONG_PW2 0x40 // Strong password policy flag #2 (subtitute 0..7 to digits)
|
||||
#define MAN_UPDATE 0x80 // Allow manual (local) update of static OTP
|
||||
// Challenge (no keyboard)
|
||||
#define HMAC_LT64 0x04 // Set when HMAC message is less than 64 bytes
|
||||
#define CHAL_BTN_TRIG 0x08 // Challenge-response operation requires button press
|
||||
#define CHAL_YUBICO 0x20 // Challenge-response enabled - Yubico OTP mode
|
||||
#define CHAL_HMAC 0x22 // Challenge-response enabled - HMAC-SHA1
|
||||
// OATH
|
||||
#define OATH_HOTP8 0x02 // Generate 8 digits HOTP rather than 6 digits
|
||||
#define OATH_FIXED_MODHEX1 0x10 // First byte in fixed part sent as modhex
|
||||
#define OATH_FIXED_MODHEX2 0x40 // First two bytes in fixed part sent as modhex
|
||||
#define OATH_FIXED_MODHEX 0x50 // Fixed part sent as modhex
|
||||
#define OATH_FIXED_MASK 0x50 // Mask to get out fixed flags
|
||||
#define CFGFLAG_UPDATE_MASK (PACING_10MS | PACING_20MS)
|
||||
|
||||
static uint8_t config_seq = { 1 };
|
||||
|
||||
PACK(
|
||||
typedef struct otp_config {
|
||||
uint8_t fixed_data[FIXED_SIZE];
|
||||
uint8_t uid[UID_SIZE];
|
||||
uint8_t aes_key[KEY_SIZE];
|
||||
uint8_t acc_code[ACC_CODE_SIZE];
|
||||
uint8_t fixed_size;
|
||||
uint8_t ext_flags;
|
||||
uint8_t tkt_flags;
|
||||
uint8_t cfg_flags;
|
||||
uint8_t rfu[2];
|
||||
uint16_t crc;
|
||||
}) otp_config_t;
|
||||
|
||||
#define otp_config_size sizeof(otp_config_t)
|
||||
uint16_t otp_status(bool is_otp);
|
||||
|
||||
int otp_process_apdu();
|
||||
int otp_unload();
|
||||
|
||||
#ifndef ENABLE_EMULATION
|
||||
extern int (*hid_set_report_cb)(uint8_t, uint8_t, hid_report_type_t, uint8_t const *, uint16_t);
|
||||
extern uint16_t (*hid_get_report_cb)(uint8_t, uint8_t, hid_report_type_t, uint8_t *, uint16_t);
|
||||
int otp_hid_set_report_cb(uint8_t, uint8_t, hid_report_type_t, uint8_t const *, uint16_t);
|
||||
uint16_t otp_hid_get_report_cb(uint8_t, uint8_t, hid_report_type_t, uint8_t *, uint16_t);
|
||||
#endif
|
||||
|
||||
const uint8_t otp_aid[] = {
|
||||
7,
|
||||
0xa0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01
|
||||
};
|
||||
|
||||
int otp_select(app_t *a, uint8_t force) {
|
||||
(void) force;
|
||||
if (cap_supported(CAP_OTP)) {
|
||||
a->process_apdu = otp_process_apdu;
|
||||
a->unload = otp_unload;
|
||||
if (file_has_data(search_dynamic_file(EF_OTP_SLOT1)) ||
|
||||
file_has_data(search_dynamic_file(EF_OTP_SLOT2))) {
|
||||
config_seq = 1;
|
||||
}
|
||||
else {
|
||||
config_seq = 0;
|
||||
}
|
||||
otp_status(false);
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
return PICOKEY_ERR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
uint8_t modhex_tab[] =
|
||||
{ 'c', 'b', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'r', 't', 'u', 'v' };
|
||||
int encode_modhex(const uint8_t *in, size_t len, uint8_t *out) {
|
||||
for (size_t l = 0; l < len; l++) {
|
||||
*out++ = modhex_tab[in[l] >> 4];
|
||||
*out++ = modhex_tab[in[l] & 0xf];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static bool scanned = false;
|
||||
extern void scan_all();
|
||||
void init_otp() {
|
||||
if (scanned == false) {
|
||||
scan_all();
|
||||
for (uint8_t i = 0; i < 2; i++) {
|
||||
file_t *ef = search_dynamic_file(EF_OTP_SLOT1 + i);
|
||||
uint8_t *data = file_get_data(ef);
|
||||
otp_config_t *otp_config = (otp_config_t *) data;
|
||||
if (file_has_data(ef) && !(otp_config->tkt_flags & OATH_HOTP) &&
|
||||
!(otp_config->cfg_flags & SHORT_TICKET || otp_config->cfg_flags & STATIC_TICKET)) {
|
||||
uint16_t counter = get_uint16_t_be(data + otp_config_size);
|
||||
if (++counter <= 0x7fff) {
|
||||
uint8_t new_data[otp_config_size + 8];
|
||||
memcpy(new_data, data, sizeof(new_data));
|
||||
put_uint16_t_be(counter, new_data + otp_config_size);
|
||||
file_put_data(ef, new_data, sizeof(new_data));
|
||||
}
|
||||
}
|
||||
}
|
||||
scanned = true;
|
||||
low_flash_available();
|
||||
}
|
||||
}
|
||||
extern int calculate_oath(uint8_t truncate,
|
||||
const uint8_t *key,
|
||||
size_t key_len,
|
||||
const uint8_t *chal,
|
||||
size_t chal_len);
|
||||
|
||||
uint16_t calculate_crc(const uint8_t *data, size_t data_len) {
|
||||
uint16_t crc = 0xFFFF;
|
||||
for (size_t idx = 0; idx < data_len; idx++) {
|
||||
crc ^= data[idx];
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
uint16_t j = crc & 0x1;
|
||||
crc >>= 1;
|
||||
if (j == 1) {
|
||||
crc ^= 0x8408;
|
||||
}
|
||||
}
|
||||
}
|
||||
return crc & 0xFFFF;
|
||||
}
|
||||
|
||||
#ifndef ENABLE_EMULATION
|
||||
static uint8_t session_counter[2] = { 0 };
|
||||
#endif
|
||||
int otp_button_pressed(uint8_t slot) {
|
||||
init_otp();
|
||||
if (!cap_supported(CAP_OTP)) {
|
||||
return 3;
|
||||
}
|
||||
#ifndef ENABLE_EMULATION
|
||||
file_t *ef = search_dynamic_file(slot == 1 ? EF_OTP_SLOT1 : EF_OTP_SLOT2);
|
||||
const uint8_t *data = file_get_data(ef);
|
||||
otp_config_t *otp_config = (otp_config_t *) data;
|
||||
if (file_has_data(ef) == false) {
|
||||
return 1;
|
||||
}
|
||||
if (otp_config->cfg_flags & CHAL_YUBICO && otp_config->tkt_flags & CHAL_RESP) {
|
||||
return 2;
|
||||
}
|
||||
if (otp_config->tkt_flags & OATH_HOTP) {
|
||||
uint8_t tmp_key[KEY_SIZE + 2];
|
||||
tmp_key[0] = 0x01;
|
||||
memcpy(tmp_key + 2, otp_config->aes_key, KEY_SIZE);
|
||||
uint64_t imf = 0;
|
||||
const uint8_t *p = data + otp_config_size;
|
||||
imf = get_uint64_t_be(p);
|
||||
p += 8;
|
||||
if (imf == 0) {
|
||||
imf = get_uint16_t_be(otp_config->uid + 4);
|
||||
}
|
||||
uint8_t chal[8];
|
||||
put_uint64_t_be(imf, chal);
|
||||
res_APDU_size = 0;
|
||||
int ret = calculate_oath(1, tmp_key, sizeof(tmp_key), chal, sizeof(chal));
|
||||
if (ret == PICOKEY_OK) {
|
||||
uint32_t base = otp_config->cfg_flags & OATH_HOTP8 ? 1e8 : 1e6;
|
||||
uint32_t number = get_uint16_t_be(res_APDU + 2);
|
||||
number %= base;
|
||||
char number_str[9];
|
||||
if (otp_config->cfg_flags & OATH_HOTP8) {
|
||||
sprintf(number_str, "%08lu", (long unsigned int) number);
|
||||
add_keyboard_buffer((const uint8_t *) number_str, 8, true);
|
||||
}
|
||||
else {
|
||||
sprintf(number_str, "%06lu", (long unsigned int) number);
|
||||
add_keyboard_buffer((const uint8_t *) number_str, 6, true);
|
||||
}
|
||||
imf++;
|
||||
uint8_t new_chal[8];
|
||||
put_uint64_t_be(imf, new_chal);
|
||||
uint8_t new_otp_config[otp_config_size + sizeof(new_chal)];
|
||||
memcpy(new_otp_config, otp_config, otp_config_size);
|
||||
memcpy(new_otp_config + otp_config_size, new_chal, sizeof(new_chal));
|
||||
file_put_data(ef, new_otp_config, sizeof(new_otp_config));
|
||||
low_flash_available();
|
||||
}
|
||||
if (otp_config->tkt_flags & APPEND_CR) {
|
||||
append_keyboard_buffer((const uint8_t *) "\r", 1);
|
||||
}
|
||||
}
|
||||
else if (otp_config->cfg_flags & SHORT_TICKET || otp_config->cfg_flags & STATIC_TICKET) {
|
||||
uint8_t fixed_size = FIXED_SIZE + UID_SIZE + KEY_SIZE;
|
||||
if (otp_config->cfg_flags & SHORT_TICKET) { // Not clear which is the purpose of SHORT_TICKET
|
||||
//fixed_size /= 2;
|
||||
}
|
||||
add_keyboard_buffer(otp_config->fixed_data, fixed_size, false);
|
||||
if (otp_config->tkt_flags & APPEND_CR) {
|
||||
append_keyboard_buffer((const uint8_t *) "\x28", 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
uint8_t otpk[22], *po = otpk;
|
||||
bool update_counter = false;
|
||||
uint16_t counter = get_uint16_t_be(data + otp_config_size), crc = 0;
|
||||
uint32_t ts = board_millis() / 1000;
|
||||
if (counter == 0) {
|
||||
update_counter = true;
|
||||
counter = 1;
|
||||
}
|
||||
memcpy(po, otp_config->fixed_data, 6);
|
||||
po += 6;
|
||||
memcpy(po, otp_config->uid, UID_SIZE);
|
||||
po += UID_SIZE;
|
||||
po += put_uint16_t_le(counter, po);
|
||||
ts >>= 1;
|
||||
*po++ = ts & 0xff;
|
||||
*po++ = ts >> 8;
|
||||
*po++ = ts >> 16;
|
||||
*po++ = session_counter[slot - 1];
|
||||
random_gen(NULL, po, 2);
|
||||
po += 2;
|
||||
crc = calculate_crc(otpk + 6, 14);
|
||||
po += put_uint16_t_le(~crc, po);
|
||||
mbedtls_aes_context ctx;
|
||||
mbedtls_aes_init(&ctx);
|
||||
mbedtls_aes_setkey_enc(&ctx, otp_config->aes_key, 128);
|
||||
mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, otpk + 6, otpk + 6);
|
||||
mbedtls_aes_free(&ctx);
|
||||
uint8_t otp_out[44];
|
||||
encode_modhex(otpk, sizeof(otpk), otp_out);
|
||||
add_keyboard_buffer((const uint8_t *) otp_out, sizeof(otp_out), true);
|
||||
if (otp_config->tkt_flags & APPEND_CR) {
|
||||
append_keyboard_buffer((const uint8_t *) "\r", 1);
|
||||
}
|
||||
|
||||
if (++session_counter[slot - 1] == 0) {
|
||||
if (++counter <= 0x7fff) {
|
||||
update_counter = true;
|
||||
}
|
||||
}
|
||||
if (update_counter == true) {
|
||||
uint8_t new_data[otp_config_size + 8];
|
||||
memcpy(new_data, data, sizeof(new_data));
|
||||
put_uint16_t_be(counter, new_data + otp_config_size);
|
||||
file_put_data(ef, new_data, sizeof(new_data));
|
||||
low_flash_available();
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void) slot;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
INITIALIZER( otp_ctor ) {
|
||||
register_app(otp_select, otp_aid);
|
||||
button_pressed_cb = otp_button_pressed;
|
||||
#ifndef ENABLE_EMULATION
|
||||
hid_set_report_cb = otp_hid_set_report_cb;
|
||||
hid_get_report_cb = otp_hid_get_report_cb;
|
||||
#endif
|
||||
}
|
||||
|
||||
int otp_unload() {
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
uint8_t status_byte = 0x0;
|
||||
uint16_t otp_status(bool is_otp) {
|
||||
if (scanned == false) {
|
||||
scan_all();
|
||||
scanned = true;
|
||||
}
|
||||
res_APDU_size = 0;
|
||||
if (is_otp) {
|
||||
res_APDU_size++;
|
||||
}
|
||||
res_APDU[res_APDU_size++] = PICO_FIDO_VERSION_MAJOR;
|
||||
res_APDU[res_APDU_size++] = PICO_FIDO_VERSION_MINOR;
|
||||
res_APDU[res_APDU_size++] = 0;
|
||||
res_APDU[res_APDU_size++] = config_seq;
|
||||
uint8_t opts = 0;
|
||||
file_t *ef = search_dynamic_file(EF_OTP_SLOT1);
|
||||
if (file_has_data(ef)) {
|
||||
opts |= CONFIG1_VALID;
|
||||
otp_config_t *otp_config = (otp_config_t *) file_get_data(ef);
|
||||
if (!(otp_config->tkt_flags & CHAL_RESP) || otp_config->cfg_flags & CHAL_BTN_TRIG) {
|
||||
opts |= CONFIG1_TOUCH;
|
||||
}
|
||||
}
|
||||
ef = search_dynamic_file(EF_OTP_SLOT2);
|
||||
if (file_has_data(ef)) {
|
||||
opts |= CONFIG2_VALID;
|
||||
otp_config_t *otp_config = (otp_config_t *) file_get_data(ef);
|
||||
if (!(otp_config->tkt_flags & CHAL_RESP) || otp_config->cfg_flags & CHAL_BTN_TRIG) {
|
||||
opts |= CONFIG2_TOUCH;
|
||||
}
|
||||
}
|
||||
res_APDU[res_APDU_size++] = opts;
|
||||
res_APDU[res_APDU_size++] = 0;
|
||||
res_APDU[res_APDU_size++] = status_byte;
|
||||
if (is_otp) {
|
||||
res_APDU_size = 0;
|
||||
}
|
||||
else {
|
||||
apdu.ne = res_APDU_size;
|
||||
}
|
||||
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
bool check_crc(const otp_config_t *data) {
|
||||
uint16_t crc = calculate_crc((const uint8_t *) data, otp_config_size);
|
||||
return crc == 0xF0B8;
|
||||
}
|
||||
|
||||
bool _is_otp = false;
|
||||
int cmd_otp() {
|
||||
uint8_t p1 = P1(apdu), p2 = P2(apdu);
|
||||
if (p2 != 0x00) {
|
||||
return SW_INCORRECT_P1P2();
|
||||
}
|
||||
if (p1 == 0x01 || p1 == 0x03) { // Configure slot
|
||||
otp_config_t *odata = (otp_config_t *) apdu.data;
|
||||
file_t *ef = file_new(p1 == 0x01 ? EF_OTP_SLOT1 : EF_OTP_SLOT2);
|
||||
if (file_has_data(ef)) {
|
||||
otp_config_t *otpc = (otp_config_t *) file_get_data(ef);
|
||||
if (memcmp(otpc->acc_code, apdu.data + otp_config_size, ACC_CODE_SIZE) != 0) {
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
}
|
||||
for (int c = 0; c < otp_config_size; c++) {
|
||||
if (apdu.data[c] != 0) {
|
||||
if (odata->rfu[0] != 0 || odata->rfu[1] != 0 || check_crc(odata) == false) {
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
memset(apdu.data + otp_config_size, 0, 8); // Add 8 bytes extra
|
||||
file_put_data(ef, apdu.data, otp_config_size + 8);
|
||||
low_flash_available();
|
||||
config_seq++;
|
||||
return otp_status(_is_otp);
|
||||
}
|
||||
}
|
||||
// Delete slot
|
||||
delete_file(ef);
|
||||
config_seq++;
|
||||
return otp_status(_is_otp);
|
||||
}
|
||||
else if (p1 == 0x04 || p1 == 0x05) { // Update slot
|
||||
otp_config_t *odata = (otp_config_t *) apdu.data;
|
||||
if (odata->rfu[0] != 0 || odata->rfu[1] != 0 || check_crc(odata) == false) {
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
file_t *ef = search_dynamic_file(p1 == 0x04 ? EF_OTP_SLOT1 : EF_OTP_SLOT2);
|
||||
if (file_has_data(ef)) {
|
||||
otp_config_t *otpc = (otp_config_t *) file_get_data(ef);
|
||||
if (memcmp(otpc->acc_code, apdu.data + otp_config_size, ACC_CODE_SIZE) != 0) {
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
memcpy(apdu.data, file_get_data(ef), FIXED_SIZE + UID_SIZE + KEY_SIZE);
|
||||
odata->fixed_size = otpc->fixed_size;
|
||||
odata->ext_flags = (otpc->ext_flags & ~EXTFLAG_UPDATE_MASK) |
|
||||
(odata->ext_flags & EXTFLAG_UPDATE_MASK);
|
||||
odata->tkt_flags = (otpc->tkt_flags & ~TKTFLAG_UPDATE_MASK) |
|
||||
(odata->tkt_flags & TKTFLAG_UPDATE_MASK);
|
||||
if (!(otpc->tkt_flags & CHAL_RESP)) {
|
||||
odata->cfg_flags = (otpc->cfg_flags & ~CFGFLAG_UPDATE_MASK) |
|
||||
(odata->cfg_flags & CFGFLAG_UPDATE_MASK);
|
||||
}
|
||||
else {
|
||||
odata->cfg_flags = otpc->cfg_flags;
|
||||
}
|
||||
file_put_data(ef, apdu.data, otp_config_size);
|
||||
low_flash_available();
|
||||
config_seq++;
|
||||
}
|
||||
return otp_status(_is_otp);
|
||||
}
|
||||
else if (p1 == 0x06) { // Swap slots
|
||||
uint8_t tmp[otp_config_size + 8];
|
||||
bool ef1_data = false;
|
||||
file_t *ef1 = file_new(EF_OTP_SLOT1);
|
||||
file_t *ef2 = file_new(EF_OTP_SLOT2);
|
||||
if (file_has_data(ef1)) {
|
||||
memcpy(tmp, file_get_data(ef1), file_get_size(ef1));
|
||||
ef1_data = true;
|
||||
}
|
||||
if (file_has_data(ef2)) {
|
||||
file_put_data(ef1, file_get_data(ef2), file_get_size(ef2));
|
||||
}
|
||||
else {
|
||||
delete_file(ef1);
|
||||
// When a dynamic file is deleted, existing referenes are invalidated
|
||||
ef2 = file_new(EF_OTP_SLOT2);
|
||||
}
|
||||
if (ef1_data) {
|
||||
file_put_data(ef2, tmp, sizeof(tmp));
|
||||
}
|
||||
else {
|
||||
delete_file(ef2);
|
||||
}
|
||||
low_flash_available();
|
||||
config_seq++;
|
||||
return otp_status(_is_otp);
|
||||
}
|
||||
else if (p1 == 0x10) {
|
||||
memcpy(res_APDU, pico_serial.id, 4);
|
||||
res_APDU_size = 4;
|
||||
}
|
||||
else if (p1 == 0x13) { // Get config
|
||||
man_get_config();
|
||||
}
|
||||
else if (p1 == 0x30 || p1 == 0x38 || p1 == 0x20 || p1 == 0x28) { // Calculate OTP
|
||||
file_t *ef = search_dynamic_file(p1 == 0x30 || p1 == 0x20 ? EF_OTP_SLOT1 : EF_OTP_SLOT2);
|
||||
if (file_has_data(ef)) {
|
||||
otp_config_t *otp_config = (otp_config_t *) file_get_data(ef);
|
||||
if (!(otp_config->tkt_flags & CHAL_RESP)) {
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
int ret = 0;
|
||||
#ifndef ENABLE_EMULATION
|
||||
uint8_t *rdata_bk = apdu.rdata;
|
||||
if (otp_config->cfg_flags & CHAL_BTN_TRIG) {
|
||||
status_byte = 0x20;
|
||||
otp_status(_is_otp);
|
||||
if (wait_button() == true) {
|
||||
status_byte = 0x00;
|
||||
otp_status(_is_otp);
|
||||
return SW_CONDITIONS_NOT_SATISFIED();
|
||||
}
|
||||
status_byte = 0x10;
|
||||
apdu.rdata = rdata_bk;
|
||||
}
|
||||
#endif
|
||||
if (p1 == 0x30 || p1 == 0x38) {
|
||||
if (!(otp_config->cfg_flags & CHAL_HMAC)) {
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
uint8_t aes_key[KEY_SIZE + UID_SIZE];
|
||||
memcpy(aes_key, otp_config->aes_key, KEY_SIZE);
|
||||
memcpy(aes_key + KEY_SIZE, otp_config->uid, UID_SIZE);
|
||||
uint8_t chal_len = 64;
|
||||
if (otp_config->cfg_flags & HMAC_LT64) {
|
||||
while (chal_len > 0 && apdu.data[63] == apdu.data[chal_len - 1]) {
|
||||
chal_len--;
|
||||
}
|
||||
}
|
||||
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), aes_key, sizeof(aes_key), apdu.data, chal_len, res_APDU);
|
||||
if (ret == 0) {
|
||||
res_APDU_size = 20;
|
||||
}
|
||||
}
|
||||
else if (p1 == 0x20 || p1 == 0x28) {
|
||||
if (!(otp_config->cfg_flags & CHAL_YUBICO)) {
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
uint8_t challenge[16];
|
||||
memcpy(challenge, apdu.data, 6);
|
||||
memcpy(challenge + 6, pico_serial_str, 10);
|
||||
mbedtls_aes_context ctx;
|
||||
mbedtls_aes_init(&ctx);
|
||||
mbedtls_aes_setkey_enc(&ctx, otp_config->aes_key, 128);
|
||||
ret = mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, challenge, res_APDU);
|
||||
mbedtls_aes_free(&ctx);
|
||||
if (ret == 0) {
|
||||
res_APDU_size = 16;
|
||||
}
|
||||
}
|
||||
if (ret == 0) {
|
||||
status_byte = 0x00;
|
||||
}
|
||||
}
|
||||
}
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
#define INS_OTP 0x01
|
||||
|
||||
static const cmd_t cmds[] = {
|
||||
{ INS_OTP, cmd_otp },
|
||||
{ 0x00, 0x0 }
|
||||
};
|
||||
|
||||
int otp_process_apdu() {
|
||||
if (CLA(apdu) != 0x00) {
|
||||
return SW_CLA_NOT_SUPPORTED();
|
||||
}
|
||||
if (cap_supported(CAP_OTP)) {
|
||||
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
|
||||
if (cmd->ins == INS(apdu)) {
|
||||
int r = cmd->cmd_handler();
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
return SW_INS_NOT_SUPPORTED();
|
||||
}
|
||||
|
||||
#ifndef ENABLE_EMULATION
|
||||
|
||||
uint8_t otp_frame_rx[70] = {0};
|
||||
uint8_t otp_frame_tx[70] = {0};
|
||||
uint8_t otp_exp_seq = 0, otp_curr_seq = 0;
|
||||
uint8_t otp_header[4] = {0};
|
||||
|
||||
extern uint16_t *get_send_buffer_size(uint8_t itf);
|
||||
|
||||
int otp_send_frame(uint8_t *frame, size_t frame_len) {
|
||||
uint16_t crc = calculate_crc(frame, frame_len);
|
||||
frame_len += put_uint16_t_le(~crc, frame + frame_len);
|
||||
*get_send_buffer_size(ITF_KEYBOARD) = frame_len;
|
||||
otp_exp_seq = (frame_len / 7);
|
||||
if (frame_len % 7) {
|
||||
otp_exp_seq++;
|
||||
}
|
||||
otp_curr_seq = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int otp_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {
|
||||
if (report_type == 3) {
|
||||
DEBUG_PAYLOAD(buffer, bufsize);
|
||||
if (buffer[7] == 0xFF) { // reset
|
||||
*get_send_buffer_size(ITF_KEYBOARD) = 0;
|
||||
otp_curr_seq = otp_exp_seq = 0;
|
||||
memset(otp_frame_tx, 0, sizeof(otp_frame_tx));
|
||||
}
|
||||
else if (buffer[7] & 0x80) { // a frame
|
||||
uint8_t rseq = buffer[7] & 0x1F;
|
||||
if (rseq < 10) {
|
||||
if (rseq == 0) {
|
||||
memset(otp_frame_rx, 0, sizeof(otp_frame_rx));
|
||||
}
|
||||
memcpy(otp_frame_rx + rseq * 7, buffer, 7);
|
||||
if (rseq == 9) {
|
||||
DEBUG_DATA(otp_frame_rx, sizeof(otp_frame_rx));
|
||||
DEBUG_PAYLOAD(otp_frame_rx, sizeof(otp_frame_rx));
|
||||
uint16_t residual_crc = calculate_crc(otp_frame_rx, 64), rcrc = get_uint16_t_le(otp_frame_rx + 65);
|
||||
uint8_t slot_id = otp_frame_rx[64];
|
||||
if (residual_crc == rcrc) {
|
||||
uint8_t hdr[5];
|
||||
apdu.header = hdr;
|
||||
apdu.data = otp_frame_rx;
|
||||
apdu.nc = 64;
|
||||
apdu.rdata = otp_frame_tx;
|
||||
apdu.header[0] = 0;
|
||||
apdu.header[1] = 0x01;
|
||||
apdu.header[2] = slot_id;
|
||||
apdu.header[3] = 0;
|
||||
_is_otp = true;
|
||||
int ret = otp_process_apdu();
|
||||
if (ret == 0x9000 && res_APDU_size > 0) {
|
||||
otp_send_frame(apdu.rdata, apdu.rlen);
|
||||
}
|
||||
_is_otp = false;
|
||||
}
|
||||
else {
|
||||
printf("[OTP] Bad CRC!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t otp_hid_get_report_cb(uint8_t itf,
|
||||
uint8_t report_id,
|
||||
hid_report_type_t report_type,
|
||||
uint8_t *buffer,
|
||||
uint16_t reqlen) {
|
||||
// TODO not Implemented
|
||||
(void) itf;
|
||||
(void) report_id;
|
||||
(void) report_type;
|
||||
(void) buffer;
|
||||
(void) reqlen;
|
||||
uint16_t send_buffer_size = *get_send_buffer_size(ITF_KEYBOARD);
|
||||
if (send_buffer_size > 0) {
|
||||
uint8_t seq = otp_curr_seq++;
|
||||
memset(buffer, 0, 8);
|
||||
memcpy(buffer, otp_frame_tx + 7 * seq, MIN(7, send_buffer_size));
|
||||
buffer[7] = 0x40 | seq;
|
||||
DEBUG_DATA(buffer, 8);
|
||||
*get_send_buffer_size(ITF_KEYBOARD) -= MIN(7, send_buffer_size);
|
||||
}
|
||||
else if (otp_curr_seq == otp_exp_seq && otp_exp_seq > 0) {
|
||||
memset(buffer, 0, 7);
|
||||
buffer[7] = 0x40;
|
||||
DEBUG_DATA(buffer,8);
|
||||
otp_curr_seq = otp_exp_seq = 0;
|
||||
}
|
||||
else {
|
||||
res_APDU = buffer;
|
||||
otp_status(true);
|
||||
DEBUG_DATA(buffer, 8);
|
||||
}
|
||||
|
||||
return reqlen;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -18,10 +18,9 @@
|
||||
#ifndef __VERSION_H_
|
||||
#define __VERSION_H_
|
||||
|
||||
#define PICO_FIDO_VERSION 0x020A
|
||||
#define PICO_FIDO_VERSION 0x0606
|
||||
|
||||
#define PICO_FIDO_VERSION_MAJOR ((PICO_FIDO_VERSION >> 8) & 0xff)
|
||||
#define PICO_FIDO_VERSION_MINOR (PICO_FIDO_VERSION & 0xff)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
7
tests/build-in-docker.sh
Executable file
7
tests/build-in-docker.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash -eu
|
||||
|
||||
source tests/docker_env.sh
|
||||
#run_in_docker rm -rf CMakeFiles
|
||||
run_in_docker mkdir -p build_in_docker
|
||||
run_in_docker -w "$PWD/build_in_docker" cmake -DENABLE_EMULATION=1 -DENABLE_EDDSA=1 ..
|
||||
run_in_docker -w "$PWD/build_in_docker" make -j ${NUM_PROC}
|
||||
@@ -1,3 +1,23 @@
|
||||
"""
|
||||
/*
|
||||
* This file is part of the Pico Fido distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
from http import client
|
||||
from fido2.hid import CtapHidDevice
|
||||
from fido2.client import Fido2Client, WindowsClient, UserInteraction, ClientError, _Ctap1ClientBackend
|
||||
@@ -5,13 +25,15 @@ from fido2.attestation import FidoU2FAttestation
|
||||
from fido2.ctap2.pin import ClientPin
|
||||
from fido2.server import Fido2Server
|
||||
from fido2.ctap import CtapError
|
||||
from fido2.webauthn import CollectedClientData, AttestedCredentialData
|
||||
from fido2.webauthn import CollectedClientData, PublicKeyCredentialParameters, PublicKeyCredentialType
|
||||
from utils import *
|
||||
from fido2.cose import ES256
|
||||
import sys
|
||||
import pytest
|
||||
import os
|
||||
import struct
|
||||
from inputimeout import inputimeout
|
||||
|
||||
|
||||
DEFAULT_PIN='12345678'
|
||||
|
||||
@@ -94,6 +116,10 @@ class Device():
|
||||
self.__rp = rp
|
||||
self.__attestation = attestation
|
||||
self.__server = Fido2Server(self.__rp, attestation=self.__attestation)
|
||||
self.__server.allowed_algorithms = [
|
||||
PublicKeyCredentialParameters(PublicKeyCredentialType.PUBLIC_KEY, p['alg'])
|
||||
for p in self.__client._backend.info.algorithms
|
||||
]
|
||||
|
||||
def client(self):
|
||||
return self.__client
|
||||
@@ -159,7 +185,11 @@ class Device():
|
||||
|
||||
def reboot(self):
|
||||
print("Please reboot authenticator and hit enter")
|
||||
input()
|
||||
try:
|
||||
inputimeout(prompt='>>', timeout=5)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self.__set_client(self.__origin, self.__user_interaction, self.__uv)
|
||||
self.__set_server(rp=self.__rp, attestation=self.__attestation)
|
||||
|
||||
@@ -335,20 +365,20 @@ def device():
|
||||
dev = Device()
|
||||
return dev
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
@pytest.fixture(scope="module")
|
||||
def info(device):
|
||||
return device.client()._backend.info
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
@pytest.fixture(scope="module")
|
||||
def MCRes(device, *args):
|
||||
return device.doMC(*args)
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
@pytest.fixture(scope="module")
|
||||
def resetdevice(device):
|
||||
device.reset()
|
||||
return device
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
@pytest.fixture(scope="module")
|
||||
def GARes(device, MCRes, *args):
|
||||
res = device.doGA(allow_list=[
|
||||
{"id": MCRes['res'].attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
|
||||
@@ -359,11 +389,11 @@ def GARes(device, MCRes, *args):
|
||||
verify(MCRes['res'].attestation_object, a, res['req']['client_data'].hash)
|
||||
return res
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
@pytest.fixture(scope="module")
|
||||
def MCRes_DC(device, *args):
|
||||
return device.doMC(rk=True, *args)
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
@pytest.fixture(scope="module")
|
||||
def GARes_DC(device, MCRes_DC, *args):
|
||||
res = device.GA(allow_list=[
|
||||
{"id": MCRes_DC['res'].attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
|
||||
@@ -380,7 +410,7 @@ def RegRes(resetdevice, *args):
|
||||
return res
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def AuthRes(device, RegRes, *args):
|
||||
res = device.doGA(ctap1=True, allow_list=[
|
||||
{"id": RegRes['res'].attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
|
||||
@@ -393,3 +423,30 @@ def AuthRes(device, RegRes, *args):
|
||||
@pytest.fixture(scope="class")
|
||||
def client_pin(resetdevice):
|
||||
return ClientPin(resetdevice.client()._backend.ctap2)
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
def ccid_card():
|
||||
cardtype = AnyCardType()
|
||||
try:
|
||||
# request card insertion
|
||||
cardrequest = CardRequest(timeout=10, cardType=cardtype)
|
||||
card = cardrequest.waitforcard()
|
||||
|
||||
# connect to the card and perform a few transmits
|
||||
card.connection.connect()
|
||||
return card
|
||||
|
||||
except CardRequestTimeoutException:
|
||||
print('time-out: no card inserted during last 10s')
|
||||
return None
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
def select_oath(ccid_card):
|
||||
aid = [0xa0, 0x00, 0x00, 0x05, 0x27, 0x21, 0x01, 0x01]
|
||||
resp = send_apdu(ccid_card, 0xA4, 0x04, 0x00, aid)
|
||||
return ccid_card
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
def reset_oath(select_oath):
|
||||
send_apdu(select_oath, 0x04, p1=0xde, p2=0xad)
|
||||
return select_oath
|
||||
|
||||
36
tests/docker/bullseye/Dockerfile
Normal file
36
tests/docker/bullseye/Dockerfile
Normal file
@@ -0,0 +1,36 @@
|
||||
FROM debian:bullseye
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt update && apt upgrade -y
|
||||
RUN apt install -y apt-utils
|
||||
RUN apt install -y libccid \
|
||||
libpcsclite-dev \
|
||||
git \
|
||||
autoconf \
|
||||
pkg-config \
|
||||
libtool \
|
||||
help2man \
|
||||
automake \
|
||||
gcc \
|
||||
make \
|
||||
build-essential \
|
||||
opensc \
|
||||
python3 \
|
||||
python3-pip \
|
||||
swig \
|
||||
cmake \
|
||||
libfuse-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
RUN pip3 install pytest pycvc cryptography pyscard inputimeout
|
||||
RUN git clone https://github.com/polhenarejos/python-fido2.git
|
||||
WORKDIR /python-fido2
|
||||
RUN git checkout development
|
||||
RUN pip3 install .
|
||||
WORKDIR /
|
||||
RUN git clone https://github.com/frankmorgner/vsmartcard.git
|
||||
WORKDIR /vsmartcard/virtualsmartcard
|
||||
RUN autoreconf --verbose --install
|
||||
RUN ./configure --sysconfdir=/etc
|
||||
RUN make && make install
|
||||
WORKDIR /
|
||||
270
tests/docker/fido2/__init__.py
Normal file
270
tests/docker/fido2/__init__.py
Normal file
@@ -0,0 +1,270 @@
|
||||
# Copyright (c) 2020 Yubico AB
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or
|
||||
# without modification, are permitted provided that the following
|
||||
# conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following
|
||||
# disclaimer in the documentation and/or other materials provided
|
||||
# with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from .base import HidDescriptor
|
||||
from ..ctap import CtapDevice, CtapError, STATUS
|
||||
from ..utils import LOG_LEVEL_TRAFFIC
|
||||
from threading import Event
|
||||
from enum import IntEnum, IntFlag, unique
|
||||
from typing import Tuple, Optional, Callable, Iterator
|
||||
import struct
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
if sys.platform.startswith("linux"):
|
||||
from . import linux as backend
|
||||
elif sys.platform.startswith("win32"):
|
||||
from . import windows as backend
|
||||
elif sys.platform.startswith("darwin"):
|
||||
from . import macos as backend
|
||||
elif sys.platform.startswith("freebsd"):
|
||||
from . import freebsd as backend
|
||||
elif sys.platform.startswith("netbsd"):
|
||||
from . import netbsd as backend
|
||||
elif sys.platform.startswith("openbsd"):
|
||||
from . import openbsd as backend
|
||||
else:
|
||||
raise Exception("Unsupported platform")
|
||||
from . import emulation as backend
|
||||
|
||||
list_descriptors = backend.list_descriptors
|
||||
get_descriptor = backend.get_descriptor
|
||||
open_connection = backend.open_connection
|
||||
|
||||
|
||||
@unique
|
||||
class CTAPHID(IntEnum):
|
||||
PING = 0x01
|
||||
MSG = 0x03
|
||||
LOCK = 0x04
|
||||
INIT = 0x06
|
||||
WINK = 0x08
|
||||
CBOR = 0x10
|
||||
CANCEL = 0x11
|
||||
|
||||
ERROR = 0x3F
|
||||
KEEPALIVE = 0x3B
|
||||
|
||||
VENDOR_FIRST = 0x40
|
||||
|
||||
|
||||
@unique
|
||||
class CAPABILITY(IntFlag):
|
||||
WINK = 0x01
|
||||
LOCK = 0x02 # Not used
|
||||
CBOR = 0x04
|
||||
NMSG = 0x08
|
||||
|
||||
def supported(self, flags: CAPABILITY) -> bool:
|
||||
return bool(flags & self)
|
||||
|
||||
|
||||
TYPE_INIT = 0x80
|
||||
|
||||
|
||||
class CtapHidDevice(CtapDevice):
|
||||
"""
|
||||
CtapDevice implementation using the HID transport.
|
||||
|
||||
:cvar descriptor: Device descriptor.
|
||||
"""
|
||||
|
||||
def __init__(self, descriptor: HidDescriptor, connection):
|
||||
self.descriptor = descriptor
|
||||
self._packet_size = descriptor.report_size_out
|
||||
self._connection = connection
|
||||
|
||||
nonce = os.urandom(8)
|
||||
self._channel_id = 0xFFFFFFFF
|
||||
response = self.call(CTAPHID.INIT, nonce)
|
||||
r_nonce, response = response[:8], response[8:]
|
||||
if r_nonce != nonce:
|
||||
raise Exception("Wrong nonce")
|
||||
(
|
||||
self._channel_id,
|
||||
self._u2fhid_version,
|
||||
v1,
|
||||
v2,
|
||||
v3,
|
||||
self._capabilities,
|
||||
) = struct.unpack_from(">IBBBBB", response)
|
||||
self._device_version = (v1, v2, v3)
|
||||
|
||||
def __repr__(self):
|
||||
return f"CtapHidDevice({self.descriptor.path!r})"
|
||||
|
||||
@property
|
||||
def version(self) -> int:
|
||||
"""CTAP HID protocol version."""
|
||||
return self._u2fhid_version
|
||||
|
||||
@property
|
||||
def device_version(self) -> Tuple[int, int, int]:
|
||||
"""Device version number."""
|
||||
return self._device_version
|
||||
|
||||
@property
|
||||
def capabilities(self) -> int:
|
||||
"""Capabilities supported by the device."""
|
||||
return self._capabilities
|
||||
|
||||
@property
|
||||
def product_name(self) -> Optional[str]:
|
||||
"""Product name of device."""
|
||||
return self.descriptor.product_name
|
||||
|
||||
@property
|
||||
def serial_number(self) -> Optional[str]:
|
||||
"""Serial number of device."""
|
||||
return self.descriptor.serial_number
|
||||
|
||||
def _send_cancel(self):
|
||||
packet = struct.pack(">IB", self._channel_id, TYPE_INIT | CTAPHID.CANCEL).ljust(
|
||||
self._packet_size, b"\0"
|
||||
)
|
||||
logger.log(LOG_LEVEL_TRAFFIC, "SEND: %s", packet.hex())
|
||||
self._connection.write_packet(packet)
|
||||
|
||||
def call(
|
||||
self,
|
||||
cmd: int,
|
||||
data: bytes = b"",
|
||||
event: Optional[Event] = None,
|
||||
on_keepalive: Optional[Callable[[int], None]] = None,
|
||||
) -> bytes:
|
||||
event = event or Event()
|
||||
remaining = data
|
||||
seq = 0
|
||||
|
||||
# Send request
|
||||
header = struct.pack(">IBH", self._channel_id, TYPE_INIT | cmd, len(remaining))
|
||||
while remaining or seq == 0:
|
||||
size = min(len(remaining), self._packet_size - len(header))
|
||||
body, remaining = remaining[:size], remaining[size:]
|
||||
packet = header + body
|
||||
logger.log(LOG_LEVEL_TRAFFIC, "SEND: %s", packet.hex())
|
||||
self._connection.write_packet(packet.ljust(self._packet_size, b"\0"))
|
||||
header = struct.pack(">IB", self._channel_id, 0x7F & seq)
|
||||
seq += 1
|
||||
|
||||
try:
|
||||
# Read response
|
||||
seq = 0
|
||||
response = b""
|
||||
last_ka = None
|
||||
while True:
|
||||
if event.is_set():
|
||||
# Cancel
|
||||
logger.debug("Sending cancel...")
|
||||
self._send_cancel()
|
||||
|
||||
recv = self._connection.read_packet()
|
||||
logger.log(LOG_LEVEL_TRAFFIC, "RECV: %s", recv.hex())
|
||||
|
||||
r_channel = struct.unpack_from(">I", recv)[0]
|
||||
recv = recv[4:]
|
||||
if r_channel != self._channel_id:
|
||||
raise Exception("Wrong channel")
|
||||
|
||||
if not response: # Initialization packet
|
||||
r_cmd, r_len = struct.unpack_from(">BH", recv)
|
||||
recv = recv[3:]
|
||||
if r_cmd == TYPE_INIT | cmd:
|
||||
pass # first data packet
|
||||
elif r_cmd == TYPE_INIT | CTAPHID.KEEPALIVE:
|
||||
ka_status = struct.unpack_from(">B", recv)[0]
|
||||
logger.debug(f"Got keepalive status: {ka_status:02x}")
|
||||
if on_keepalive and ka_status != last_ka:
|
||||
try:
|
||||
ka_status = STATUS(ka_status)
|
||||
except ValueError:
|
||||
pass # Unknown status value
|
||||
last_ka = ka_status
|
||||
on_keepalive(ka_status)
|
||||
continue
|
||||
elif r_cmd == TYPE_INIT | CTAPHID.ERROR:
|
||||
raise CtapError(struct.unpack_from(">B", recv)[0])
|
||||
else:
|
||||
raise CtapError(CtapError.ERR.INVALID_COMMAND)
|
||||
else: # Continuation packet
|
||||
r_seq = struct.unpack_from(">B", recv)[0]
|
||||
recv = recv[1:]
|
||||
if r_seq != seq:
|
||||
raise Exception("Wrong sequence number")
|
||||
seq += 1
|
||||
|
||||
response += recv
|
||||
if len(response) >= r_len:
|
||||
break
|
||||
|
||||
return response[:r_len]
|
||||
except KeyboardInterrupt:
|
||||
logger.debug("Keyboard interrupt, cancelling...")
|
||||
self._send_cancel()
|
||||
|
||||
raise
|
||||
|
||||
def wink(self) -> None:
|
||||
"""Causes the authenticator to blink."""
|
||||
self.call(CTAPHID.WINK)
|
||||
|
||||
def ping(self, msg: bytes = b"Hello FIDO") -> bytes:
|
||||
"""Sends data to the authenticator, which echoes it back.
|
||||
|
||||
:param msg: The data to send.
|
||||
:return: The response from the authenticator.
|
||||
"""
|
||||
return self.call(CTAPHID.PING, msg)
|
||||
|
||||
def lock(self, lock_time: int = 10) -> None:
|
||||
"""Locks the channel."""
|
||||
self.call(CTAPHID.LOCK, struct.pack(">B", lock_time))
|
||||
|
||||
def close(self) -> None:
|
||||
if self._connection:
|
||||
self._connection.close()
|
||||
self._connection = None
|
||||
|
||||
@classmethod
|
||||
def list_devices(cls) -> Iterator[CtapHidDevice]:
|
||||
for d in list_descriptors():
|
||||
yield cls(d, open_connection(d))
|
||||
|
||||
|
||||
def list_devices() -> Iterator[CtapHidDevice]:
|
||||
return CtapHidDevice.list_devices()
|
||||
|
||||
|
||||
def open_device(path) -> CtapHidDevice:
|
||||
descriptor = get_descriptor(path)
|
||||
return CtapHidDevice(descriptor, open_connection(descriptor))
|
||||
79
tests/docker/fido2/emulation.py
Normal file
79
tests/docker/fido2/emulation.py
Normal file
@@ -0,0 +1,79 @@
|
||||
# Original work Copyright 2016 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# Modified work Copyright 2020 Yubico AB. All Rights Reserved.
|
||||
# This file, with modifications, is licensed under the above Apache License.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from .base import HidDescriptor, CtapHidConnection
|
||||
|
||||
import socket
|
||||
from typing import Set
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
HOST = '127.0.0.1'
|
||||
PORT = 35962
|
||||
|
||||
# Don't typecheck this file on Windows
|
||||
assert sys.platform != "win32" # nosec
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class EmulationCtapHidConnection(CtapHidConnection):
|
||||
def __init__(self, descriptor):
|
||||
self.descriptor = descriptor
|
||||
self.handle = descriptor.path
|
||||
self.handle.connect((HOST, PORT))
|
||||
|
||||
def write_packet(self, packet):
|
||||
if (self.handle.send(len(packet).to_bytes(2, 'big')) != 2):
|
||||
raise OSError("write_packet sending size failed")
|
||||
if (self.handle.send(packet) != len(packet)):
|
||||
raise OSError("write_packet sending packet failed")
|
||||
|
||||
def read_packet(self):
|
||||
bts = self.handle.recv(2)
|
||||
if (len(bts) != 2):
|
||||
raise OSError("read_packet failed reading size")
|
||||
size = int.from_bytes(bts, 'big')
|
||||
data = self.handle.recv(size)
|
||||
if (len(data) != size):
|
||||
raise OSError("read_packet failed reading packet")
|
||||
return data
|
||||
|
||||
def close(self) -> None:
|
||||
return self.handle.close()
|
||||
|
||||
|
||||
def open_connection(descriptor):
|
||||
return EmulationCtapHidConnection(descriptor)
|
||||
|
||||
|
||||
def get_descriptor(_):
|
||||
HOST = 'localhost' # The remote host
|
||||
PORT = 35962 # The same port as used by the server
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
return HidDescriptor(s, 0x00, 0x00, 64, 64, "Pico-Fido", "AAAAAA")
|
||||
|
||||
def list_descriptors():
|
||||
devices = []
|
||||
try:
|
||||
devices.append(get_descriptor(None))
|
||||
except ValueError:
|
||||
pass # Not a CTAP device, ignore.
|
||||
|
||||
return devices
|
||||
107
tests/docker_env.sh
Normal file
107
tests/docker_env.sh
Normal file
@@ -0,0 +1,107 @@
|
||||
#!/bin/bash -eu
|
||||
|
||||
# Taken from Mbed-TLS project
|
||||
# https://github.com/Mbed-TLS/mbedtls/blob/master/tests/scripts/docker_env.sh
|
||||
#
|
||||
# docker_env.sh
|
||||
#
|
||||
# Purpose
|
||||
# -------
|
||||
#
|
||||
# This is a helper script to enable running tests under a Docker container,
|
||||
# thus making it easier to get set up as well as isolating test dependencies
|
||||
# (which include legacy/insecure configurations of openssl and gnutls).
|
||||
#
|
||||
# WARNING: the Dockerfile used by this script is no longer maintained! See
|
||||
# https://github.com/Mbed-TLS/mbedtls-test/blob/master/README.md#quick-start
|
||||
# for the set of Docker images we use on the CI.
|
||||
#
|
||||
# Notes for users
|
||||
# ---------------
|
||||
# This script expects a Linux x86_64 system with a recent version of Docker
|
||||
# installed and available for use, as well as http/https access. If a proxy
|
||||
# server must be used, invoke this script with the usual environment variables
|
||||
# (http_proxy and https_proxy) set appropriately. If an alternate Docker
|
||||
# registry is needed, specify MBEDTLS_DOCKER_REGISTRY to point at the
|
||||
# host name.
|
||||
#
|
||||
#
|
||||
# Running this script directly will check for Docker availability and set up
|
||||
# the Docker image.
|
||||
|
||||
# Copyright The Mbed TLS Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
# default values, can be overridden by the environment
|
||||
: ${MBEDTLS_DOCKER_GUEST:=bullseye}
|
||||
|
||||
|
||||
DOCKER_IMAGE_TAG="pico-hsm-test:${MBEDTLS_DOCKER_GUEST}"
|
||||
|
||||
# Make sure docker is available
|
||||
if ! which docker > /dev/null; then
|
||||
echo "Docker is required but doesn't seem to be installed. See https://www.docker.com/ to get started"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Figure out if we need to 'sudo docker'
|
||||
if groups | grep docker > /dev/null; then
|
||||
DOCKER="docker"
|
||||
else
|
||||
echo "Using sudo to invoke docker since you're not a member of the docker group..."
|
||||
DOCKER="docker"
|
||||
fi
|
||||
|
||||
# Figure out the number of processors available
|
||||
if [ "$(uname)" == "Darwin" ]; then
|
||||
NUM_PROC="$(sysctl -n hw.logicalcpu)"
|
||||
else
|
||||
NUM_PROC="$(nproc)"
|
||||
fi
|
||||
|
||||
# Build the Docker image
|
||||
echo "Getting docker image up to date (this may take a few minutes)..."
|
||||
${DOCKER} image build \
|
||||
-t ${DOCKER_IMAGE_TAG} \
|
||||
--cache-from=${DOCKER_IMAGE_TAG} \
|
||||
--network host \
|
||||
--build-arg MAKEFLAGS_PARALLEL="-j ${NUM_PROC}" \
|
||||
tests/docker/${MBEDTLS_DOCKER_GUEST}
|
||||
|
||||
run_in_docker()
|
||||
{
|
||||
ENV_ARGS=""
|
||||
while [ "$1" == "-e" ]; do
|
||||
ENV_ARGS="${ENV_ARGS} $1 $2"
|
||||
shift 2
|
||||
done
|
||||
|
||||
WORKDIR="${PWD}"
|
||||
if [ "$1" == '-w' ]; then
|
||||
WORKDIR="$2"
|
||||
shift 2
|
||||
fi
|
||||
|
||||
${DOCKER} container run --rm \
|
||||
--cap-add ALL \
|
||||
--privileged \
|
||||
--volume $PWD:$PWD \
|
||||
--workdir ${WORKDIR} \
|
||||
-e MAKEFLAGS \
|
||||
${ENV_ARGS} \
|
||||
${DOCKER_IMAGE_TAG} \
|
||||
$@
|
||||
}
|
||||
47
tests/pico-fido/test_000_getinfo.py
Normal file
47
tests/pico-fido/test_000_getinfo.py
Normal file
@@ -0,0 +1,47 @@
|
||||
"""
|
||||
/*
|
||||
* This file is part of the Pico Fido distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
import pytest
|
||||
from fido2.client import CtapError
|
||||
|
||||
|
||||
def test_getinfo(device):
|
||||
pass
|
||||
|
||||
|
||||
def test_get_info_version(info):
|
||||
assert "FIDO_2_0" in info.versions
|
||||
|
||||
|
||||
def test_Check_pin_protocols_field(info):
|
||||
if len(info.pin_uv_protocols):
|
||||
assert sum(info.pin_uv_protocols) > 0
|
||||
|
||||
|
||||
def test_Check_options_field(info):
|
||||
for x in info.options:
|
||||
assert info.options[x] in [True, False]
|
||||
|
||||
|
||||
def test_Check_up_option(device, info):
|
||||
if "up" not in info.options or info.options["up"]:
|
||||
with pytest.raises(CtapError) as e:
|
||||
device.MC(options={"up": True})
|
||||
assert e.value.code == CtapError.ERR.INVALID_OPTION
|
||||
@@ -1,3 +1,23 @@
|
||||
"""
|
||||
/*
|
||||
* This file is part of the Pico Fido distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
import os
|
||||
import pytest
|
||||
from fido2.ctap import CtapError
|
||||
@@ -14,9 +34,9 @@ def test_lockout(device, resetdevice, client_pin):
|
||||
pin_token = client_pin.get_pin_token(pin)
|
||||
|
||||
for i in range(1, 10):
|
||||
err = CtapError.ERR.PIN_INVALID
|
||||
if i in (3, 6):
|
||||
err = CtapError.ERR.PIN_AUTH_BLOCKED
|
||||
err = [CtapError.ERR.PIN_INVALID]
|
||||
if 3 <= i <= 7:
|
||||
err = [CtapError.ERR.PIN_AUTH_BLOCKED]
|
||||
elif i >= 8:
|
||||
err = [CtapError.ERR.PIN_BLOCKED, CtapError.ERR.PIN_INVALID]
|
||||
|
||||
@@ -1,5 +1,28 @@
|
||||
"""
|
||||
/*
|
||||
* This file is part of the Pico Fido distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
from fido2.client import CtapError
|
||||
from fido2.cose import ES256
|
||||
from fido2.cose import ES256, ES384, ES512, EdDSA
|
||||
import fido2.features
|
||||
fido2.features.webauthn_json_mapping.enabled = False
|
||||
from utils import ES256K
|
||||
import pytest
|
||||
|
||||
|
||||
@@ -11,7 +34,7 @@ def test_make_credential():
|
||||
pass
|
||||
|
||||
def test_attestation_format(MCRes):
|
||||
assert MCRes['res'].attestation_object.fmt in ["packed", "tpm", "android-key", "adroid-safetynet"]
|
||||
assert MCRes['res'].attestation_object.fmt in ["packed", "tpm", "android-key", "adroid-safetynet"]
|
||||
|
||||
def test_authdata_length(MCRes):
|
||||
assert len(MCRes['res'].attestation_object.auth_data) >= 77
|
||||
@@ -100,18 +123,25 @@ def test_bad_type_pubKeyCredParams(device):
|
||||
with pytest.raises(CtapError) as e:
|
||||
device.doMC(key_params=["wrong"])
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"alg", [ES256.ALGORITHM, ES384.ALGORITHM, ES512.ALGORITHM, ES256K.ALGORITHM, EdDSA.ALGORITHM]
|
||||
)
|
||||
def test_algorithms(device, info, alg):
|
||||
if ({'alg': alg, 'type': 'public-key'} in info.algorithms):
|
||||
device.doMC(key_params=[{"alg": alg, "type": "public-key"}])
|
||||
|
||||
def test_missing_pubKeyCredParams_type(device):
|
||||
with pytest.raises(CtapError) as e:
|
||||
device.doMC(key_params=[{"alg": ES256.ALGORITHM}])
|
||||
|
||||
assert e.value.code == CtapError.ERR.MISSING_PARAMETER
|
||||
assert e.value.code == CtapError.ERR.INVALID_CBOR
|
||||
|
||||
def test_missing_pubKeyCredParams_alg(device):
|
||||
with pytest.raises(CtapError) as e:
|
||||
device.doMC(key_params=[{"type": "public-key"}])
|
||||
|
||||
assert e.value.code in [
|
||||
CtapError.ERR.MISSING_PARAMETER,
|
||||
CtapError.ERR.INVALID_CBOR,
|
||||
CtapError.ERR.UNSUPPORTED_ALGORITHM,
|
||||
]
|
||||
|
||||
@@ -119,6 +149,8 @@ def test_bad_type_pubKeyCredParams_alg(device):
|
||||
with pytest.raises(CtapError) as e:
|
||||
device.doMC(key_params=[{"alg": "7", "type": "public-key"}])
|
||||
|
||||
assert e.value.code == CtapError.ERR.CBOR_UNEXPECTED_TYPE
|
||||
|
||||
def test_unsupported_algorithm(device):
|
||||
with pytest.raises(CtapError) as e:
|
||||
device.doMC(key_params=[{"alg": 1337, "type": "public-key"}])
|
||||
@@ -160,5 +192,6 @@ def test_exclude_list_excluded(device):
|
||||
|
||||
assert e.value.code == CtapError.ERR.CREDENTIAL_EXCLUDED
|
||||
|
||||
def test_unknown_option(resetdevice):
|
||||
resetdevice.MC(options={"unknown": False})
|
||||
def test_unknown_option(device):
|
||||
device.reset()
|
||||
device.MC(options={"unknown": False})
|
||||
@@ -1,5 +1,26 @@
|
||||
from fido2.utils import sha256
|
||||
"""
|
||||
/*
|
||||
* This file is part of the Pico Fido distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
from fido2.client import CtapError
|
||||
from fido2.cose import ES256, ES384, ES512, EdDSA
|
||||
from utils import verify, ES256K
|
||||
import pytest
|
||||
|
||||
def test_authenticate(device):
|
||||
@@ -27,6 +48,17 @@ def test_empty_allowList(device):
|
||||
device.doGA(allow_list=[])
|
||||
assert e.value.code == CtapError.ERR.NO_CREDENTIALS
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"alg", [ES256.ALGORITHM, ES384.ALGORITHM, ES512.ALGORITHM, ES256K.ALGORITHM, EdDSA.ALGORITHM]
|
||||
)
|
||||
def test_algorithms(device, info, alg):
|
||||
if ({'alg': alg, 'type': 'public-key'} in info.algorithms):
|
||||
MCRes = device.doMC(key_params=[{"alg": alg, "type": "public-key"}])
|
||||
res = device.GA(allow_list=[
|
||||
{"id": MCRes['res'].attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
|
||||
])
|
||||
verify(MCRes['res'].attestation_object, res['res'], res['req']['client_data_hash'])
|
||||
|
||||
def test_get_assertion_allow_list_filtering_and_buffering(device):
|
||||
""" Check that authenticator filters and stores items in allow list correctly """
|
||||
allow_list = []
|
||||
@@ -104,7 +136,6 @@ def test_missing_rp(device):
|
||||
assert e.value.code == CtapError.ERR.MISSING_PARAMETER
|
||||
|
||||
def test_bad_rp(device):
|
||||
|
||||
with pytest.raises(CtapError) as e:
|
||||
device.doGA(rp_id={"id": {"type": "wrong"}})
|
||||
|
||||
@@ -182,11 +213,19 @@ def test_allow_list_missing_id(device, MCRes):
|
||||
]
|
||||
)
|
||||
|
||||
def test_user_presence_option_false(device, MCRes):
|
||||
def test_silent_ok(device, MCRes):
|
||||
res = device.GA(options={"up": False}, allow_list=[
|
||||
{"id": MCRes['res'].attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
|
||||
])
|
||||
|
||||
def test_silent_ko(device, MCRes):
|
||||
cred = MCRes['res'].attestation_object.auth_data.credential_data.credential_id + b'\x00'
|
||||
with pytest.raises(CtapError) as e:
|
||||
res = device.GA(options={"up": False}, allow_list=[
|
||||
{"id": cred, "type": "public-key"}
|
||||
])
|
||||
assert e.value.code == CtapError.ERR.NO_CREDENTIALS
|
||||
|
||||
def test_credential_resets(device, MCRes, GARes):
|
||||
device.reset()
|
||||
with pytest.raises(CtapError) as e:
|
||||
@@ -1,3 +1,23 @@
|
||||
"""
|
||||
/*
|
||||
* This file is part of the Pico Fido distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
from fido2.client import CtapError
|
||||
import pytest
|
||||
import random
|
||||
@@ -37,9 +57,8 @@ def test_with_allow_list_after_reset(device, MCRes_DC, GARes_DC):
|
||||
|
||||
device.reset()
|
||||
|
||||
with pytest.raises(CtapError) as e:
|
||||
ga_res = device.doGA(allow_list=allow_list)
|
||||
assert e.value.code == CtapError.ERR.NO_CREDENTIALS
|
||||
# It returns a silent authentication
|
||||
ga_res = device.doGA(allow_list=allow_list)
|
||||
|
||||
|
||||
|
||||
@@ -235,5 +254,5 @@ def test_returned_credential(device):
|
||||
device.GNA()
|
||||
|
||||
# the returned credential should have user id in it
|
||||
print(ga_res)
|
||||
assert 'id' in ga_res.user and len(ga_res.user["id"]) > 0
|
||||
#print(ga_res)
|
||||
#assert 'id' in ga_res.user and len(ga_res.user["id"]) > 0
|
||||
@@ -1,3 +1,23 @@
|
||||
"""
|
||||
/*
|
||||
* This file is part of the Pico Fido distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
import pytest
|
||||
from fido2.ctap import CtapError
|
||||
from fido2.ctap2.pin import PinProtocolV2, ClientPin
|
||||
@@ -1,3 +1,23 @@
|
||||
"""
|
||||
/*
|
||||
* This file is part of the Pico Fido distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
import pytest
|
||||
from fido2.ctap2.extensions import CredProtectExtension
|
||||
from fido2.webauthn import UserVerificationRequirement
|
||||
@@ -106,10 +126,7 @@ def test_credprotect_optional_and_list_works_no_uv(device, MCCredProtectOptional
|
||||
res1 = device.doGA(allow_list=allow_list)['res'].get_assertions()[0]
|
||||
assert res1.number_of_credentials in (None, 2)
|
||||
|
||||
results = [res1]
|
||||
if res1.number_of_credentials == 2:
|
||||
res2 = device.GNA()['res']
|
||||
results.append(res2)
|
||||
results = device.doGA(allow_list=allow_list)['res'].get_assertions()
|
||||
|
||||
# the required credProtect is not returned.
|
||||
for res in results:
|
||||
@@ -1,3 +1,23 @@
|
||||
"""
|
||||
/*
|
||||
* This file is part of the Pico Fido distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
import pytest
|
||||
from fido2.ctap import CtapError
|
||||
from fido2.ctap2.extensions import HmacSecretExtension
|
||||
@@ -49,12 +69,12 @@ def test_hmac_secret_entropy(device, MCHmacSecret, hmac, salts
|
||||
|
||||
#print(shannon_entropy(auth.authenticator_data.extensions['hmac-secret']))
|
||||
if len(salts) == 1:
|
||||
assert shannon_entropy(auth.authenticator_data.extensions['hmac-secret']) > 4.6
|
||||
assert shannon_entropy(ext["hmacGetSecret"]['output1']) > 4.6
|
||||
assert shannon_entropy(auth.authenticator_data.extensions['hmac-secret']) > 4.5
|
||||
assert shannon_entropy(ext["hmacGetSecret"]['output1']) > 4.5
|
||||
if len(salts) == 2:
|
||||
assert shannon_entropy(auth.authenticator_data.extensions['hmac-secret']) > 5.4
|
||||
assert shannon_entropy(ext["hmacGetSecret"]['output1']) > 4.6
|
||||
assert shannon_entropy(ext["hmacGetSecret"]['output2']) > 4.6
|
||||
assert shannon_entropy(ext["hmacGetSecret"]['output1']) > 4.5
|
||||
assert shannon_entropy(ext["hmacGetSecret"]['output2']) > 4.5
|
||||
|
||||
def get_output(device, MCHmacSecret, hmac, salts):
|
||||
hout = {'salt1':salts[0]}
|
||||
@@ -1,3 +1,23 @@
|
||||
"""
|
||||
/*
|
||||
* This file is part of the Pico Fido distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
import pytest
|
||||
from fido2.ctap2.extensions import CredProtectExtension
|
||||
from fido2.webauthn import UserVerificationRequirement
|
||||
@@ -49,7 +69,7 @@ def test_minpin(SetMinPin, MCMinPin):
|
||||
|
||||
def test_minpin_bad_rpid(SetMinPinWrongRpid, MCMinPin):
|
||||
assert not MCMinPin.auth_data.extensions
|
||||
assert "minPinLength" not in MCMinPin.auth_data.extensions
|
||||
#assert "minPinLength" not in MCMinPin.auth_data.extensions
|
||||
|
||||
def test_setminpin(device, SetMinPin, MCMinPin):
|
||||
cfg = FidoConfig(device)
|
||||
@@ -1,3 +1,23 @@
|
||||
"""
|
||||
/*
|
||||
* This file is part of the Pico Fido distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
import pytest
|
||||
import time
|
||||
import random
|
||||
@@ -87,7 +107,7 @@ def assert_cred_response_has_all_fields(cred_res):
|
||||
CredentialManagement.RESULT.CREDENTIAL_ID,
|
||||
CredentialManagement.RESULT.PUBLIC_KEY,
|
||||
CredentialManagement.RESULT.TOTAL_CREDENTIALS,
|
||||
CredentialManagement.RESULT.CRED_PROTECT,
|
||||
#CredentialManagement.RESULT.CRED_PROTECT,
|
||||
):
|
||||
assert i in cred_res
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
"""
|
||||
/*
|
||||
* This file is part of the Pico Fido distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
# Test U2F register works with FIDO2 auth
|
||||
def test_ctap1_register(RegRes):
|
||||
pass
|
||||
@@ -1,3 +1,23 @@
|
||||
"""
|
||||
/*
|
||||
* This file is part of the Pico Fido distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
import pytest
|
||||
import os
|
||||
from fido2.ctap1 import APDU, ApduError, Ctap1
|
||||
@@ -1,3 +1,23 @@
|
||||
"""
|
||||
/*
|
||||
* This file is part of the Pico Fido distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
import os
|
||||
import socket
|
||||
import time
|
||||
@@ -154,22 +174,17 @@ class TestHID(object):
|
||||
def test_check_busy(self, device):
|
||||
t1 = time.time() * 1000
|
||||
device.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
oldcid = device.cid()
|
||||
#oldcid = device.cid().to_bytes(4, 'big')
|
||||
newcid = b"\x11\x22\x33\x44"
|
||||
device.send_raw("\x81\x04\x00")
|
||||
device.set_cid(newcid)
|
||||
device.send_raw("\x81\x04\x00")
|
||||
cmd, r = device.recv_raw() # busy response
|
||||
t2 = time.time() * 1000
|
||||
assert t2 - t1 < 100
|
||||
#t2 = time.time() * 1000
|
||||
#assert t2 - t1 < 100
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.CHANNEL_BUSY
|
||||
|
||||
device.set_cid(oldcid)
|
||||
cmd, r = device.recv_raw() # timeout response
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.TIMEOUT
|
||||
|
||||
def test_check_busy_interleaved(self, device):
|
||||
cid1 = b"\x11\x22\x33\x44"
|
||||
cid2 = b"\x01\x22\x33\x44"
|
||||
@@ -181,16 +196,13 @@ class TestHID(object):
|
||||
|
||||
device.set_cid(cid2) # send ping on 2nd channel
|
||||
device.send_raw("\x81\x00\x39")
|
||||
time.sleep(0.1)
|
||||
device.send_raw("\x00")
|
||||
|
||||
cmd, r = device.recv_raw() # busy response
|
||||
time.sleep(0.1)
|
||||
|
||||
|
||||
device.set_cid(cid1) # finish 1st channel ping
|
||||
device.send_raw("\x00")
|
||||
|
||||
device.set_cid(cid2)
|
||||
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.CHANNEL_BUSY
|
||||
|
||||
@@ -198,27 +210,30 @@ class TestHID(object):
|
||||
cmd, r = device.recv_raw() # ping response
|
||||
assert cmd == 0x81
|
||||
assert len(r) == 0x39
|
||||
cmd, r = device.recv_raw() # ping response
|
||||
|
||||
def test_cid_0(self, device):
|
||||
device.set_cid("\x00\x00\x00\x00")
|
||||
device.reset()
|
||||
time.sleep(0.1)
|
||||
device.set_cid(b"\x00\x00\x00\x00")
|
||||
device.send_raw(
|
||||
"\x86\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88", cid="\x00\x00\x00\x00"
|
||||
)
|
||||
cmd, r = device.recv_raw() # timeout
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.INVALID_CHANNEL
|
||||
device.set_cid("\x05\x04\x03\x02")
|
||||
device.set_cid(b"\x05\x04\x03\x02")
|
||||
|
||||
def test_cid_ffffffff(self, device):
|
||||
|
||||
device.set_cid("\xff\xff\xff\xff")
|
||||
device.set_cid(b"\xff\xff\xff\xff")
|
||||
device.send_raw(
|
||||
"\x81\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88", cid="\xff\xff\xff\xff"
|
||||
)
|
||||
cmd, r = device.recv_raw() # timeout
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.INVALID_CHANNEL
|
||||
device.set_cid("\x05\x04\x03\x02")
|
||||
device.set_cid(b"\x05\x04\x03\x02")
|
||||
|
||||
def test_keep_alive(self, device, check_timeouts=False):
|
||||
|
||||
258
tests/pico-fido/test_070_oath.py
Normal file
258
tests/pico-fido/test_070_oath.py
Normal file
@@ -0,0 +1,258 @@
|
||||
"""
|
||||
/*
|
||||
* This file is part of the Pico Fido distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from utils import *
|
||||
import hmac, hashlib
|
||||
|
||||
INS_PUT = 0x01
|
||||
INS_DELETE = 0x02
|
||||
INS_SET_CODE = 0x03
|
||||
INS_RESET = 0x04
|
||||
INS_LIST = 0xa1
|
||||
INS_CALCULATE = 0xa2
|
||||
INS_VALIDATE = 0xa3
|
||||
INS_CALC_ALL = 0xa4
|
||||
INS_SEND_REMAINING = 0xa5
|
||||
|
||||
RESP_MORE_DATA = 0x61
|
||||
|
||||
TAG_NAME = 0x71
|
||||
TAG_NAME_LIST = 0x72
|
||||
TAG_KEY = 0x73
|
||||
TAG_CHALLENGE = 0x74
|
||||
TAG_RESPONSE = 0x75
|
||||
TAG_T_RESPONSE = 0x76
|
||||
TAG_NO_RESPONSE = 0x77
|
||||
TAG_PROPERTY = 0x78
|
||||
TAG_VERSION = 0x79
|
||||
TAG_IMF = 0x7a
|
||||
TAG_ALGO = 0x7b
|
||||
TAG_TOUCH_RESPONSE = 0x7c
|
||||
|
||||
TYPE_MASK = 0xf0
|
||||
TYPE_HOTP = 0x10
|
||||
TYPE_TOTP = 0x20
|
||||
|
||||
ALG_MASK = 0x0f
|
||||
ALG_SHA1 = 0x01
|
||||
ALG_SHA256 = 0x02
|
||||
|
||||
PROP_ALWAYS_INC = 0x01
|
||||
PROP_REQUIRE_TOUCH = 0x02
|
||||
|
||||
## Based on tests on https://github.com/Yubico/ykneo-oath/blob/master/test/test/pkgYkneoOathTest/YkneoOathTest.java
|
||||
|
||||
def test_select_oath(select_oath):
|
||||
pass
|
||||
|
||||
def list_apdu(ccid_card):
|
||||
resp = send_apdu(ccid_card, INS_LIST, p1=0, p2=0)
|
||||
return resp
|
||||
|
||||
name_kaka = [ord('k'), ord('a'), ord('k'), ord('a')]
|
||||
data_name = [TAG_NAME] + [len(name_kaka)] + name_kaka
|
||||
data_key = [TAG_KEY, 0x16, 0x21, 0x06, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b]
|
||||
data_chal = [TAG_CHALLENGE, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]
|
||||
|
||||
def test_life(reset_oath):
|
||||
data = data_name + data_key
|
||||
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=list(data))
|
||||
assert(len(resp) == 0)
|
||||
resp = list_apdu(reset_oath)
|
||||
exp = [TAG_NAME_LIST, 5, 0x21] + name_kaka
|
||||
assert(resp == exp)
|
||||
|
||||
data = data_name + data_chal
|
||||
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=0, data=data)
|
||||
exp = [TAG_RESPONSE, 0x15, 0x06, 0xb3, 0x99, 0xbd, 0xfc, 0x9d, 0x05, 0xd1, 0x2a, 0xc4, 0x35, 0xc4, 0xc8, 0xd6, 0xcb, 0xd2, 0x47, 0xc4, 0x0a, 0x30, 0xf1]
|
||||
assert(resp == exp)
|
||||
|
||||
data = data_name
|
||||
resp = send_apdu(reset_oath, INS_DELETE, p1=0, p2=0, data=data)
|
||||
resp = list_apdu(reset_oath)
|
||||
assert(len(resp) == 0)
|
||||
|
||||
def test_overwrite(reset_oath):
|
||||
data = data_name + data_key
|
||||
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=list(data))
|
||||
assert(len(resp) == 0)
|
||||
resp = list_apdu(reset_oath)
|
||||
exp = [TAG_NAME_LIST, 5, 0x21] + name_kaka
|
||||
assert(resp == exp)
|
||||
|
||||
data = data_name + [TAG_CHALLENGE, 0x8] + list(bytes(b'\xff'*8))
|
||||
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=0, data=data)
|
||||
exp = [TAG_RESPONSE, 0x15, 0x06, 0x79, 0x3e, 0x1b, 0xbd, 0xbf, 0xa7, 0x75, 0xa8, 0x63,0xcc, 0x80, 0x02, 0xce, 0xe4, 0xbd, 0x6c, 0xd7, 0xce, 0xb8, 0xcd]
|
||||
assert(resp == exp)
|
||||
|
||||
resp = list_apdu(reset_oath)
|
||||
exp = [TAG_NAME_LIST, 5, 0x21] + name_kaka
|
||||
assert(resp == exp)
|
||||
|
||||
data = data_name + [TAG_CHALLENGE, 0x8] + list(bytes(b'\xff\x00'*4))
|
||||
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=0, data=data)
|
||||
exp = [TAG_RESPONSE, 0x15, 0x06, 0x3b, 0x0e, 0x3c, 0x63, 0x1c, 0x01, 0x67, 0xb0, 0x93, 0xa5, 0xec, 0xb9, 0x09, 0x7d, 0x0b, 0x8e, 0x9a, 0xcc, 0x2f, 0x7f]
|
||||
assert(resp == exp)
|
||||
|
||||
def test_auth(reset_oath):
|
||||
key = list(bytes(b'kaka blahonga'))
|
||||
chal = [1,2,3,4,5,6,7,8]
|
||||
resp = [0x0c, 0x42, 0x8e, 0x9c, 0xba, 0xa3, 0xb3, 0xab, 0x18, 0x53, 0xd8, 0x79, 0xb9, 0xd2, 0x26, 0xf7, 0xce, 0xcc, 0x4a, 0x7a]
|
||||
data = [TAG_KEY, len(key)+1, ALG_SHA1 | TYPE_TOTP] + key + [TAG_CHALLENGE, len(chal)] + chal + [TAG_RESPONSE, len(resp)] + resp
|
||||
resp = send_apdu(reset_oath, INS_SET_CODE, p1=0, p2=0, data=data)
|
||||
|
||||
reset_oath.connection.reconnect()
|
||||
with pytest.raises(APDUResponse) as e:
|
||||
resp = list_apdu(reset_oath)
|
||||
assert([e.value.sw1, e.value.sw2] == [0x69, 0x82])
|
||||
|
||||
aid = [0xa0, 0x00, 0x00, 0x05, 0x27, 0x21, 0x01, 0x01]
|
||||
resp = send_apdu(reset_oath, 0xA4, 0x04, 0x00, aid)
|
||||
assert(resp[15] == TAG_CHALLENGE)
|
||||
assert(resp[16] == 8)
|
||||
resp2 = hmac.digest(bytes(key), bytes(resp[17:17+8]), 'sha1')
|
||||
data = [TAG_RESPONSE, len(resp2)] + list(resp2) + [TAG_CHALLENGE, len(chal)] + chal
|
||||
resp = send_apdu(reset_oath, INS_VALIDATE, p1=0, p2=0, data=data)
|
||||
exp = [TAG_RESPONSE, 20] + list(hmac.digest(bytes(key), bytes(chal), 'sha1'))
|
||||
assert(exp == resp)
|
||||
resp = list_apdu(reset_oath)
|
||||
|
||||
def test_bothoath(reset_oath):
|
||||
digits = 6
|
||||
tname = list(bytes(b'totp'))
|
||||
data = [TAG_NAME, len(tname)] + tname + [TAG_KEY, 9, TYPE_TOTP | ALG_SHA1, digits] + list(bytes(b'foo bar'))
|
||||
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=data)
|
||||
data[2] = ord('h')
|
||||
data[8] = TYPE_HOTP | ALG_SHA1
|
||||
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=data)
|
||||
|
||||
hname = tname[:]
|
||||
hname[0] = ord('h')
|
||||
data = [TAG_CHALLENGE, 8, 0, 0, 0, 0, 0x02, 0xbc, 0xad, 0xc8]
|
||||
resp = send_apdu(reset_oath, INS_CALC_ALL, p1=0, p2=1, data=data)
|
||||
exp = [TAG_NAME, len(tname)] + tname + [TAG_T_RESPONSE, 5, digits, 0x3d, 0xc6, 0xbf, 0x3d] + [TAG_NAME, len(hname)] + hname + [TAG_NO_RESPONSE, 0x01, digits]
|
||||
assert(exp == resp)
|
||||
|
||||
data = [TAG_NAME, len(hname)] + hname + [TAG_CHALLENGE]
|
||||
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=1, data=data)
|
||||
exp = [TAG_T_RESPONSE, 5, digits, 0x17, 0xfa, 0x2d, 0x40]
|
||||
assert(resp == exp)
|
||||
|
||||
def test_imf_overwrite(reset_oath):
|
||||
key = list(bytes(b'kaka'))
|
||||
imf = [0xff, 0x00, 0xff, 0xff]
|
||||
name = list(bytes(b'kaka'))
|
||||
|
||||
data = [TAG_NAME, len(name)] + name + [TAG_KEY, len(key)+2, ALG_SHA1 | TYPE_HOTP, 6] + key + [TAG_IMF, len(imf)] + imf
|
||||
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=data)
|
||||
data = [TAG_NAME, len(name)] + name + [TAG_CHALLENGE]
|
||||
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=1, data=data)
|
||||
exp = [TAG_T_RESPONSE, 5, 6, 0x45, 0xd9, 0x0f, 0x25]
|
||||
assert(exp == resp)
|
||||
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=1, data=data)
|
||||
exp = [TAG_T_RESPONSE, 5, 6, 0x1b, 0xc5, 0x4a, 0x85]
|
||||
assert(exp == resp)
|
||||
|
||||
data = [TAG_NAME, len(name)] + name + [TAG_KEY, len(key)+2, ALG_SHA1 | TYPE_HOTP, 6] + key
|
||||
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=data)
|
||||
data = [TAG_NAME, len(name)] + name + [TAG_CHALLENGE]
|
||||
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=1, data=data)
|
||||
exp = [TAG_T_RESPONSE, 5, 6, 0x16, 0x53, 0x24, 0xdb]
|
||||
assert(exp == resp)
|
||||
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=1, data=data)
|
||||
exp = [TAG_T_RESPONSE, 5, 6, 0x53, 0xed, 0x5e, 0xb2]
|
||||
assert(exp == resp)
|
||||
|
||||
def test_imf_more(reset_oath):
|
||||
key = [0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30,
|
||||
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30]
|
||||
imf = [0, 0, 0, 1]
|
||||
name = list(bytes(b'kaka'))
|
||||
|
||||
data = [TAG_NAME, len(name)] + name + [TAG_KEY, len(key)+2, ALG_SHA1 | TYPE_HOTP, 6] + key + [TAG_IMF, len(imf)] + imf
|
||||
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=data)
|
||||
data = [TAG_NAME, len(name)] + name + [TAG_CHALLENGE]
|
||||
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=1, data=data)
|
||||
exp = [TAG_T_RESPONSE, 5, 6, 0x41, 0x39, 0x7e, 0xea]
|
||||
assert(exp == resp)
|
||||
|
||||
def test_delete(reset_oath):
|
||||
key = list(bytes(b'blahonga!'))
|
||||
firstname = list(bytes(b'one'))
|
||||
secondname = list(bytes(b'two'))
|
||||
thirdname = list(bytes(b'three'))
|
||||
type = ALG_SHA1 | TYPE_TOTP
|
||||
|
||||
data = [TAG_NAME, len(firstname)] + firstname + [TAG_KEY, len(key)+2, type, 6] + key
|
||||
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=data)
|
||||
data = [TAG_NAME, len(secondname)] + secondname + [TAG_KEY, len(key)+2, type, 6] + key
|
||||
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=data)
|
||||
resp = list_apdu(reset_oath)
|
||||
exp = [TAG_NAME_LIST, len(firstname)+1, type] + firstname + [TAG_NAME_LIST, len(secondname)+1, type] + secondname
|
||||
assert(exp == resp)
|
||||
|
||||
data = [TAG_NAME, len(firstname)] + firstname
|
||||
resp = send_apdu(reset_oath, INS_DELETE, p1=0, p2=0, data=data)
|
||||
resp = list_apdu(reset_oath)
|
||||
exp = [TAG_NAME_LIST, len(secondname)+1, type] + secondname
|
||||
assert(exp == resp)
|
||||
|
||||
data = [TAG_NAME, len(thirdname)] + thirdname + [TAG_KEY, len(key)+2, type, 6] + key
|
||||
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=data)
|
||||
resp = list_apdu(reset_oath)
|
||||
exp = [TAG_NAME_LIST, len(thirdname)+1, type] + thirdname + [TAG_NAME_LIST, len(secondname)+1, type] + secondname
|
||||
assert(exp == resp)
|
||||
|
||||
def test_noauth(reset_oath):
|
||||
key = list(bytes(b'kaka blahonga'))
|
||||
chal = [1,2,3,4,5,6,7,8]
|
||||
resp = [0x0c, 0x42, 0x8e, 0x9c, 0xba, 0xa3, 0xb3, 0xab, 0x18, 0x53, 0xd8, 0x79, 0xb9, 0xd2, 0x26, 0xf7, 0xce, 0xcc, 0x4a, 0x7a]
|
||||
data = [TAG_KEY, len(key)+1, ALG_SHA1 | TYPE_TOTP] + key + [TAG_CHALLENGE, len(chal)] + chal + [TAG_RESPONSE, len(resp)] + resp
|
||||
resp = send_apdu(reset_oath, INS_SET_CODE, p1=0, p2=0, data=data)
|
||||
|
||||
reset_oath.connection.reconnect()
|
||||
with pytest.raises(APDUResponse) as e:
|
||||
resp = list_apdu(reset_oath)
|
||||
assert([e.value.sw1, e.value.sw2] == [0x69, 0x82])
|
||||
|
||||
with pytest.raises(APDUResponse) as e:
|
||||
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=None)
|
||||
assert([e.value.sw1, e.value.sw2] == [0x69, 0x82])
|
||||
|
||||
with pytest.raises(APDUResponse) as e:
|
||||
resp = send_apdu(reset_oath, INS_DELETE, p1=0, p2=0, data=None)
|
||||
assert([e.value.sw1, e.value.sw2] == [0x69, 0x82])
|
||||
|
||||
with pytest.raises(APDUResponse) as e:
|
||||
resp = send_apdu(reset_oath, INS_SET_CODE, p1=0, p2=0, data=None)
|
||||
assert([e.value.sw1, e.value.sw2] == [0x69, 0x82])
|
||||
|
||||
with pytest.raises(APDUResponse) as e:
|
||||
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=0, data=None)
|
||||
assert([e.value.sw1, e.value.sw2] == [0x69, 0x82])
|
||||
|
||||
with pytest.raises(APDUResponse) as e:
|
||||
resp = send_apdu(reset_oath, INS_CALC_ALL, p1=0, p2=0, data=None)
|
||||
assert([e.value.sw1, e.value.sw2] == [0x69, 0x82])
|
||||
|
||||
with pytest.raises(APDUResponse) as e:
|
||||
resp = send_apdu(reset_oath, INS_RESET, p1=0, p2=0, data=None)
|
||||
assert([e.value.sw1, e.value.sw2] == [0x6A, 0x86])
|
||||
resp = send_apdu(reset_oath, INS_RESET, p1=0xde, p2=0xad, data=None)
|
||||
@@ -1,27 +0,0 @@
|
||||
import pytest
|
||||
from fido2.client import CtapError
|
||||
|
||||
|
||||
def test_getinfo(device):
|
||||
pass
|
||||
|
||||
|
||||
def test_get_info_version(info):
|
||||
assert "FIDO_2_0" in info.versions
|
||||
|
||||
|
||||
def test_Check_pin_protocols_field(info):
|
||||
if len(info.pin_uv_protocols):
|
||||
assert sum(info.pin_uv_protocols) > 0
|
||||
|
||||
|
||||
def test_Check_options_field(info):
|
||||
for x in info.options:
|
||||
assert info.options[x] in [True, False]
|
||||
|
||||
|
||||
def test_Check_up_option(device, info):
|
||||
if "up" not in info.options or info.options["up"]:
|
||||
with pytest.raises(CtapError) as e:
|
||||
device.MC(options={"up": True})
|
||||
assert e.value.code == CtapError.ERR.INVALID_OPTION
|
||||
5
tests/run-test-in-docker.sh
Executable file
5
tests/run-test-in-docker.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash -eu
|
||||
|
||||
source tests/docker_env.sh
|
||||
run_in_docker ./tests/start-up-and-test.sh
|
||||
|
||||
8
tests/start-up-and-test.sh
Executable file
8
tests/start-up-and-test.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash -eu
|
||||
|
||||
/usr/sbin/pcscd &
|
||||
sleep 2
|
||||
rm -f memory.flash
|
||||
cp -R tests/docker/fido2/* /usr/local/lib/python3.9/dist-packages/fido2/hid
|
||||
./build_in_docker/pico_fido > /dev/null &
|
||||
pytest tests
|
||||
@@ -1,4 +1,29 @@
|
||||
"""
|
||||
/*
|
||||
* This file is part of the Pico Fido distribution (https://github.com/polhenarejos/pico-fido).
|
||||
* Copyright (c) 2022 Pol Henarejos.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
from fido2.webauthn import AttestedCredentialData
|
||||
from fido2.cose import CoseKey
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives.asymmetric import ec
|
||||
from fido2.utils import bytes2int, int2bytes
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
import random
|
||||
import string
|
||||
import secrets
|
||||
@@ -6,6 +31,46 @@ import math
|
||||
from threading import Event, Timer
|
||||
from numbers import Number
|
||||
|
||||
import sys
|
||||
try:
|
||||
from smartcard.CardType import AnyCardType
|
||||
from smartcard.CardRequest import CardRequest
|
||||
from smartcard.Exceptions import CardRequestTimeoutException, CardConnectionException
|
||||
except ModuleNotFoundError:
|
||||
print('ERROR: smarctard module not found! Install pyscard package.\nTry with `pip install pyscard`')
|
||||
sys.exit(-1)
|
||||
|
||||
class APDUResponse(Exception):
|
||||
def __init__(self, sw1, sw2):
|
||||
self.sw1 = sw1
|
||||
self.sw2 = sw2
|
||||
super().__init__(f'SW:{sw1:02X}{sw2:02X}')
|
||||
|
||||
def send_apdu(card, command, p1, p2, data=None, ne=None):
|
||||
lc = []
|
||||
dataf = []
|
||||
if (data):
|
||||
lc = [0x00] + list(len(data).to_bytes(2, 'big'))
|
||||
dataf = data
|
||||
if (ne is None):
|
||||
le = [0x00, 0x00]
|
||||
else:
|
||||
le = list(ne.to_bytes(2, 'big'))
|
||||
if (isinstance(command, list) and len(command) > 1):
|
||||
apdu = command
|
||||
else:
|
||||
apdu = [0x00, command]
|
||||
|
||||
apdu = apdu + [p1, p2] + lc + dataf + le
|
||||
try:
|
||||
response, sw1, sw2 = card.connection.transmit(apdu)
|
||||
except CardConnectionException:
|
||||
card.connection.reconnect()
|
||||
response, sw1, sw2 = card.connection.transmit(apdu)
|
||||
if (sw1 != 0x90):
|
||||
raise APDUResponse(sw1, sw2)
|
||||
return response
|
||||
|
||||
|
||||
def verify(MC, GA, client_data_hash):
|
||||
credential_data = AttestedCredentialData(MC.auth_data.credential_data)
|
||||
@@ -115,3 +180,29 @@ class Timeout(object):
|
||||
if self.timer:
|
||||
self.timer.cancel()
|
||||
self.timer.join()
|
||||
|
||||
class ES256K(CoseKey):
|
||||
ALGORITHM = -47
|
||||
_HASH_ALG = hashes.SHA256()
|
||||
|
||||
def verify(self, message, signature):
|
||||
if self[-1] != 8:
|
||||
raise ValueError("Unsupported elliptic curve")
|
||||
ec.EllipticCurvePublicNumbers(
|
||||
bytes2int(self[-2]), bytes2int(self[-3]), ec.SECP256K1()
|
||||
).public_key(default_backend()).verify(
|
||||
signature, message, ec.ECDSA(self._HASH_ALG)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_cryptography_key(cls, public_key):
|
||||
pn = public_key.public_numbers()
|
||||
return cls(
|
||||
{
|
||||
1: 2,
|
||||
3: cls.ALGORITHM,
|
||||
-1: 8,
|
||||
-2: int2bytes(pn.x, 32),
|
||||
-3: int2bytes(pn.y, 32),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -23,7 +23,6 @@ import sys
|
||||
import argparse
|
||||
import platform
|
||||
from binascii import hexlify
|
||||
from words import words
|
||||
from threading import Event
|
||||
from typing import Mapping, Any, Optional, Callable
|
||||
import struct
|
||||
@@ -33,7 +32,7 @@ from enum import IntEnum, unique
|
||||
|
||||
try:
|
||||
from fido2.ctap2.config import Config
|
||||
from fido2.ctap2 import Ctap2
|
||||
from fido2.ctap2 import Ctap2, ClientPin, PinProtocolV2
|
||||
from fido2.hid import CtapHidDevice, CTAPHID
|
||||
from fido2.utils import bytes2int, int2bytes
|
||||
from fido2 import cbor
|
||||
@@ -58,14 +57,6 @@ except:
|
||||
from enum import IntEnum
|
||||
from binascii import hexlify
|
||||
|
||||
if (platform.system() == 'Windows' or platform.system() == 'Linux'):
|
||||
from secure_key import windows as skey
|
||||
elif (platform.system() == 'Darwin'):
|
||||
from secure_key import macos as skey
|
||||
else:
|
||||
print('ERROR: platform not supported')
|
||||
sys.exit(-1)
|
||||
|
||||
def get_pki_data(url, data=None, method='GET'):
|
||||
user_agent = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; '
|
||||
'rv:1.9.0.7) Gecko/2009021910 Firefox/3.0.7'
|
||||
@@ -86,10 +77,17 @@ class VendorConfig(Config):
|
||||
class PARAM(IntEnum):
|
||||
VENDOR_COMMAND_ID = 0x01
|
||||
VENDOR_AUT_CT = 0x02
|
||||
VENDOR_PARAM = 0x02
|
||||
|
||||
class CMD(IntEnum):
|
||||
CONFIG_AUT_ENABLE = 0x03e43f56b34285e2
|
||||
CONFIG_AUT_DISABLE = 0x1831a40f04a25ed9
|
||||
CONFIG_AUT_ENABLE = 0x03e43f56b34285e2
|
||||
CONFIG_AUT_DISABLE = 0x1831a40f04a25ed9
|
||||
CONFIG_VENDOR_PROTOTYPE = 0x7f
|
||||
CONFIG_VENDOR_PHY = 0x1b
|
||||
CONFIG_PHY_VIDPID = 0x6fcb19b0cbe3acfa
|
||||
CONFIG_PHY_OPTS = 0x969f3b09eceb805f
|
||||
CONFIG_PHY_LED_GPIO = 0x7b392a394de9f948
|
||||
CONFIG_PHY_LED_BTNESS = 0x76a85945985d02fd
|
||||
|
||||
class RESP(IntEnum):
|
||||
KEY_AGREEMENT = 0x01
|
||||
@@ -99,7 +97,7 @@ class VendorConfig(Config):
|
||||
|
||||
def enable_device_aut(self, ct):
|
||||
self._call(
|
||||
Config.CMD.VENDOR_PROTOTYPE,
|
||||
VendorConfig.CMD.CONFIG_VENDOR_PROTOTYPE,
|
||||
{
|
||||
VendorConfig.PARAM.VENDOR_COMMAND_ID: VendorConfig.CMD.CONFIG_AUT_ENABLE,
|
||||
VendorConfig.PARAM.VENDOR_AUT_CT: ct
|
||||
@@ -108,12 +106,48 @@ class VendorConfig(Config):
|
||||
|
||||
def disable_device_aut(self):
|
||||
self._call(
|
||||
Config.CMD.VENDOR_PROTOTYPE,
|
||||
VendorConfig.CMD.CONFIG_VENDOR_PROTOTYPE,
|
||||
{
|
||||
VendorConfig.PARAM.VENDOR_COMMAND_ID: VendorConfig.CMD.CONFIG_AUT_DISABLE
|
||||
},
|
||||
)
|
||||
|
||||
def vidpid(self, vid, pid):
|
||||
self._call(
|
||||
VendorConfig.CMD.CONFIG_VENDOR_PHY,
|
||||
{
|
||||
VendorConfig.PARAM.VENDOR_COMMAND_ID: VendorConfig.CMD.CONFIG_PHY_VIDPID,
|
||||
VendorConfig.PARAM.VENDOR_PARAM: (vid & 0xFFFF) << 16 | pid
|
||||
},
|
||||
)
|
||||
|
||||
def led_gpio(self, gpio):
|
||||
self._call(
|
||||
VendorConfig.CMD.CONFIG_VENDOR_PHY,
|
||||
{
|
||||
VendorConfig.PARAM.VENDOR_COMMAND_ID: VendorConfig.CMD.CONFIG_PHY_LED_GPIO,
|
||||
VendorConfig.PARAM.VENDOR_PARAM: gpio
|
||||
},
|
||||
)
|
||||
|
||||
def led_brightness(self, brightness):
|
||||
self._call(
|
||||
VendorConfig.CMD.CONFIG_VENDOR_PHY,
|
||||
{
|
||||
VendorConfig.PARAM.VENDOR_COMMAND_ID: VendorConfig.CMD.CONFIG_PHY_LED_BTNESS,
|
||||
VendorConfig.PARAM.VENDOR_PARAM: brightness
|
||||
},
|
||||
)
|
||||
|
||||
def phy_opts(self, opts):
|
||||
self._call(
|
||||
VendorConfig.CMD.CONFIG_VENDOR_PHY,
|
||||
{
|
||||
VendorConfig.PARAM.VENDOR_COMMAND_ID: VendorConfig.CMD.CONFIG_PHY_OPTS,
|
||||
VendorConfig.PARAM.VENDOR_PARAM: opts
|
||||
},
|
||||
)
|
||||
|
||||
class Ctap2Vendor(Ctap2):
|
||||
def __init__(self, device: CtapDevice, strict_cbor: bool = True):
|
||||
super().__init__(device=device, strict_cbor=strict_cbor)
|
||||
@@ -198,6 +232,8 @@ class Vendor:
|
||||
VENDOR_MSE = 0x02
|
||||
VENDOR_UNLOCK = 0x03
|
||||
VENDOR_EA = 0x04
|
||||
VENDOR_PHY = 0x05
|
||||
VENDOR_MEMORY = 0x06
|
||||
|
||||
@unique
|
||||
class PARAM(IntEnum):
|
||||
@@ -215,6 +251,10 @@ class Vendor:
|
||||
PARAM = 0x01
|
||||
COSE_KEY = 0x02
|
||||
|
||||
class PHY_OPTS(IntEnum):
|
||||
PHY_OPT_WCID = 0x1
|
||||
PHY_OPT_DIMM = 0x2
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
ctap: Ctap2Vendor,
|
||||
@@ -230,7 +270,7 @@ class Vendor:
|
||||
self.__key_enc = None
|
||||
self.__iv = None
|
||||
|
||||
self.vcfg = VendorConfig(ctap)
|
||||
self.vcfg = VendorConfig(ctap, pin_uv_protocol=pin_uv_protocol, pin_uv_token=pin_uv_token)
|
||||
|
||||
def _call(self, cmd, sub_cmd, params=None):
|
||||
if params:
|
||||
@@ -252,6 +292,14 @@ class Vendor:
|
||||
return self.ctap.vendor(cmd, sub_cmd, params, pin_uv_protocol, pin_uv_param)
|
||||
|
||||
def backup_save(self, filename):
|
||||
if (platform.system() == 'Windows' or platform.system() == 'Linux'):
|
||||
from secure_key import windows as skey
|
||||
elif (platform.system() == 'Darwin'):
|
||||
from secure_key import macos as skey
|
||||
else:
|
||||
print('ERROR: platform not supported')
|
||||
sys.exit(-1)
|
||||
from words import words
|
||||
ret = self._call(
|
||||
Vendor.CMD.VENDOR_BACKUP,
|
||||
Vendor.SUBCMD.ENABLE,
|
||||
@@ -270,6 +318,14 @@ class Vendor:
|
||||
print(f'{(c+1):02d} - {words[coef]}')
|
||||
|
||||
def backup_load(self, filename):
|
||||
if (platform.system() == 'Windows' or platform.system() == 'Linux'):
|
||||
from secure_key import windows as skey
|
||||
elif (platform.system() == 'Darwin'):
|
||||
from secure_key import macos as skey
|
||||
else:
|
||||
print('ERROR: platform not supported')
|
||||
sys.exit(-1)
|
||||
from words import words
|
||||
d = 0
|
||||
if (d == 0):
|
||||
for c in range(24):
|
||||
@@ -349,6 +405,13 @@ class Vendor:
|
||||
)
|
||||
|
||||
def _get_key_device(self):
|
||||
if (platform.system() == 'Windows' or platform.system() == 'Linux'):
|
||||
from secure_key import windows as skey
|
||||
elif (platform.system() == 'Darwin'):
|
||||
from secure_key import macos as skey
|
||||
else:
|
||||
print('ERROR: platform not supported')
|
||||
sys.exit(-1)
|
||||
return skey.get_secure_key()
|
||||
|
||||
def get_skey(self):
|
||||
@@ -378,9 +441,52 @@ class Vendor:
|
||||
}
|
||||
)
|
||||
|
||||
def vidpid(self, vid, pid):
|
||||
return self.vcfg.vidpid(vid, pid)
|
||||
|
||||
def led_gpio(self, gpio):
|
||||
return self.vcfg.led_gpio(gpio)
|
||||
|
||||
def led_brightness(self, brightness):
|
||||
if (brightness > 15):
|
||||
print('ERROR: Brightness must be between 0 and 15')
|
||||
return
|
||||
return self.vcfg.led_brightness(brightness)
|
||||
|
||||
def led_dimmable(self, onoff):
|
||||
opts = self.phy_opts()
|
||||
if (onoff):
|
||||
opts |= Vendor.PHY_OPTS.PHY_OPT_DIMM
|
||||
else:
|
||||
opts &= ~Vendor.PHY_OPTS.PHY_OPT_DIMM
|
||||
print(f'opts: {opts}')
|
||||
return self.vcfg.phy_opts(opts)
|
||||
|
||||
def wcid(self, onoff):
|
||||
opts = self.phy_opts()
|
||||
if (onoff):
|
||||
opts |= Vendor.PHY_OPTS.PHY_OPT_WCID
|
||||
else:
|
||||
opts &= ~Vendor.PHY_OPTS.PHY_OPT_WCID
|
||||
return self.vcfg.phy_opts(opts)
|
||||
|
||||
def phy_opts(self):
|
||||
return self._call(
|
||||
Vendor.CMD.VENDOR_PHY,
|
||||
Vendor.SUBCMD.ENABLE,
|
||||
)[Vendor.RESP.PARAM]
|
||||
|
||||
def memory(self):
|
||||
resp = self._call(
|
||||
Vendor.CMD.VENDOR_MEMORY,
|
||||
Vendor.SUBCMD.ENABLE,
|
||||
)
|
||||
return { 'free': resp[1], 'used': resp[2], 'total': resp[3], 'files': resp[4], 'size': resp[5] }
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser()
|
||||
subparser = parser.add_subparsers(title="commands", dest="command")
|
||||
parser.add_argument('-p','--pin', help='Specify the PIN of the device.', required=True)
|
||||
parser_secure = subparser.add_parser('secure', help='Manages security of Pico Fido.')
|
||||
parser_secure.add_argument('subcommand', choices=['enable', 'disable', 'unlock'], help='Enables, disables or unlocks the security.')
|
||||
|
||||
@@ -392,6 +498,21 @@ def parse_args():
|
||||
parser_attestation.add_argument('subcommand', choices=['csr'])
|
||||
parser_attestation.add_argument('--filename', help='Uploads the certificate filename to the device as enterprise attestation certificate. If not provided, it will generate an enterprise attestation certificate automatically.')
|
||||
|
||||
parser_phy = subparser.add_parser('phy', help='Set PHY options.')
|
||||
subparser_phy = parser_phy.add_subparsers(title='commands', dest='subcommand', required=True)
|
||||
parser_phy_vp = subparser_phy.add_parser('vidpid', help='Sets VID/PID. Use VID:PID format (e.g. 1234:5678)')
|
||||
parser_phy_vp.add_argument('value', help='Value of the PHY option.', metavar='VAL', nargs='?')
|
||||
parser_phy_ledn = subparser_phy.add_parser('led_gpio', help='Sets LED GPIO number.')
|
||||
parser_phy_ledn.add_argument('value', help='Value of the PHY option.', metavar='VAL', nargs='?')
|
||||
parser_phy_optwcid = subparser_phy.add_parser('wcid', help='Enable/Disable Web CCID interface.')
|
||||
parser_phy_optwcid.add_argument('value', choices=['enable', 'disable'], help='Enable/Disable Web CCID interface.', nargs='?')
|
||||
parser_phy_ledbtness = subparser_phy.add_parser('led_brightness', help='Sets LED max. brightness.')
|
||||
parser_phy_ledbtness.add_argument('value', help='Value of the max. brightness.', metavar='VAL', nargs='?')
|
||||
parser_phy_optdimm = subparser_phy.add_parser('led_dimmable', help='Enable/Disable LED dimming.')
|
||||
parser_phy_optdimm.add_argument('value', choices=['enable', 'disable'], help='Enable/Disable LED dimming.', nargs='?')
|
||||
|
||||
parser_mem = subparser.add_parser('memory', help='Get current memory usage.')
|
||||
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
@@ -425,16 +546,51 @@ def attestation(vdr, args):
|
||||
cert = x509.load_pem_x509_certificate(dataf)
|
||||
vdr.upload_ea(cert.public_bytes(Encoding.DER))
|
||||
|
||||
def phy(vdr, args):
|
||||
val = args.value if 'value' in args else None
|
||||
if (val):
|
||||
if (args.subcommand == 'vidpid'):
|
||||
sp = val.split(':')
|
||||
if (len(sp) != 2):
|
||||
print('ERROR: VID/PID have wrong format. Use VID:PID format (e.g. 1234:5678)')
|
||||
ret = vdr.vidpid(int(sp[0],16), int(sp[1],16))
|
||||
elif (args.subcommand == 'led_gpio'):
|
||||
val = int(val)
|
||||
ret = vdr.led_gpio(val)
|
||||
elif (args.subcommand == 'led_brightness'):
|
||||
val = int(val)
|
||||
ret = vdr.led_brightness(val)
|
||||
elif (args.subcommand == 'led_dimmable'):
|
||||
ret = vdr.led_dimmable(val == 'enable')
|
||||
elif (args.subcommand == 'wcid'):
|
||||
ret = vdr.wcid(val == 'enable')
|
||||
|
||||
if (ret):
|
||||
print(f'Current value: {hexlify(ret)}')
|
||||
else:
|
||||
print('Command executed successfully. Please, restart your Pico Key.')
|
||||
|
||||
def memory(vdr, args):
|
||||
mem = vdr.memory()
|
||||
print(f'Memory usage:')
|
||||
print(f'\tFree: {mem["free"]/1024:.2f} kilobytes ({mem["free"]*100/mem["total"]:.2f}%)')
|
||||
print(f'\tUsed: {mem["used"]/1024:.2f} kilobytes ({mem["used"]*100/mem["total"]:.2f}%)')
|
||||
print(f'\tTotal: {mem["total"]/1024:.2f} kilobytes')
|
||||
print(f'\tFlash size: {mem["size"]/1024:.2f} kilobytes')
|
||||
print(f'\tFiles: {mem["files"]}')
|
||||
|
||||
def main(args):
|
||||
print('Pico Fido Tool v1.4')
|
||||
print('Pico Fido Tool v1.10')
|
||||
print('Author: Pol Henarejos')
|
||||
print('Report bugs to https://github.com/polhenarejos/pico-fido/issues')
|
||||
print('')
|
||||
print('')
|
||||
|
||||
dev = next(CtapHidDevice.list_devices(), None)
|
||||
|
||||
vdr = Vendor(Ctap2Vendor(dev))
|
||||
ctap = Ctap2Vendor(dev)
|
||||
client_pin = ClientPin(ctap)
|
||||
token = client_pin.get_pin_token(args.pin, permissions=ClientPin.PERMISSION.AUTHENTICATOR_CFG)
|
||||
vdr = Vendor(ctap, pin_uv_protocol=PinProtocolV2(), pin_uv_token=token)
|
||||
|
||||
if (args.command == 'secure'):
|
||||
secure(vdr, args)
|
||||
@@ -442,6 +598,10 @@ def main(args):
|
||||
backup(vdr, args)
|
||||
elif (args.command == 'attestation'):
|
||||
attestation(vdr, args)
|
||||
elif (args.command == 'phy'):
|
||||
phy(vdr, args)
|
||||
elif (args.command == 'memory'):
|
||||
memory(vdr, args)
|
||||
|
||||
def run():
|
||||
args = parse_args()
|
||||
|
||||
@@ -51,7 +51,9 @@ def get_secure_key():
|
||||
try:
|
||||
backend = get_backend(False)
|
||||
key = backend.get_password(DOMAIN, USERNAME)[0]
|
||||
except keyring.errors.KeyringError:
|
||||
if (key is None):
|
||||
raise TypeError
|
||||
except (keyring.errors.KeyringError, TypeError):
|
||||
try:
|
||||
key = generate_secure_key(False)[0] # It should be True, but secure enclave causes python segfault
|
||||
except keyring.errors.PasswordSetError:
|
||||
|
||||
44
tools/secure_key/windows.py
Normal file
44
tools/secure_key/windows.py
Normal file
@@ -0,0 +1,44 @@
|
||||
import sys
|
||||
|
||||
DOMAIN = "PicoKeys.com"
|
||||
USERNAME = "Pico-Fido"
|
||||
|
||||
try:
|
||||
import keyring
|
||||
except:
|
||||
print('ERROR: keyring module not found! Install keyring package.\nTry with `pip install keyring`')
|
||||
sys.exit(-1)
|
||||
|
||||
try:
|
||||
from cryptography.hazmat.primitives.serialization import Encoding, PrivateFormat, NoEncryption, load_pem_private_key
|
||||
from cryptography.hazmat.primitives.asymmetric import ec
|
||||
except:
|
||||
print('ERROR: cryptography module not found! Install cryptography package.\nTry with `pip install cryptography`')
|
||||
sys.exit(-1)
|
||||
|
||||
|
||||
|
||||
def generate_secure_key():
|
||||
pkey = ec.generate_private_key(ec.SECP256R1())
|
||||
set_secure_key(pkey)
|
||||
return keyring.get_password(DOMAIN, USERNAME)
|
||||
|
||||
def get_d(key):
|
||||
return load_pem_private_key(key, password=None).private_numbers().private_value.to_bytes(32, 'big')
|
||||
|
||||
def set_secure_key(pk):
|
||||
try:
|
||||
keyring.delete_password(DOMAIN, USERNAME)
|
||||
except:
|
||||
pass
|
||||
keyring.set_password(DOMAIN, USERNAME, pk.private_bytes(Encoding.PEM, PrivateFormat.PKCS8, NoEncryption()).decode())
|
||||
|
||||
def get_secure_key():
|
||||
key = None
|
||||
try:
|
||||
key = keyring.get_password(DOMAIN, USERNAME)
|
||||
if (key is None):
|
||||
raise TypeError
|
||||
except (keyring.errors.KeyringError, TypeError):
|
||||
key = generate_secure_key()
|
||||
return get_d(key.encode())
|
||||
@@ -2,12 +2,54 @@
|
||||
|
||||
git submodule update --init --recursive
|
||||
sudo apt update
|
||||
|
||||
if [[ $1 == "pico" ]]; then
|
||||
sudo apt install -y cmake gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib
|
||||
git clone https://github.com/raspberrypi/pico-sdk
|
||||
cd pico-sdk
|
||||
git checkout tags/2.1.1
|
||||
git submodule update --init
|
||||
cd ..
|
||||
git clone https://github.com/raspberrypi/picotool
|
||||
cd picotool
|
||||
git submodule update --init
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DPICO_SDK_PATH=../../pico-sdk ..
|
||||
make -j`nproc`
|
||||
sudo make install
|
||||
cd ../..
|
||||
mkdir build_pico
|
||||
cd build_pico
|
||||
cmake -DPICO_SDK_PATH=../pico-sdk ..
|
||||
make
|
||||
cd ..
|
||||
elif [[ $1 == "esp32" ]]; then
|
||||
sudo apt install -y git wget flex bison gperf python3 python3-pip python3-venv cmake ninja-build ccache libffi-dev libssl-dev dfu-util libusb-1.0-0
|
||||
git clone --recursive https://github.com/espressif/esp-idf.git
|
||||
cd esp-idf
|
||||
./install.sh esp32s3
|
||||
. ./export.sh
|
||||
cd ..
|
||||
idf.py set-target esp32s3
|
||||
idf.py all
|
||||
mkdir -p release
|
||||
cd build
|
||||
esptool.py --chip ESP32-S3 merge_bin -o ../release/pico_fido_esp32-s3.bin @flash_args
|
||||
cd ..
|
||||
cd esp-idf
|
||||
./install.sh esp32s2
|
||||
. ./export.sh
|
||||
cd ..
|
||||
idf.py set-target esp32s2
|
||||
idf.py all
|
||||
mkdir -p release
|
||||
cd build
|
||||
esptool.py --chip ESP32-S2 merge_bin -o ../release/pico_fido_esp32-s2.bin @flash_args
|
||||
cd ..
|
||||
else
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DENABLE_EMULATION=1 ..
|
||||
make
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user