Compare commits
425 Commits
sanitize
...
reset-time
Author | SHA1 | Date | |
---|---|---|---|
c555e4ce46 | |||
299e91b91b | |||
cbf40f4ec7 | |||
8d93f88631 | |||
5f8a9a44fc | |||
8aa1f4ad01 | |||
04cffb6509 | |||
f002d08071 | |||
e53b83257d | |||
05e149fb17 | |||
530e175ad1 | |||
6cd3873b37 | |||
241f58657b | |||
3b42289cce | |||
b3712b57fc | |||
37769bb735 | |||
d677f8c346 | |||
98bcf647c4 | |||
682a443f4e | |||
a28a05673f | |||
3a70ee0ec6 | |||
872a320abc | |||
3cbf7ec451 | |||
748c552eea | |||
98f996fcfe | |||
97eb6bba8a | |||
fdc5a68fcd | |||
1c1005a0e8 | |||
4831410111 | |||
05bc8bee55 | |||
7112633779 | |||
79b43a90fd | |||
ec7a6fd740 | |||
f2d6698066 | |||
3c9315e34c | |||
8ed7157bfe | |||
28a1b1cc06 | |||
5738bcc7a3 | |||
4fb166631d | |||
1b862d3b0c | |||
094420b32b | |||
349cbc39f2 | |||
e294cb3458 | |||
da31f984dd | |||
9d3e8c06fc | |||
b378bbf61d | |||
04b89a9739 | |||
0d28a7bcf6 | |||
aeafd09007 | |||
8b6148ac90 | |||
15a4fdfa66 | |||
e713daba26 | |||
b78f2cd2e7 | |||
601c98000a | |||
ab1c9417b1 | |||
f6d96013e1 | |||
f74dba7ff0 | |||
794accf3dc | |||
2ca0ced808 | |||
17b430fd44 | |||
0d4197fb2c | |||
f74a77d80b | |||
5f1d61a3ba | |||
46f2920e63 | |||
53427c4279 | |||
ac10933379 | |||
8a44d14fef | |||
1d59bbfdd4 | |||
54c66d80b6 | |||
6217fc34b9 | |||
af23e84a8d | |||
9650d99b34 | |||
145b04750e | |||
078acbc4b4 | |||
670a4e5d62 | |||
de55e521cc | |||
bbfe51499f | |||
6cb15a6482 | |||
85ddc40036 | |||
1d63154699 | |||
ee55bf3ba0 | |||
3b4b6dd4fe | |||
28e607ddac | |||
dcd256faf4 | |||
85365c635d | |||
a388607dab | |||
d266e7927c | |||
0ac074e8a8 | |||
67eb721da2 | |||
6b5d353501 | |||
bacce7d978 | |||
975cdf02f2 | |||
21f3a0d10f | |||
b535b41d92 | |||
fd32cc0761 | |||
78dd2a10d3 | |||
fa1bb0dce5 | |||
169dfd2f0d | |||
dafd974d93 | |||
712fde6858 | |||
fcc2e86a6d | |||
8b146c4a16 | |||
a1a79b05fd | |||
c0df8b680d | |||
9ac2aa90c3 | |||
d33749fc16 | |||
96a2cbcb41 | |||
7212982385 | |||
89e218e561 | |||
666cd6a0ba | |||
b4f59ec355 | |||
b7d74cc99f | |||
375a607356 | |||
ea8409c072 | |||
04f06b3b0d | |||
a57c5170e1 | |||
aaffce4021 | |||
463a8b444d | |||
44ed3ceea5 | |||
30f73b41e4 | |||
6f6e831fba | |||
9fd608d3ee | |||
765dc27b15 | |||
48147a39df | |||
6c48d75e44 | |||
fca1e9d405 | |||
0fbc28fbc1 | |||
2a02d0de33 | |||
00b09e0d40 | |||
26db2b3f6b | |||
ff88660027 | |||
9ecfda02c5 | |||
9158453830 | |||
08658eb11e | |||
49d79fa5da | |||
69a7191860 | |||
a2fd507f45 | |||
a58658e35d | |||
bb2929b28f | |||
8e0eda8ed4 | |||
0ebe0ff502 | |||
7bcb7ea840 | |||
811a57f7ab | |||
5168afa16e | |||
208d26be89 | |||
45293fe998 | |||
a1a42fec5c | |||
8c256298ae | |||
01b928c0ec | |||
018a4d394c | |||
7a75fba6d3 | |||
c61f15a090 | |||
f072561899 | |||
6652feb4a2 | |||
fc7ea68d4a | |||
cb116efcc9 | |||
80b9df3e04 | |||
194ef5edcf | |||
006117bb6b | |||
75c75fa897 | |||
2969d09ffa | |||
b871e10d08 | |||
18d39a7047 | |||
a9bbdee35b | |||
321bbe3691 | |||
1ce191343f | |||
9041e5903c | |||
689d471688 | |||
8b9e44c3ed | |||
83dd92d9ba | |||
a5e1dc2a0c | |||
a053bbc669 | |||
3621f2ed4f | |||
3c7bf5a264 | |||
8bf1921263 | |||
e3ff136196 | |||
74181406fe | |||
987b04523d | |||
8023347c8e | |||
9dae7b2e7c | |||
cb13fb65de | |||
7fddd58704 | |||
3a1ea275cc | |||
22293f82f2 | |||
40c3c13b07 | |||
7042b0b656 | |||
ea803aab95 | |||
1100b159a9 | |||
9ddba5dfc3 | |||
35e52f4968 | |||
efddd2f3a8 | |||
17ceb7b9e8 | |||
188a34d1da | |||
9248c6462c | |||
118e129152 | |||
beb5a5892c | |||
d618081dd0 | |||
e4e0a3a84e | |||
3ba9b671fc | |||
69c34f9ca9 | |||
3b4c154fd1 | |||
ccd9a04146 | |||
bde4c09c21 | |||
5d3914bc5e | |||
abe306a649 | |||
41ceb78f6c | |||
8e192f2363 | |||
affc256ca2 | |||
b3ac739a35 | |||
3b53537077 | |||
3fad9a7a7d | |||
8973608f59 | |||
8af6505f6d | |||
d39d7978fd | |||
c972a13034 | |||
a95e62e2ea | |||
c79b7abfb6 | |||
dfb124dc8b | |||
972760eb78 | |||
0d621d13f9 | |||
32f920e372 | |||
a5aff478dd | |||
728acc1671 | |||
62b4418dac | |||
8059a9765f | |||
b743d5fac5 | |||
dccfb0d1b3 | |||
a72f0ede05 | |||
adcbd3aeb8 | |||
d931954a13 | |||
b706cc30b0 | |||
57fe39704b | |||
4b6619b705 | |||
a5877f518f | |||
5a0cc0d02c | |||
b452e3dfe4 | |||
7f82233d17 | |||
8e3753e711 | |||
816ca21f08 | |||
6c60a37e8a | |||
ee351421cb | |||
bac576f3a0 | |||
6e637299e5 | |||
43b3e93854 | |||
5a448d636c | |||
7be0553377 | |||
095b08e3d9 | |||
89e021003a | |||
4f3d4b09eb | |||
3f4843b03a | |||
26af0c423e | |||
19422d9daa | |||
b7a4cf001a | |||
3927aec06d | |||
f5794481ae | |||
caac9d0cc1 | |||
ffadab05a3 | |||
2423154fee | |||
cf79b7865d | |||
6f0cf99c92 | |||
7ef68fd5d3 | |||
3be8611fcf | |||
21489658a7 | |||
a07a3dee8d | |||
416da63a9a | |||
027fa791a3 | |||
3e52d7b42b | |||
301e18c6a2 | |||
44205141eb | |||
6e1110ca9b | |||
9105b988e2 | |||
14c94ea8f5 | |||
435b908c17 | |||
78280e570b | |||
36aec9f20b | |||
b5d3276df6 | |||
ffd854a303 | |||
349a84dcec | |||
6c6a9bc5b6 | |||
e1528cb248 | |||
e0955c190e | |||
ef1e9bc1a1 | |||
f6c7dbdbdd | |||
ea1969ca94 | |||
c14bc76ddf | |||
f7fd30b9ae | |||
11763b6502 | |||
ae5d341c72 | |||
afed99bae9 | |||
d6078ce015 | |||
d630254224 | |||
b2ac7b5479 | |||
c050b82210 | |||
ba8119b370 | |||
d350f9a638 | |||
53275e0182 | |||
2e14ac5b11 | |||
a760bed29f | |||
e854184a11 | |||
87c2076107 | |||
9be192b82f | |||
e95233bb5f | |||
2a2145b7f2 | |||
7cfd0ccb19 | |||
7d84caa754 | |||
f7dd595d84 | |||
04570aaf15 | |||
1a3aad9761 | |||
9545dacc5d | |||
b5e592adc9 | |||
1cd6692246 | |||
17884505b4 | |||
c2312fa69a | |||
832f06b6b4 | |||
4ab5477f96 | |||
434f2f89ec | |||
9174c00abf | |||
46111e27e3 | |||
763995763f | |||
4f317863e9 | |||
8f9aeebb9b | |||
86c6b3cbc8 | |||
a53dfb2812 | |||
04d6cffcb6 | |||
76e46605e3 | |||
dc94e73c62 | |||
018311cfab | |||
c37638d4cb | |||
81a67eda24 | |||
70c176e9ef | |||
7e39425e98 | |||
25bf9d8e54 | |||
2e67a5f772 | |||
66befb96b8 | |||
257f6d1ed5 | |||
b4b59a6d4d | |||
c18a08b14b | |||
c249148d80 | |||
02a51454b7 | |||
4e3420f19f | |||
6b8e575fac | |||
28788d1eeb | |||
1ee031ae9c | |||
6dfe42e486 | |||
29b2032dae | |||
f9f1e96c73 | |||
e2738d11d3 | |||
a8c7c43e14 | |||
db479850a6 | |||
09d450ed02 | |||
be37ed46f7 | |||
420d052ac9 | |||
3698c942a2 | |||
17a170bb90 | |||
d4e61421b6 | |||
690d7c716a | |||
78e3b291c2 | |||
b47854c335 | |||
2af747ddaa | |||
9ead11de8d | |||
f17faca689 | |||
ca66b6e43b | |||
1cd1b3c295 | |||
df2cff2350 | |||
f5d50e001d | |||
235785b225 | |||
303c42901a | |||
df2f950e69 | |||
10bf4242e1 | |||
9e95b0075c | |||
ddbe31776c | |||
645ca6a5a0 | |||
15fc39faed | |||
a1eedc0048 | |||
89e00482e4 | |||
533ce39237 | |||
63ee003535 | |||
fa9408d5d6 | |||
ed9689435d | |||
24a006068d | |||
315b6564ab | |||
4d9285085f | |||
2272e69e15 | |||
151e1d0e9b | |||
d1df8b8b77 | |||
cb76c34ed2 | |||
f2ebaf6abe | |||
4845d2c172 | |||
75b1d9cd01 | |||
26bc8a2889 | |||
88a8eba424 | |||
d2c85881e6 | |||
236498ee03 | |||
a51c9192b1 | |||
4dc6bcf771 | |||
cce81b23d9 | |||
8c2e2386a9 | |||
c783a1442a | |||
b61e5db736 | |||
b41cd5d5b8 | |||
b42e990f67 | |||
ff53bb1e32 | |||
2d72e02051 | |||
91c77da179 | |||
795cf5c4a1 | |||
d1722b85af | |||
2c500fe25a | |||
751b2fd69c | |||
c2216929a9 | |||
a51417bf61 | |||
3f225f362f | |||
dd4ff920ad | |||
bddd60c080 | |||
ba581db49c | |||
3a5cd786dc | |||
5f878ff022 | |||
14f91a6e15 | |||
4fad28ea47 | |||
0ff9870612 | |||
cd29a0e0fe | |||
46b7f9a778 | |||
31328fe7e7 | |||
035b1a8632 | |||
b1563dbe94 | |||
2a9e3ac576 |
228
.all-contributorsrc
Normal file
228
.all-contributorsrc
Normal file
@ -0,0 +1,228 @@
|
||||
{
|
||||
"files": [
|
||||
"README.md"
|
||||
],
|
||||
"imageSize": 100,
|
||||
"commit": false,
|
||||
"contributors": [
|
||||
{
|
||||
"login": "szszszsz",
|
||||
"name": "Szczepan Zalega",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/17005426?v=4",
|
||||
"profile": "https://github.com/szszszsz",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc",
|
||||
"ideas"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Wesseldr",
|
||||
"name": "Wessel dR",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/4012809?v=4",
|
||||
"profile": "https://github.com/Wesseldr",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "agl",
|
||||
"name": "Adam Langley",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/21203?v=4",
|
||||
"profile": "https://www.imperialviolet.org",
|
||||
"contributions": [
|
||||
"bug",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "merlokk",
|
||||
"name": "Oleg Moiseenko",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/807634?v=4",
|
||||
"profile": "http://www.lotteam.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "aseigler",
|
||||
"name": "Alex Seigler",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/6605560?v=4",
|
||||
"profile": "https://github.com/aseigler",
|
||||
"contributions": [
|
||||
"bug"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "dschuermann",
|
||||
"name": "Dominik Schürmann",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/321888?v=4",
|
||||
"profile": "https://www.cotech.de/services/",
|
||||
"contributions": [
|
||||
"bug"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ehershey",
|
||||
"name": "Ernie Hershey",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/286008?v=4",
|
||||
"profile": "https://github.com/ehershey",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "YakBizzarro",
|
||||
"name": "Andrea Corna",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/767740?v=4",
|
||||
"profile": "https://github.com/YakBizzarro",
|
||||
"contributions": [
|
||||
"infra"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "pjz",
|
||||
"name": "Paul Jimenez",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/11100?v=4",
|
||||
"profile": "https://place.org/~pj/",
|
||||
"contributions": [
|
||||
"infra",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "yparitcher",
|
||||
"name": "yparitcher",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/38916402?v=4",
|
||||
"profile": "https://github.com/yparitcher",
|
||||
"contributions": [
|
||||
"ideas",
|
||||
"maintenance"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "StoyanDimitrov",
|
||||
"name": "StoyanDimitrov",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/10962709?v=4",
|
||||
"profile": "https://github.com/StoyanDimitrov",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "alphathegeek",
|
||||
"name": "alphathegeek",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/51253712?v=4",
|
||||
"profile": "https://github.com/alphathegeek",
|
||||
"contributions": [
|
||||
"ideas"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "rgerganov",
|
||||
"name": "Radoslav Gerganov",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/271616?v=4",
|
||||
"profile": "https://xakcop.com",
|
||||
"contributions": [
|
||||
"ideas",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "manuel-domke",
|
||||
"name": "Manuel Domke",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/10274356?v=4",
|
||||
"profile": "http://13-37.org",
|
||||
"contributions": [
|
||||
"ideas",
|
||||
"code",
|
||||
"business"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "esden",
|
||||
"name": "Piotr Esden-Tempski",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/17334?v=4",
|
||||
"profile": "http://1bitsquared.com",
|
||||
"contributions": [
|
||||
"business"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "m3hm00d",
|
||||
"name": "f.m3hm00d",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/42179593?v=4",
|
||||
"profile": "https://github.com/m3hm00d",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "hughsie",
|
||||
"name": "Richard Hughes",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/151380?v=4",
|
||||
"profile": "http://blogs.gnome.org/hughsie/",
|
||||
"contributions": [
|
||||
"ideas",
|
||||
"code",
|
||||
"infra",
|
||||
"tool"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "kimusan",
|
||||
"name": "Kim Schulz",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/1150049?v=4",
|
||||
"profile": "http://www.schulz.dk",
|
||||
"contributions": [
|
||||
"business",
|
||||
"ideas"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "oplik0",
|
||||
"name": "Jakub",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/25460763?v=4",
|
||||
"profile": "https://github.com/oplik0",
|
||||
"contributions": [
|
||||
"bug"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jolo1581",
|
||||
"name": "Jan A.",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/53423977?v=4",
|
||||
"profile": "https://github.com/jolo1581",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ccinelli",
|
||||
"name": "ccinelli",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/38021940?v=4",
|
||||
"profile": "https://github.com/ccinelli",
|
||||
"contributions": [
|
||||
"infra",
|
||||
"test"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Nitrokey",
|
||||
"name": "Nitrokey",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/9438831?v=4",
|
||||
"profile": "https://www.nitrokey.com",
|
||||
"contributions": [
|
||||
"code",
|
||||
"test",
|
||||
"ideas"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
"projectName": "solo",
|
||||
"projectOwner": "solokeys",
|
||||
"repoType": "github",
|
||||
"repoHost": "https://github.com"
|
||||
}
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -34,7 +34,8 @@
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
targets/*/*.hex
|
||||
targets/*/*.sha2
|
||||
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
@ -83,3 +84,5 @@ targets/*/docs/
|
||||
main
|
||||
|
||||
builds/*
|
||||
tools/testing/.idea/*
|
||||
tools/testing/tests/__pycache__/*
|
||||
|
@ -6,14 +6,15 @@ addons:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- gcc-7
|
||||
- gcc-8
|
||||
- cppcheck
|
||||
services:
|
||||
- docker
|
||||
before_install:
|
||||
- sudo add-apt-repository -y ppa:team-gcc-arm-embedded/ppa
|
||||
- sudo apt-get update -q
|
||||
- sudo apt-get install -y gcc-arm-embedded
|
||||
- sudo apt-get install -y python3-venv
|
||||
- sudo apt-get install -y gcc-arm-embedded python3-venv
|
||||
script:
|
||||
- export CC=gcc-7
|
||||
- export CC=gcc-8
|
||||
- pyenv shell 3.6.7
|
||||
- make travis
|
||||
|
61
Dockerfile
61
Dockerfile
@ -1,33 +1,38 @@
|
||||
FROM debian:stretch-slim
|
||||
FROM debian:9.11-slim
|
||||
MAINTAINER SoloKeys <hello@solokeys.com>
|
||||
|
||||
RUN apt-get update -qq
|
||||
RUN apt-get install -qq bzip2 git make wget >/dev/null
|
||||
# Install necessary packages
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
ca-certificates \
|
||||
make \
|
||||
wget \
|
||||
bzip2 \
|
||||
git \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 1. ARM GCC: for compilation
|
||||
RUN wget -q -O gcc.tar.bz2 https://developer.arm.com/-/media/Files/downloads/gnu-rm/8-2018q4/gcc-arm-none-eabi-8-2018-q4-major-linux.tar.bz2?revision=d830f9dd-cd4f-406d-8672-cca9210dd220?product=GNU%20Arm%20Embedded%20Toolchain,64-bit,,Linux,8-2018-q4-major
|
||||
# from website
|
||||
RUN echo "f55f90d483ddb3bcf4dae5882c2094cd gcc.tar.bz2" > gcc.md5
|
||||
RUN md5sum -c gcc.md5
|
||||
# self-generated
|
||||
RUN echo "fb31fbdfe08406ece43eef5df623c0b2deb8b53e405e2c878300f7a1f303ee52 gcc.tar.bz2" > gcc.sha256
|
||||
RUN sha256sum -c gcc.sha256
|
||||
RUN tar -C /opt -xf gcc.tar.bz2
|
||||
# Install ARM compiler
|
||||
RUN set -eux; \
|
||||
url="https://developer.arm.com/-/media/Files/downloads/gnu-rm/8-2019q3/RC1.1/gcc-arm-none-eabi-8-2019-q3-update-linux.tar.bz2?revision=c34d758a-be0c-476e-a2de-af8c6e16a8a2?product=GNU%20Arm%20Embedded%20Toolchain,64-bit,,Linux,8-2019-q3-update"; \
|
||||
wget -O gcc.tar.bz2 "$url"; \
|
||||
echo "6341f11972dac8de185646d0fbd73bfc gcc.tar.bz2" | md5sum -c -; \
|
||||
echo "b50b02b0a16e5aad8620e9d7c31110ef285c1dde28980b1a9448b764d77d8f92 gcc.tar.bz2" | sha256sum -c -; \
|
||||
tar -C /opt -xf gcc.tar.bz2; \
|
||||
rm gcc.tar.bz2;
|
||||
|
||||
# 2. Python3.7: for solo-python (merging etc.)
|
||||
RUN wget -q -O miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-4.5.12-Linux-x86_64.sh
|
||||
# from website
|
||||
RUN echo "866ae9dff53ad0874e1d1a60b1ad1ef8 miniconda.sh" > miniconda.md5
|
||||
RUN md5sum -c miniconda.md5
|
||||
# self-generated
|
||||
RUN echo "e5e5b4cd2a918e0e96b395534222773f7241dc59d776db1b9f7fedfcb489157a miniconda.sh" > miniconda.sha256
|
||||
RUN sha256sum -c miniconda.sha256
|
||||
# Python3.7: for solo-python (merging etc.)
|
||||
RUN set -eux; \
|
||||
url="https://repo.anaconda.com/miniconda/Miniconda3-4.5.12-Linux-x86_64.sh"; \
|
||||
wget -O miniconda.sh "$url"; \
|
||||
echo "866ae9dff53ad0874e1d1a60b1ad1ef8 miniconda.sh" | md5sum -c -; \
|
||||
echo "e5e5b4cd2a918e0e96b395534222773f7241dc59d776db1b9f7fedfcb489157a miniconda.sh" | sha256sum -c -; \
|
||||
bash ./miniconda.sh -b -p /opt/conda; \
|
||||
ln -s /opt/conda/bin/python /usr/local/bin/python3; \
|
||||
ln -s /opt/conda/bin/python /usr/local/bin/python; \
|
||||
ln -s /opt/conda/bin/pip /usr/local/bin/pip3; \
|
||||
ln -s /opt/conda/bin/pip /usr/local/bin/pip; \
|
||||
rm miniconda.sh; \
|
||||
pip install -U pip
|
||||
|
||||
RUN bash ./miniconda.sh -b -p /opt/conda
|
||||
RUN ln -s /opt/conda/bin/python /usr/local/bin/python3
|
||||
RUN ln -s /opt/conda/bin/python /usr/local/bin/python
|
||||
RUN ln -s /opt/conda/bin/pip /usr/local/bin/pip3
|
||||
RUN ln -s /opt/conda/bin/pip /usr/local/bin/pip
|
||||
|
||||
# 3. Source code
|
||||
RUN git clone --recurse-submodules https://github.com/solokeys/solo /solo --config core.autocrlf=input
|
||||
# solo-python (Python3.7 script for merging etc.)
|
||||
RUN pip install -U solo-python
|
88
Makefile
88
Makefile
@ -1,3 +1,5 @@
|
||||
include fido2/version.mk
|
||||
|
||||
#define uECC_arch_other 0
|
||||
#define uECC_x86 1
|
||||
#define uECC_x86_64 2
|
||||
@ -6,44 +8,34 @@
|
||||
#define uECC_arm_thumb2 5
|
||||
#define uECC_arm64 6
|
||||
#define uECC_avr 7
|
||||
|
||||
ecc_platform=2
|
||||
|
||||
src = $(wildcard pc/*.c) $(wildcard fido2/*.c) $(wildcard fido2/extensions/*.c) \
|
||||
$(wildcard crypto/sha256/*.c) crypto/tiny-AES-c/aes.c
|
||||
src = pc/device.c pc/main.c
|
||||
|
||||
obj = $(src:.c=.o) crypto/micro-ecc/uECC.o
|
||||
obj = $(src:.c=.o)
|
||||
|
||||
LIBCBOR = tinycbor/lib/libtinycbor.a
|
||||
LIBSOLO = fido2/libsolo.a
|
||||
|
||||
ifeq ($(shell uname -s),Darwin)
|
||||
export LDFLAGS = -Wl,-dead_strip
|
||||
else
|
||||
export LDFLAGS = -Wl,--gc-sections
|
||||
endif
|
||||
LDFLAGS += $(LIBCBOR)
|
||||
LDFLAGS += $(LIBSOLO) $(LIBCBOR)
|
||||
|
||||
VERSION:=$(shell git describe --abbrev=0 )
|
||||
VERSION_FULL:=$(shell git describe)
|
||||
VERSION_MAJ:=$(shell python -c 'print("$(VERSION)".split(".")[0])')
|
||||
VERSION_MIN:=$(shell python -c 'print("$(VERSION)".split(".")[1])')
|
||||
VERSION_PAT:=$(shell python -c 'print("$(VERSION)".split(".")[2])')
|
||||
|
||||
VERSION_FLAGS= -DSOLO_VERSION_MAJ=$(VERSION_MAJ) -DSOLO_VERSION_MIN=$(VERSION_MIN) \
|
||||
-DSOLO_VERSION_PATCH=$(VERSION_PAT) -DSOLO_VERSION=\"$(VERSION_FULL)\"
|
||||
CFLAGS = -O2 -fdata-sections -ffunction-sections -g
|
||||
ECC_CFLAGS = -O2 -fdata-sections -ffunction-sections -DuECC_PLATFORM=$(ecc_platform)
|
||||
|
||||
CFLAGS = -O2 -fdata-sections -ffunction-sections $(VERSION_FLAGS) -g
|
||||
|
||||
INCLUDES = -I./tinycbor/src -I./crypto/sha256 -I./crypto/micro-ecc/ -Icrypto/tiny-AES-c/ -I./fido2/ -I./pc -I./fido2/extensions
|
||||
INCLUDES += -I./crypto/cifra/src
|
||||
INCLUDES = -I../ -I./fido2/ -I./pc -I../pc -I./tinycbor/src
|
||||
|
||||
CFLAGS += $(INCLUDES)
|
||||
# for crypto/tiny-AES-c
|
||||
CFLAGS += -DAES256=1 -DAPP_CONFIG=\"app.h\"
|
||||
CFLAGS += -DAES256=1 -DSOLO_EXPERIMENTAL=1 -DDEBUG_LEVEL=1
|
||||
|
||||
name = main
|
||||
|
||||
.PHONY: all $(LIBCBOR) black blackcheck cppcheck wink fido2-test clean full-clean travis test clean version
|
||||
.PHONY: all $(LIBCBOR) $(LIBSOLO) black blackcheck cppcheck wink fido2-test clean full-clean travis test clean version
|
||||
all: main
|
||||
|
||||
tinycbor/Makefile crypto/tiny-AES-c/aes.c:
|
||||
@ -53,7 +45,10 @@ tinycbor/Makefile crypto/tiny-AES-c/aes.c:
|
||||
cbor: $(LIBCBOR)
|
||||
|
||||
$(LIBCBOR):
|
||||
cd tinycbor/ && $(MAKE) clean && $(MAKE) LDFLAGS='' -j8
|
||||
cd tinycbor/ && $(MAKE) LDFLAGS='' -j8
|
||||
|
||||
$(LIBSOLO):
|
||||
cd fido2/ && $(MAKE) CFLAGS="$(CFLAGS)" ECC_CFLAGS="$(ECC_CFLAGS)" APP_CONFIG=app.h -j8
|
||||
|
||||
version:
|
||||
@git describe
|
||||
@ -62,16 +57,13 @@ test: venv
|
||||
$(MAKE) clean
|
||||
$(MAKE) -C . main
|
||||
$(MAKE) clean
|
||||
$(MAKE) -C ./targets/stm32l432 test PREFIX=$(PREFIX) "VENV=$(VENV)"
|
||||
$(MAKE) -C ./targets/stm32l432 test PREFIX=$(PREFIX) "VENV=$(VENV)" VERSION_FULL=${SOLO_VERSION_FULL}
|
||||
$(MAKE) clean
|
||||
$(MAKE) cppcheck
|
||||
|
||||
$(name): $(obj) $(LIBCBOR)
|
||||
$(name): $(obj) $(LIBCBOR) $(LIBSOLO)
|
||||
$(CC) $(LDFLAGS) -o $@ $(obj) $(LDFLAGS)
|
||||
|
||||
crypto/micro-ecc/uECC.o: ./crypto/micro-ecc/uECC.c
|
||||
$(CC) -c -o $@ $^ -O2 -fdata-sections -ffunction-sections -DuECC_PLATFORM=$(ecc_platform) -I./crypto/micro-ecc/
|
||||
|
||||
venv:
|
||||
python3 -m venv venv
|
||||
venv/bin/pip -q install --upgrade pip
|
||||
@ -88,18 +80,31 @@ wink: venv
|
||||
fido2-test: venv
|
||||
venv/bin/python tools/ctap_test.py
|
||||
|
||||
DOCKER_IMAGE := "solokeys/solo-firmware:local"
|
||||
SOLO_VERSIONISH := "master"
|
||||
docker-build:
|
||||
docker build -t $(DOCKER_IMAGE) .
|
||||
update:
|
||||
git fetch --tags
|
||||
git checkout master
|
||||
git rebase origin/master
|
||||
git submodule update --init --recursive
|
||||
|
||||
DOCKER_TOOLCHAIN_IMAGE := "solokeys/solo-firmware-toolchain"
|
||||
|
||||
docker-build-toolchain:
|
||||
docker build -t $(DOCKER_TOOLCHAIN_IMAGE) .
|
||||
docker tag $(DOCKER_TOOLCHAIN_IMAGE):latest $(DOCKER_TOOLCHAIN_IMAGE):${SOLO_VERSION}
|
||||
docker tag $(DOCKER_TOOLCHAIN_IMAGE):latest $(DOCKER_TOOLCHAIN_IMAGE):${SOLO_VERSION_MAJ}
|
||||
docker tag $(DOCKER_TOOLCHAIN_IMAGE):latest $(DOCKER_TOOLCHAIN_IMAGE):${SOLO_VERSION_MAJ}.${SOLO_VERSION_MIN}
|
||||
|
||||
uncached-docker-build-toolchain:
|
||||
docker build --no-cache -t $(DOCKER_TOOLCHAIN_IMAGE) .
|
||||
docker tag $(DOCKER_TOOLCHAIN_IMAGE):latest $(DOCKER_TOOLCHAIN_IMAGE):${SOLO_VERSION}
|
||||
docker tag $(DOCKER_TOOLCHAIN_IMAGE):latest $(DOCKER_TOOLCHAIN_IMAGE):${SOLO_VERSION_MAJ}
|
||||
docker tag $(DOCKER_TOOLCHAIN_IMAGE):latest $(DOCKER_TOOLCHAIN_IMAGE):${SOLO_VERSION_MAJ}.${SOLO_VERSION_MIN}
|
||||
|
||||
docker-build-all:
|
||||
docker run --rm -v "$(CURDIR)/builds:/builds" \
|
||||
-v "$(CURDIR)/in-docker-build.sh:/in-docker-build.sh" \
|
||||
$(DOCKER_IMAGE) "./in-docker-build.sh" $(SOLO_VERSIONISH)
|
||||
uncached-docker-build:
|
||||
docker build --no-cache -t $(DOCKER_IMAGE) .
|
||||
docker run --rm -v "$(CURDIR)/builds:/builds" \
|
||||
-v "$(CURDIR)/in-docker-build.sh:/in-docker-build.sh" \
|
||||
$(DOCKER_IMAGE) "./in-docker-build.sh" $(SOLO_VERSIONISH)
|
||||
-v "$(CURDIR):/solo" \
|
||||
-u $(shell id -u ${USER}):$(shell id -g ${USER}) \
|
||||
$(DOCKER_TOOLCHAIN_IMAGE) "solo/in-docker-build.sh" ${SOLO_VERSION_FULL}
|
||||
|
||||
CPPCHECK_FLAGS=--quiet --error-exitcode=2
|
||||
|
||||
@ -116,10 +121,19 @@ clean:
|
||||
(cd `dirname $$f` ; git checkout -- .) ;\
|
||||
fi ;\
|
||||
done
|
||||
cd fido2 && $(MAKE) clean
|
||||
|
||||
full-clean: clean
|
||||
rm -rf venv
|
||||
|
||||
test-docker:
|
||||
rm -rf builds/*
|
||||
$(MAKE) uncached-docker-build-toolchain
|
||||
# Check if there are 4 docker images/tas named "solokeys/solo-firmware-toolchain"
|
||||
NTAGS=$$(docker images | grep -c "solokeys/solo-firmware-toolchain") && [ $$NTAGS -eq 4 ]
|
||||
$(MAKE) docker-build-all
|
||||
|
||||
travis:
|
||||
$(MAKE) test VENV=". ../../venv/bin/activate;"
|
||||
$(MAKE) black
|
||||
$(MAKE) test-docker
|
||||
$(MAKE) black
|
155
README.md
155
README.md
@ -1,23 +1,12 @@
|
||||
[](https://github.com/solokeys/solo/blob/master/LICENSE)
|
||||
[](https://travis-ci.com/solokeys/solo)
|
||||
[](https://discourse.solokeys.com)
|
||||
[](https://update.solokeys.com/)
|
||||
[](https://keybase.io/team/solokeys.public)
|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fsolokeys%2Fsolo?ref=badge_shield)
|
||||
|
||||
[](https://github.com/solokeys/solo/releases)
|
||||
[](https://github.com/solokeys/solo/commits/master)
|
||||
[](https://github.com/solokeys/solo/commits/master)
|
||||
[](https://github.com/solokeys/solo/commits/master)
|
||||
[](https://github.com/solokeys/solo/graphs/contributors)
|
||||
|
||||
|
||||
# Solo
|
||||
[](https://travis-ci.com/solokeys/solo)
|
||||
|
||||
Solo is an open source security key, and you can get one at [solokeys.com](https://solokeys.com).
|
||||
|
||||
Solo supports FIDO2 and U2F standards for strong two-factor authentication and password-less login, and it will protect you against phishing and other online attacks. With colored cases and multilingual guides we want to make secure login more personable and accessible to everyone around the globe.
|
||||
[<img src="https://static.solokeys.com/images/photos/hero-on-white-cropped.png" width="600">](https://solokeys.com)
|
||||
|
||||
<img src="https://static.solokeys.com/images/photos/hero-on-white-cropped.png" width="600">
|
||||
Solo supports FIDO2 and U2F standards for strong two-factor authentication and password-less login, and it will protect you against phishing and other online attacks. With colored cases and multilingual guides we want to make secure login more personable and accessible to everyone around the globe.
|
||||
|
||||
This repo contains the Solo firmware, including implementations of FIDO2 and U2F (CTAP2 and CTAP) over USB and NFC. The main implementation is for STM32L432, but it is easily portable.
|
||||
|
||||
@ -41,12 +30,62 @@ Solo for Hacker is a special version of Solo that let you customize its firmware
|
||||
|
||||
Check out [solokeys.com](https://solokeys.com), for options on where to buy Solo. Solo Hacker can be converted to a secure version, but normal Solo cannot be converted to a Hacker version.
|
||||
|
||||
If you have a Solo for Hacker, here's how you can load your own code on it. You can find more details, including how to permanently lock it, in our [documentation](https://docs.solokeys.io/solo/building/). We only support Python3.
|
||||
If you have a Solo for Hacker, here's how you can load your own code on it. You can find more details, including how to permanently lock it, in our [documentation](https://docs.solokeys.io/solo/building/). We support Python3.
|
||||
|
||||
For example, if you want to turn off any blue light emission, you can edit [`led_rgb()`](https://github.com/solokeys/solo/blob/master/targets/stm32l432/src/app.h#L48) and change `LED_INIT_VALUE`
|
||||
to be a different hex color.
|
||||
|
||||
Then recompile, load your new firmware, and enjoy a different LED color Solo.
|
||||
|
||||
In the Hacker version, hardware is the same but the firmware is unlocked, so you can 1) load an unsigned application, or 2) entirely reflash the key. By contrast, in a regular Solo you can only upgrade to a firmware signed by SoloKeys, and flash is locked and debug disabled permanently.
|
||||
|
||||
Hacker Solo isn't really secure so you should only use it for development. An attacker with physical access to a Solo for Hacker can reflash it following the steps above, and even a malware on your computer could possibly reflash it.
|
||||
|
||||
## Checking out the code
|
||||
```bash
|
||||
git clone --recurse-submodules https://github.com/solokeys/solo
|
||||
cd solo
|
||||
```
|
||||
|
||||
If you forgot the `--recurse-submodules` while cloning, simply run `git submodule update --init --recursive`.
|
||||
|
||||
`make update` will also checkout the latest code on `master` and submodules.
|
||||
|
||||
## Checking out the code to build a specific version
|
||||
|
||||
You can checkout the code to build a specific version of the firmware with:
|
||||
```
|
||||
VERSION_TO_BUILD=2.5.3
|
||||
git fetch --tags
|
||||
git checkout ${VERSION_TO_BUILD}
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
## Installing the toolchain and applying updates
|
||||
|
||||
In order to compile ARM code, you need the ARM compiler and other things like bundling bootloader and firmware require the [solo-python](https://github.com/solokeys/solo-python) python package. Check our [documentation](https://docs.solokeys.io/solo/) for details.
|
||||
|
||||
You can update your solokey after running `pip3 install solo-python` with `solo key update` for the latest version. To apply a custom image use `solo program bootloader <file>(.json|.hex)`.
|
||||
|
||||
## Installing the toolkit and compiling in Docker
|
||||
Alternatively, you can use Docker to create a container with the toolchain.
|
||||
You can run:
|
||||
|
||||
```bash
|
||||
# Build the toolchain container
|
||||
make docker-build-toolchain
|
||||
|
||||
# Build all versions of the firmware in the "builds" folder
|
||||
make docker-build-all
|
||||
```
|
||||
|
||||
The `builds` folder will contain all the variation on the firmware in `.hex` files.
|
||||
|
||||
## Build locally
|
||||
|
||||
If you have the toolchain installed on your machine you can build the firmware with:
|
||||
|
||||
```bash
|
||||
cd targets/stm32l432
|
||||
make cbor
|
||||
make build-hacker
|
||||
@ -58,19 +97,6 @@ solo program aux enter-bootloader
|
||||
solo program bootloader targets/stm32l432/solo.hex
|
||||
```
|
||||
|
||||
Alternatively, run `make docker-build` and use the firmware generated in `/tmp`.
|
||||
|
||||
If you forgot the `--recurse-submodules` when cloning, simply `git submodule update --init --recursive`.
|
||||
|
||||
For example, if you want to turn off any blue light emission, you can edit [`led_rgb()`](https://github.com/solokeys/solo/blob/master/targets/stm32l432/src/app.h#L48) and change `LED_INIT_VALUE`
|
||||
to be a different hex color.
|
||||
|
||||
Then recompile, load your new firmware, and enjoy a different LED color Solo.
|
||||
|
||||
In the Hacker version, hardware is the same but the firmware is unlocked, so you can 1) load an unsigned application, or 2) entirely reflash the key. By contrast, in a regular Solo you can only upgrade to a firmware signed by SoloKeys, and flash is locked and debug disabled permanently.
|
||||
|
||||
Hacker Solo isn't really secure so you should only use it for development. An attacker with physical access to a Solo for Hacker can reflash it following the steps above, and even a malware on your computer could possibly reflash it.
|
||||
|
||||
# Developing Solo (No Hardware Needed)
|
||||
|
||||
Clone Solo and build it
|
||||
@ -94,10 +120,7 @@ Run the Solo application:
|
||||
./main
|
||||
```
|
||||
|
||||
In another shell, you can run client software, for example our tests:
|
||||
```bash
|
||||
python tools/ctap_test.py sim fido2
|
||||
```
|
||||
In another shell, you can run our [test suite](https://github.com/solokeys/fido2-tests).
|
||||
|
||||
You can find more details in our [documentation](https://docs.solokeys.io/solo/), including how to build on the the NUCLEO-L432KC development board.
|
||||
|
||||
@ -107,14 +130,53 @@ You can find more details in our [documentation](https://docs.solokeys.io/solo/)
|
||||
Check out our [official documentation](https://docs.solokeys.io/solo/).
|
||||
|
||||
|
||||
# Contributors
|
||||
# Contributors ✨
|
||||
|
||||
Solo is an upgrade to [U2F Zero](https://github.com/conorpp/u2f-zero). It was born from Conor's passion for making secure hardware, and from our shared belief that security should be open to be trustworthy, in hardware like in software.
|
||||
|
||||
Contributors are welcome. The ultimate goal is to have a FIDO2 security key supporting USB, NFC, and BLE interfaces, that can run on a variety of MCUs.
|
||||
|
||||
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
|
||||
The ultimate goal is to have a FIDO2 security key supporting USB, NFC, and BLE interfaces, that can run on a variety of MCUs.
|
||||
Look at the issues to see what is currently being worked on. Feel free to add issues as well.
|
||||
|
||||
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||
<!-- prettier-ignore -->
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/szszszsz"><img src="https://avatars0.githubusercontent.com/u/17005426?v=4" width="100px;" alt="Szczepan Zalega"/><br /><sub><b>Szczepan Zalega</b></sub></a><br /><a href="https://github.com/solokeys/solo/commits?author=szszszsz" title="Code">💻</a> <a href="https://github.com/solokeys/solo/commits?author=szszszsz" title="Documentation">📖</a> <a href="#ideas-szszszsz" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
<td align="center"><a href="https://github.com/Wesseldr"><img src="https://avatars1.githubusercontent.com/u/4012809?v=4" width="100px;" alt="Wessel dR"/><br /><sub><b>Wessel dR</b></sub></a><br /><a href="https://github.com/solokeys/solo/commits?author=Wesseldr" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://www.imperialviolet.org"><img src="https://avatars3.githubusercontent.com/u/21203?v=4" width="100px;" alt="Adam Langley"/><br /><sub><b>Adam Langley</b></sub></a><br /><a href="https://github.com/solokeys/solo/issues?q=author%3Aagl" title="Bug reports">🐛</a> <a href="https://github.com/solokeys/solo/commits?author=agl" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://www.lotteam.com"><img src="https://avatars2.githubusercontent.com/u/807634?v=4" width="100px;" alt="Oleg Moiseenko"/><br /><sub><b>Oleg Moiseenko</b></sub></a><br /><a href="https://github.com/solokeys/solo/commits?author=merlokk" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/aseigler"><img src="https://avatars1.githubusercontent.com/u/6605560?v=4" width="100px;" alt="Alex Seigler"/><br /><sub><b>Alex Seigler</b></sub></a><br /><a href="https://github.com/solokeys/solo/issues?q=author%3Aaseigler" title="Bug reports">🐛</a></td>
|
||||
<td align="center"><a href="https://www.cotech.de/services/"><img src="https://avatars3.githubusercontent.com/u/321888?v=4" width="100px;" alt="Dominik Schürmann"/><br /><sub><b>Dominik Schürmann</b></sub></a><br /><a href="https://github.com/solokeys/solo/issues?q=author%3Adschuermann" title="Bug reports">🐛</a></td>
|
||||
<td align="center"><a href="https://github.com/ehershey"><img src="https://avatars0.githubusercontent.com/u/286008?v=4" width="100px;" alt="Ernie Hershey"/><br /><sub><b>Ernie Hershey</b></sub></a><br /><a href="https://github.com/solokeys/solo/commits?author=ehershey" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/YakBizzarro"><img src="https://avatars1.githubusercontent.com/u/767740?v=4" width="100px;" alt="Andrea Corna"/><br /><sub><b>Andrea Corna</b></sub></a><br /><a href="#infra-YakBizzarro" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
|
||||
<td align="center"><a href="https://place.org/~pj/"><img src="https://avatars3.githubusercontent.com/u/11100?v=4" width="100px;" alt="Paul Jimenez"/><br /><sub><b>Paul Jimenez</b></sub></a><br /><a href="#infra-pjz" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/solokeys/solo/commits?author=pjz" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/yparitcher"><img src="https://avatars0.githubusercontent.com/u/38916402?v=4" width="100px;" alt="yparitcher"/><br /><sub><b>yparitcher</b></sub></a><br /><a href="#ideas-yparitcher" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-yparitcher" title="Maintenance">🚧</a></td>
|
||||
<td align="center"><a href="https://github.com/StoyanDimitrov"><img src="https://avatars1.githubusercontent.com/u/10962709?v=4" width="100px;" alt="StoyanDimitrov"/><br /><sub><b>StoyanDimitrov</b></sub></a><br /><a href="https://github.com/solokeys/solo/commits?author=StoyanDimitrov" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/alphathegeek"><img src="https://avatars2.githubusercontent.com/u/51253712?v=4" width="100px;" alt="alphathegeek"/><br /><sub><b>alphathegeek</b></sub></a><br /><a href="#ideas-alphathegeek" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
<td align="center"><a href="https://xakcop.com"><img src="https://avatars2.githubusercontent.com/u/271616?v=4" width="100px;" alt="Radoslav Gerganov"/><br /><sub><b>Radoslav Gerganov</b></sub></a><br /><a href="#ideas-rgerganov" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/solokeys/solo/commits?author=rgerganov" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://13-37.org"><img src="https://avatars3.githubusercontent.com/u/10274356?v=4" width="100px;" alt="Manuel Domke"/><br /><sub><b>Manuel Domke</b></sub></a><br /><a href="#ideas-manuel-domke" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/solokeys/solo/commits?author=manuel-domke" title="Code">💻</a> <a href="#business-manuel-domke" title="Business development">💼</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="http://1bitsquared.com"><img src="https://avatars3.githubusercontent.com/u/17334?v=4" width="100px;" alt="Piotr Esden-Tempski"/><br /><sub><b>Piotr Esden-Tempski</b></sub></a><br /><a href="#business-esden" title="Business development">💼</a></td>
|
||||
<td align="center"><a href="https://github.com/m3hm00d"><img src="https://avatars1.githubusercontent.com/u/42179593?v=4" width="100px;" alt="f.m3hm00d"/><br /><sub><b>f.m3hm00d</b></sub></a><br /><a href="https://github.com/solokeys/solo/commits?author=m3hm00d" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://blogs.gnome.org/hughsie/"><img src="https://avatars0.githubusercontent.com/u/151380?v=4" width="100px;" alt="Richard Hughes"/><br /><sub><b>Richard Hughes</b></sub></a><br /><a href="#ideas-hughsie" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/solokeys/solo/commits?author=hughsie" title="Code">💻</a> <a href="#infra-hughsie" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#tool-hughsie" title="Tools">🔧</a></td>
|
||||
<td align="center"><a href="http://www.schulz.dk"><img src="https://avatars1.githubusercontent.com/u/1150049?v=4" width="100px;" alt="Kim Schulz"/><br /><sub><b>Kim Schulz</b></sub></a><br /><a href="#business-kimusan" title="Business development">💼</a> <a href="#ideas-kimusan" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
<td align="center"><a href="https://github.com/oplik0"><img src="https://avatars2.githubusercontent.com/u/25460763?v=4" width="100px;" alt="Jakub"/><br /><sub><b>Jakub</b></sub></a><br /><a href="https://github.com/solokeys/solo/issues?q=author%3Aoplik0" title="Bug reports">🐛</a></td>
|
||||
<td align="center"><a href="https://github.com/jolo1581"><img src="https://avatars1.githubusercontent.com/u/53423977?v=4" width="100px;" alt="Jan A."/><br /><sub><b>Jan A.</b></sub></a><br /><a href="https://github.com/solokeys/solo/commits?author=jolo1581" title="Code">💻</a> <a href="https://github.com/solokeys/solo/commits?author=jolo1581" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/ccinelli"><img src="https://avatars0.githubusercontent.com/u/38021940?v=4" width="100px;" alt="ccinelli"/><br /><sub><b>ccinelli</b></sub></a><br /><a href="#infra-ccinelli" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/solokeys/solo/commits?author=ccinelli" title="Tests">⚠️</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://www.nitrokey.com"><img src="https://avatars1.githubusercontent.com/u/9438831?v=4" width="100px;" alt="Nitrokey"/><br /><sub><b>Nitrokey</b></sub></a><br /><a href="https://github.com/solokeys/solo/commits?author=Nitrokey" title="Code">💻</a> <a href="https://github.com/solokeys/solo/commits?author=Nitrokey" title="Tests">⚠️</a> <a href="#ideas-Nitrokey" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
|
||||
# License
|
||||
|
||||
@ -123,8 +185,10 @@ Solo is fully open source.
|
||||
All software, unless otherwise noted, is dual licensed under Apache 2.0 and MIT.
|
||||
You may use Solo software under the terms of either the Apache 2.0 license or MIT license.
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
|
||||
|
||||
All hardware, unless otherwise noted, is dual licensed under CERN and CC-BY-SA.
|
||||
You may use Solo hardware under the terms of either the CERN 2.1 license or CC-BY-SA 4.0 license.
|
||||
You may use Solo hardware under the terms of either the CERN 1.2 license or CC-BY-SA 4.0 license.
|
||||
|
||||
All documentation, unless otherwise noted, is licensed under CC-BY-SA.
|
||||
You may use Solo documentation under the terms of the CC-BY-SA 4.0 license
|
||||
@ -135,3 +199,20 @@ You may use Solo documentation under the terms of the CC-BY-SA 4.0 license
|
||||
# Where To Buy Solo
|
||||
|
||||
You can buy Solo, Solo Tap, and Solo for Hackers at [solokeys.com](https://solokeys.com).
|
||||
|
||||
<br/>
|
||||
<hr/>
|
||||
<br/>
|
||||
|
||||
[](https://github.com/solokeys/solo/blob/master/LICENSE)
|
||||
[](#contributors)
|
||||
[](https://travis-ci.com/solokeys/solo)
|
||||
[](https://discourse.solokeys.com)
|
||||
[](https://keybase.io/team/solokeys.public)
|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fsolokeys%2Fsolo?ref=badge_shield)
|
||||
|
||||
[](https://github.com/solokeys/solo/releases)
|
||||
[](https://github.com/solokeys/solo/commits/master)
|
||||
[](https://github.com/solokeys/solo/commits/master)
|
||||
[](https://github.com/solokeys/solo/commits/master)
|
||||
[](https://github.com/solokeys/solo/graphs/contributors)
|
||||
|
32
SECURITY.md
Normal file
32
SECURITY.md
Normal file
@ -0,0 +1,32 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
We fix security issues as soon as they are found, and release firmware updates.
|
||||
Each such release is accompanied by release notes, see <https://github.com/solokeys/solo/releases>.
|
||||
|
||||
The latest version can be determined using the file <https://github.com/solokeys/solo/blob/master/STABLE_VERSION>.
|
||||
|
||||
To update your key:
|
||||
- either visit <https://update.solokeys.com>, or
|
||||
- use our commandline tool <https://github.com/solokeys/solo-python>:
|
||||
```
|
||||
solo key update [--secure|--hacker]
|
||||
```
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
To report vulnerabilities you have found:
|
||||
|
||||
- preferably contact [@conor1](https://keybase.io/conor1), [@0x0ece](https://keybase.io/0x0ece) or [@nickray](https://keybase.io/nickray) via Keybase, or
|
||||
- send us e-mail using OpenPGP to [security@solokeys.com](mailto:security@solokeys.com).
|
||||
|
||||
<https://keys.openpgp.org/vks/v1/by-fingerprint/85AFA2769F4381E5712C36A04DDFC46FEF1F7F3F>
|
||||
|
||||
We do not currently run a paid bug bounty program, but are happy to provide you with a bunch of Solo keys in recognition of your findings.
|
||||
|
||||
## Mailing List
|
||||
|
||||
Join our release notification mailing list to be informed about each release:
|
||||
|
||||
https://sendy.solokeys.com/subscription?f=9MLIqMDmox1Ucz89C892Kq09IqYMM7OB8UrBrkvtTkDI763QF3L5PMYlRhlVNo2AI892mO
|
@ -1 +1 @@
|
||||
2.2.2
|
||||
4.0.0
|
||||
|
147
docs/solo/application-ideas.md
Normal file
147
docs/solo/application-ideas.md
Normal file
@ -0,0 +1,147 @@
|
||||
# Using Solo for passwordless or second factor login on Linux
|
||||
|
||||
## Setup on Ubuntu and Manjaro
|
||||
Before you can use Solo for passwordless or second factor login in your Linux system you have to install some packages.
|
||||
|
||||
This was tested on **Linux Mint 19.3** and on **Manjaro 18.x**
|
||||
|
||||
First you have to install PAM modules for u2f.
|
||||
|
||||
**Ubuntu (Linux Mint):**
|
||||
```
|
||||
sudo apt install libpam-u2f pamu2fcfg
|
||||
```
|
||||
|
||||
**Manjaro**
|
||||
```
|
||||
pacman -Syu pam-u2f
|
||||
```
|
||||
|
||||
|
||||
## Setting up key
|
||||
To use Solo as passwordless or second factor login, you have to setup your system with your Solo.
|
||||
First create a new folder named **Yubico** in your **.config** folder in your **home** directory
|
||||
|
||||
```
|
||||
mkdir ~/.config/Yubico
|
||||
```
|
||||
|
||||
Then create a new key for PAM U2F module. If it is your first key you want to register use following command:
|
||||
```
|
||||
pamu2fcfg > ~/.config/Yubico/u2f_keys
|
||||
```
|
||||
If you want to register an additional key use this command instead:
|
||||
```
|
||||
pamu2fcfg >> ~/.config/Yubico/u2f_keys
|
||||
```
|
||||
Now press the button on your Solo.
|
||||
<br>
|
||||
<br>
|
||||
|
||||
If you can't generate your key on **Ubuntu** (error message), you may add Yubico Team from PPA and install latest libpam-u2f and pamu2fcfg and try again.
|
||||
```
|
||||
sudo add-apt-repository ppa:yubico/stable
|
||||
sudo apt-get update
|
||||
sudo apt-get upgrade
|
||||
```
|
||||
|
||||
**Manjaro** should work without problems.
|
||||
|
||||
|
||||
## Login into Linux
|
||||
### Passwordless
|
||||
To login passwordless into your Linux system, you have to edit the file **lightdm** (or **gdm** or which display manager you prefered).
|
||||
In case of lightdm and VIM as editor:
|
||||
|
||||
```
|
||||
sudo vim /etc/pam.d/lightdm
|
||||
```
|
||||
|
||||
**On Ubuntu:**<br>
|
||||
Search following entry:
|
||||
```
|
||||
@include common-auth
|
||||
```
|
||||
and add
|
||||
```
|
||||
auth sufficient pam_u2f.so
|
||||
```
|
||||
**before** *@include common-auth.*
|
||||
<br>
|
||||
<br>
|
||||
|
||||
**On Manjaro**<br>
|
||||
Search following enrty
|
||||
```
|
||||
auth include system-login
|
||||
```
|
||||
|
||||
and add
|
||||
```
|
||||
auth sufficient pam_u2f.so
|
||||
```
|
||||
|
||||
** before** *auth include system-login*.
|
||||
<br>
|
||||
<br>
|
||||
|
||||
Now save the file and test it.<br>
|
||||
Insert Solo in your USB port and logout.
|
||||
Now you should be able to login into Linux without password, only with pressing your button on Solo and press enter.
|
||||
|
||||
Why **sufficient**? The difference between the keyword sufficient and required is, if you don't have your Solo available, you can also login, because the system falls back to password mode.
|
||||
|
||||
|
||||
The login mechanism can be also used for additional features like:
|
||||
|
||||
- Login after screen timeout - edit /etc/pam.d/mate-screensaver (or kde-screensaver, ...)
|
||||
- Passwordless sudo - edit /etc/pam.d/sudo
|
||||
|
||||
Check out your folder **/etc/pam.d/** and do some experiments.
|
||||
|
||||
**But remember:** <br>
|
||||
The login passwordless won't make your system more secure, but maybe more comfortable. If somebody have access to your Solo, this person will be also able to login into your system.
|
||||
|
||||
|
||||
### Solo as second factor
|
||||
To use Solo as second factor, for login into your Linux system, is nearly the same.
|
||||
|
||||
```
|
||||
sudo vim /etc/pam.d/lightdm
|
||||
```
|
||||
|
||||
**On Ubuntu**<br>
|
||||
Search following entry:
|
||||
```
|
||||
@include common-auth
|
||||
```
|
||||
and add
|
||||
```
|
||||
auth required pam_u2f.so
|
||||
```
|
||||
**after** *@include common-auth*.
|
||||
<br>
|
||||
<br>
|
||||
|
||||
**On Manjaro**<br>
|
||||
Search following entry:
|
||||
```
|
||||
auth include system-login
|
||||
```
|
||||
|
||||
Add following entry
|
||||
```
|
||||
auth required pam_u2f.so
|
||||
```
|
||||
|
||||
**after** *auth include system-login*.
|
||||
<br>
|
||||
<br>
|
||||
|
||||
Save the file and test it. <br>
|
||||
In case your Solo is not present, your password will be incrorrect. If Solo is plugged into your USB port, it will signal pressing the button and you will be able to login into Linux.
|
||||
|
||||
Why **required**? If you choose the option **sufficent** your Solo is optional. You could also login without second factor if your Solo is not connected.
|
||||
|
||||
**But remember:**<br>
|
||||
If you loose your Solo you won't be able to login into your system.
|
53
docs/solo/bootloader-mode.md
Normal file
53
docs/solo/bootloader-mode.md
Normal file
@ -0,0 +1,53 @@
|
||||
# Booting into bootloader mode
|
||||
|
||||
If you have a recent version of Solo, you can put it into bootloader mode by running this command.
|
||||
|
||||
```bash
|
||||
solo program aux enter-bootloader
|
||||
```
|
||||
|
||||
If your Solo is a bit older (<=2.5.3) You can put Solo into bootloader mode by using the button method:
|
||||
Hold down button while plugging in Solo. After 2 seconds, bootloader mode will activate.
|
||||
You'll see a yellowish flashing light and you can let go of the button.
|
||||
|
||||
Now Solo is ready to [accept firmware updates](/solo/signed-updates). If the Solo is a secured model, it can only accept signed updates, typically in the `firmware-*.json` format.
|
||||
|
||||
# The boot stages of Solo
|
||||
|
||||
Solo has 3 boot stages.
|
||||
|
||||
## DFU
|
||||
|
||||
The first stage is the DFU (Device Firmware Update) which is in a ROM on Solo. It is baked into the chip and is not implemented by us.
|
||||
This is what allows the entire firmware of Solo to be programmed. **It's not recommended to develop for Solo using the DFU because
|
||||
if you program broken firmware, you could brick your device**.
|
||||
|
||||
On hacker/nonverifying-bootloader devices, you can boot into the DFU by holding down the button for 5 seconds,
|
||||
when Solo is already in bootloader mode.
|
||||
|
||||
You can also run this command when Solo is in bootloader mode to put it in DFU mode.
|
||||
|
||||
```bash
|
||||
solo program aux enter-dfu
|
||||
```
|
||||
|
||||
Note it will stay in DFU mode until you to tell it to boot again. You can boot it again by running the following.
|
||||
|
||||
```bash
|
||||
solo program aux leave-dfu
|
||||
```
|
||||
|
||||
*Warning*: If you change the firmware to something broken, and you tell the DFU to boot it, you could brick your device.
|
||||
|
||||
## Solo Bootloader
|
||||
|
||||
The next boot stage is the "Solo bootloader". So when we say to put your Solo into bootloader mode, it is this stage.
|
||||
This bootloader is written by us and allows signed firmware updates to be written. On Solo Hackers, there is no signature checking
|
||||
and will allow any firmware updates.
|
||||
|
||||
It is safe to develop for Solo using our Solo bootloader. If broken firmware is uploaded to the device, then the Solo
|
||||
bootloader can always be booted again by holding down the button when plugging in.
|
||||
|
||||
## Solo application
|
||||
|
||||
This is what contains all the important functionality of Solo. FIDO2, U2F, etc. This is what Solo will boot to by default.
|
@ -1,22 +1,34 @@
|
||||
# Building solo
|
||||
|
||||
To build, develop and debug the firmware for the STM32L432. This will work
|
||||
for Solo Hacker, the Nucleo development board, or your own homemade Solo.
|
||||
|
||||
There exists a development board [NUCLEO-L432KC](https://www.st.com/en/evaluation-tools/nucleo-l432kc.html) you can use; The board does contain a debugger, so all you need is a USB cable (and some [udev](/udev) [rules](https://rust-embedded.github.io/book/intro/install/linux.html#udev-rules)).
|
||||
|
||||
# Prerequisites
|
||||
## Prerequisites
|
||||
|
||||
Install the [latest ARM compiler toolchain](https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads) for your system. We recommend getting the latest compilers from ARM.
|
||||
|
||||
You can also install the ARM toolchain using a package manager like `apt-get` or `pacman`,
|
||||
but be warned they might be out of date. Typically it will be called `gcc-arm-none-eabi binutils-arm-none-eabi`.
|
||||
|
||||
To program your build, you'll need one of the following programs.
|
||||
Install `solo-python` usually with `pip3 install solo-python`. The `solo` python application may also be used for [programming](#programming).
|
||||
|
||||
- [openocd](http://openocd.org)
|
||||
- [stlink](https://github.com/texane/stlink)
|
||||
- [STM32CubeProg](https://www.st.com/en/development-tools/stm32cubeprog.html)
|
||||
## Obtain source code and solo tool
|
||||
|
||||
# Compilation
|
||||
Source code can be downloaded from:
|
||||
|
||||
- [github releases list](https://github.com/solokeys/solo/releases)
|
||||
- [github repository](https://github.com/solokeys/solo)
|
||||
|
||||
**solo** tool can be downloaded from:
|
||||
|
||||
- from python programs [repository](https://pypi.org/project/solo-python/) `pip install solo-python`
|
||||
- from installing prerequisites `pip3 install -r tools/requirements.txt`
|
||||
- github repository: [repository](https://github.com/solokeys/solo-python)
|
||||
- installation python enviroment with command `make venv` from root directory of source code
|
||||
|
||||
## Compilation
|
||||
|
||||
Enter the `stm32l4xx` target directory.
|
||||
|
||||
@ -24,19 +36,23 @@ Enter the `stm32l4xx` target directory.
|
||||
cd targets/stm32l432
|
||||
```
|
||||
|
||||
Now build Solo.
|
||||
Now build the Solo application.
|
||||
|
||||
```
|
||||
make build-hacker
|
||||
make firmware
|
||||
```
|
||||
|
||||
The `build-hacker` recipe does a few things. First it builds the bootloader, with
|
||||
The `firmware` recipe builds the solo application, and outputs `solo.hex`. You can use this
|
||||
to reprogram any unlocked/hacker Solo model. Note that it does not include the Solo bootloader,
|
||||
so it is not a full reprogram.
|
||||
|
||||
<!-- First it builds the bootloader, with
|
||||
signature checking disabled. Then it builds the Solo application with "hacker" features
|
||||
enabled, like being able to jump to the bootloader on command. It then merges bootloader
|
||||
and solo builds into the same binary. I.e. it combines `bootloader.hex` and `solo.hex`
|
||||
into `all.hex`.
|
||||
into `all.hex`. -->
|
||||
|
||||
If you're just planning to do development, please don't try to reprogram the bootloader,
|
||||
If you're just planning to do development, **please don't try to reprogram the bootloader**,
|
||||
as this can be risky if done often. Just use `solo.hex`.
|
||||
|
||||
### Building with debug messages
|
||||
@ -45,13 +61,13 @@ If you're developing, you probably want to see debug messages! Solo has a USB
|
||||
Serial port that it will send debug messages through (from `printf`). You can read them using
|
||||
a normal serial terminal like `picocom` or `putty`.
|
||||
|
||||
Just add `DEBUG=1` or `DEBUG=2` to your build recipe, like this.
|
||||
Just add `-debug-1` or `-debug-2` to your build recipe, like this.
|
||||
|
||||
```
|
||||
make build-hacker DEBUG=1
|
||||
make firmware-debug-1
|
||||
```
|
||||
|
||||
If you use `DEBUG=2`, that means Solo will not boot until something starts reading
|
||||
If you use `debug-2`, that means Solo will not boot until something starts reading
|
||||
its debug messages. So it basically waits to tether to a serial terminal so that you don't
|
||||
miss any debug messages.
|
||||
|
||||
@ -66,118 +82,56 @@ solo monitor <serial-port>
|
||||
|
||||
[See issue 62](https://github.com/solokeys/solo/issues/62).
|
||||
|
||||
### Building a Solo release
|
||||
### Building a complete Solo build (application + bootloader + certificate)
|
||||
|
||||
If you want to build a release of Solo, we recommend trying a Hacker build first
|
||||
just to make sure that it's working. Otherwise it may not be as easy or possible to
|
||||
fix any mistakes.
|
||||
To make a complete Solo build, you need to build the bootloader. We provide
|
||||
two easy recipes:
|
||||
|
||||
If you're ready to program a full release, run this recipe to build.
|
||||
* `bootloader-nonverifying`: bootloader with no signature checking on updates. I.e. "unlocked".
|
||||
* `bootloader-verifying`: bootloader with signature checking enforced on updated. I.e. "Locked".
|
||||
|
||||
To be safe, let's use the `-nonverifying` build.
|
||||
|
||||
```
|
||||
make build-release-locked
|
||||
make bootloader-nonverifying
|
||||
```
|
||||
|
||||
Programming `all.hex` will cause the device to permanently lock itself.
|
||||
|
||||
|
||||
# Programming
|
||||
|
||||
It's recommended to test a debug/hacker build first to make sure Solo is working as expected.
|
||||
Then you can switch to a locked down build, which cannot be reprogrammed as easily (or not at all!).
|
||||
|
||||
We recommend using our `solo` tool to manage programming. It is cross platform. First you must
|
||||
install the prerequisites:
|
||||
This outputs `bootloader.hex`. We can then merge the bootloader and application.
|
||||
|
||||
```
|
||||
pip3 install -r tools/requirements.txt
|
||||
solo mergehex bootloader.hex solo.hex bundle.hex
|
||||
```
|
||||
|
||||
If you're on Windows, you must also install [libusb](https://sourceforge.net/projects/libusb-win32/files/libusb-win32-releases/1.2.6.0/).
|
||||
|
||||
## Pre-programmed Solo Hacker
|
||||
|
||||
If your Solo device is already programmed (it flashes green when powered), we recommend
|
||||
programming it using the Solo bootloader.
|
||||
`bundle.hex` is our complete firmware build. Note it is in this step that you can
|
||||
include a custom attestation certificate or lock the device from debugging/DFU.
|
||||
By default the "hacker" attestation certifcate and key is used. Use the `--lock` flag
|
||||
to make this permanent.
|
||||
|
||||
```
|
||||
solo program aux enter-bootloader
|
||||
solo program bootloader solo.hex
|
||||
solo mergehex \
|
||||
--attestation-key "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF" \
|
||||
--attestation-cert attestation.der \
|
||||
solo.hex \
|
||||
bootloader.hex \
|
||||
bundle.hex
|
||||
```
|
||||
|
||||
Make sure to program `solo.hex` and not `all.hex`. Nothing bad would happen, but you'd
|
||||
see errors.
|
||||
|
||||
If something bad happens, you can always boot the Solo bootloader by doing the following.
|
||||
|
||||
1. Unplug device.
|
||||
2. Hold down button.
|
||||
3. Plug in device while holding down button.
|
||||
4. Wait about 2 seconds for flashing yellow light. Release button.
|
||||
|
||||
If you hold the button for an additional 5 seconds, it will boot to the ST DFU (device firmware update).
|
||||
Don't use the ST DFU unless you know what you're doing.
|
||||
|
||||
## ST USB DFU
|
||||
|
||||
If your Solo has never been programmed, it will boot the ST USB DFU. The LED is turned
|
||||
off and it enumerates as "STM BOOTLOADER".
|
||||
|
||||
You can program it by running the following.
|
||||
**Warning**: If you use `--lock`, this will permanently lock the device to this new bootloader. You
|
||||
won't be able to program the bootloader again or be able to connect a hardware debugger.
|
||||
The new bootloader may be able to accept (signed) updates still, depending on how you configured it.
|
||||
|
||||
```
|
||||
solo program aux enter-bootloader
|
||||
solo program aux enter-dfu
|
||||
# powercycle key
|
||||
solo program dfu all.hex
|
||||
# Permanent!
|
||||
solo mergehex \
|
||||
--attestation-key "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF" \
|
||||
--attestation-cert attestation.der \
|
||||
--lock \
|
||||
solo.hex \
|
||||
bootloader.hex \
|
||||
bundle.hex
|
||||
```
|
||||
|
||||
Make sure to program `all.hex`, as this contains both the bootloader and the Solo application.
|
||||
See [here for more information on custom attestation](/solo/customization/).
|
||||
|
||||
If all goes well, you should see a slow-flashing green light.
|
||||
To learn more about normal updates or a "full" update, you should [read more on Solo's boot stages](/solo/bootloader-mode).
|
||||
|
||||
## Solo Hacker vs Solo
|
||||
|
||||
A Solo hacker device doesn't need to be in bootloader mode to be programmed, it will automatically switch.
|
||||
|
||||
Solo (locked) needs the button to be held down when plugged in to boot to the bootloader.
|
||||
|
||||
A locked Solo will only accept signed updates.
|
||||
|
||||
## Signed updates
|
||||
|
||||
If this is not a device with a hacker build, you can only program signed updates.
|
||||
|
||||
```
|
||||
solo program bootloader /path/to/firmware.json
|
||||
```
|
||||
|
||||
If you've provisioned the Solo bootloader with your own secp256r1 public key, you can sign your
|
||||
firmware by running the following command.
|
||||
|
||||
```
|
||||
solo sign /path/to/signing-key.pem /path/to/solo.hex /output-path/to/firmware.json
|
||||
```
|
||||
|
||||
If your Solo isn't locked, you can always reprogram it using a debugger connected directly
|
||||
to the token.
|
||||
|
||||
# Permanently locking the device
|
||||
|
||||
If you plan to be using your Solo for real, you should lock it permanently. This prevents
|
||||
someone from connecting a debugger to your token and stealing credentials.
|
||||
|
||||
To do this, build the locked release firmware.
|
||||
```
|
||||
make build-release-locked
|
||||
```
|
||||
|
||||
Now when you program `all.hex`, the device will lock itself when it first boots. You can only update it
|
||||
with signed updates.
|
||||
|
||||
If you'd like to also permanently disable signed updates, plug in your programmed Solo and run the following:
|
||||
|
||||
```
|
||||
# WARNING: No more signed updates.
|
||||
solo program disable-bootloader
|
||||
```
|
||||
|
142
docs/solo/customization.md
Normal file
142
docs/solo/customization.md
Normal file
@ -0,0 +1,142 @@
|
||||
# Customization
|
||||
|
||||
If you are interested in customizing parts of your Solo, and you have a Solo Hacker, this page is for you.
|
||||
|
||||
## Custom Attestation key
|
||||
|
||||
The attestation key is used in the FIDO2 *makeCredential* or U2F *register* requests. It signs
|
||||
newly generated credentials. The certificate associated with the attestation key is output with newly created credentials.
|
||||
|
||||
Platforms or services can use the attestation feature to enforce specific authenticators to be used.
|
||||
This is typically a use case for organizations and isn't seen in the wild for consumer use cases.
|
||||
|
||||
Attestation keys are typically the same for at least 100K units of a particular authenticator model.
|
||||
This is so they don't contribute a significant fingerprint that platforms could use to identify the user.
|
||||
|
||||
If you don't want to use the default attestation key that Solo builds with, you can create your own
|
||||
and program it.
|
||||
|
||||
### Creating your attestation key pair
|
||||
|
||||
Since we are generating keys, it's important to use a good entropy source.
|
||||
You can use the [True RNG on your Solo](/solo/solo-extras) to generate some good random numbers.
|
||||
|
||||
```
|
||||
# Run for 1 second, then hit control-c
|
||||
solo key rng raw > seed.bin
|
||||
```
|
||||
|
||||
First we will create a self signed key pair that acts as the root of trust. This
|
||||
won't go on the authenticator, but will sign the keypair that does.
|
||||
|
||||
Please change the root certification information as needed. You may change the ECC curve.
|
||||
|
||||
```
|
||||
curve=prime256v1
|
||||
|
||||
country=US
|
||||
state=Maine
|
||||
organization=OpenSourceSecurity
|
||||
unit="Root CA"
|
||||
CN=example.com
|
||||
email=example@example.com
|
||||
|
||||
# generate EC private key
|
||||
openssl ecparam -genkey -name "$curve" -out root_key.pem -rand seed.bin
|
||||
|
||||
# generate a "signing request"
|
||||
openssl req -new -key root_key.pem -out root_key.pem.csr -subj "/C=$country/ST=$state/O=$organization/OU=$unit/CN=$CN/emailAddress=$email"
|
||||
|
||||
# self sign the request
|
||||
openssl x509 -trustout -req -days 18250 -in root_key.pem.csr -signkey root_key.pem -out root_cert.pem -sha256
|
||||
|
||||
# convert to smaller size format DER
|
||||
openssl x509 -in root_cert.pem -outform der -out root_cert.der
|
||||
|
||||
# print out information and verify
|
||||
openssl x509 -in root_cert.pem -text -noout
|
||||
```
|
||||
|
||||
You need to create a extended certificate for the device certificate to work with FIDO2. You need to create this
|
||||
file, `v3.ext`, and add these options to it.
|
||||
|
||||
```
|
||||
subjectKeyIdentifier=hash
|
||||
authorityKeyIdentifier=keyid,issuer
|
||||
basicConstraints=CA:FALSE
|
||||
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
|
||||
```
|
||||
|
||||
Now to generate & sign the attestation key pair that will go on your device, or maybe 100,000 devices :).
|
||||
Note you must use a prime256v1 curve for this step, and you must leave the unit/OU as "Authenticator Attestation".
|
||||
|
||||
```
|
||||
country=US
|
||||
state=Maine
|
||||
organization=OpenSourceSecurity
|
||||
unit="Authenticator Attestation" # MUST KEEP THIS AS "Authenticator Attestation" for FIDO2.
|
||||
CN=example.com
|
||||
email=example@example.com
|
||||
|
||||
# generate EC private key
|
||||
openssl ecparam -genkey -name "$curve" -out device_key.pem -rand seed.bin
|
||||
|
||||
# generate a "signing request"
|
||||
openssl req -new -key device_key.pem -out device_key.pem.csr -subj "/C=$country/ST=$state/O=$organization/OU=$unit/CN=$CN/emailAddress=$email"
|
||||
|
||||
# sign the request
|
||||
openssl x509 -req -days 18250 -in device_key.pem.csr -extfile v3.ext -CA root_cert.pem -CAkey root_key.pem -set_serial 01 -out device_cert.pem -sha256
|
||||
|
||||
# convert to smaller size format DER
|
||||
openssl x509 -in device_cert.pem -outform der -out device_cert.der
|
||||
|
||||
# Verify the device certificate details
|
||||
openssl x509 -in device_cert.pem -text -noout
|
||||
```
|
||||
|
||||
Let's verify that the attestation key and certificate are valid, and that they can be verified with the root key pair.
|
||||
|
||||
```
|
||||
echo 'challenge $RANDOM' > chal.txt
|
||||
|
||||
# check that they are valid key pairs
|
||||
openssl dgst -sha256 -sign device_key.pem -out sig.txt chal.txt
|
||||
openssl dgst -sha256 -verify <(openssl x509 -in device_cert.pem -pubkey -noout) -signature sig.txt chal.txt
|
||||
|
||||
openssl dgst -sha256 -sign "root_key.pem" -out sig.txt chal.txt
|
||||
openssl dgst -sha256 -verify <(openssl x509 -in root_cert.pem -pubkey -noout) -signature sig.txt chal.txt
|
||||
|
||||
# Check they are a chain
|
||||
openssl verify -verbose -CAfile "root_cert.pem" "device_cert.pem"
|
||||
```
|
||||
|
||||
If the checks succeed, you are ready to program the device attestation key and certificate.
|
||||
|
||||
### Programming an attestation key and certificate
|
||||
|
||||
First, [Build your solo application and bootloader](/solo/building).
|
||||
|
||||
Print your attestation key in a hex string format. Using our utility script:
|
||||
|
||||
```
|
||||
python3 tools/gencert/print_x_y.py device_key.pem
|
||||
```
|
||||
|
||||
Merge the `bootloader.hex`, `solo.hex`, attestion key, and certificate into one firmware file.
|
||||
|
||||
```
|
||||
solo mergehex \
|
||||
--attestation-key "(The 32-byte hex string extracted from device_key.pem)" \
|
||||
--attestation-cert device_cert.der \
|
||||
--lock \
|
||||
solo.hex \
|
||||
bootloader.hex \
|
||||
bundle.hex
|
||||
```
|
||||
|
||||
**Warning**: Using the `--lock` flag prevents the DFU from being accessed on the device again. It's recommended to try first without the `--lock` flag to make sure it works.
|
||||
|
||||
Now you have a newly created `bundle.hex` file with a custom attestation key and cert. You can [program this `bundle.hex` file
|
||||
with Solo in DFU mode](/solo/programming#procedure).
|
||||
|
||||
Are you interested in customizing in bulk? Contact hello@solokeys.com and we can help.
|
BIN
docs/solo/images/conforms.PNG
Normal file
BIN
docs/solo/images/conforms.PNG
Normal file
Binary file not shown.
After Width: | Height: | Size: 134 KiB |
BIN
docs/solo/images/solo_conforms.PNG
Normal file
BIN
docs/solo/images/solo_conforms.PNG
Normal file
Binary file not shown.
After Width: | Height: | Size: 129 KiB |
257
docs/solo/nucleo32-board.md
Normal file
257
docs/solo/nucleo32-board.md
Normal file
@ -0,0 +1,257 @@
|
||||
# Nucleo32 board preparation
|
||||
|
||||
Additional steps are required to run the firmware on the Nucleo32 board.
|
||||
|
||||
## USB-A cable
|
||||
|
||||
Board does not provide an USB cable / socket for the target MCU communication.
|
||||
Own provided USB plug has to be connected in the following way:
|
||||
|
||||
| PIN / Arduino PIN | MCU leg | USB wire color | Signal |
|
||||
| ----------------- | ------- | -------------- | ------ |
|
||||
| D10 / PA11 | 21 | white | D- |
|
||||
| D2 / PA12 | 22 | green | D+ |
|
||||
| GND (near D2) | ------- | black | GND |
|
||||
| **not connected** | ------- | red | 5V |
|
||||
|
||||
Each USB plug pin should be connected via the wire in a color defined by the standard. It might be confirmed with a
|
||||
multimeter for additional safety. USB plug description:
|
||||
|
||||
| PIN | USB wire color | Signal |
|
||||
| --- | -------------- | ------ |
|
||||
| 4 | black | GND |
|
||||
| 3 | green | D+ |
|
||||
| 2 | white | D- |
|
||||
| 1 | red | 5V |
|
||||
|
||||
See this [USB plug] image, and Wikipedia's [USB plug description].
|
||||
|
||||
Plug in [USB-A_schematic.pdf] has wrong wire order, registered as [solo-hw#1].
|
||||
|
||||
The power is taken from the debugger / board (unless the board is configured in another way).
|
||||
Make sure 5V is not connected, and is covered from contacting with the board elements.
|
||||
|
||||
Based on [USB-A_schematic.pdf].
|
||||
|
||||
## Firmware modification
|
||||
|
||||
Following patch has to be applied to skip the user presence confirmation, for tests. Might be applied at a later stage.
|
||||
|
||||
```text
|
||||
diff --git a/targets/stm32l432/src/app.h b/targets/stm32l432/src/app.h
|
||||
index c14a7ed..c89c3b5 100644
|
||||
--- a/targets/stm32l432/src/app.h
|
||||
+++ b/targets/stm32l432/src/app.h
|
||||
@@ -71,6 +71,6 @@ void hw_init(void);
|
||||
#define SOLO_BUTTON_PIN LL_GPIO_PIN_0
|
||||
|
||||
#define SKIP_BUTTON_CHECK_WITH_DELAY 0
|
||||
-#define SKIP_BUTTON_CHECK_FAST 0
|
||||
+#define SKIP_BUTTON_CHECK_FAST 1
|
||||
|
||||
#endif
|
||||
```
|
||||
|
||||
It is possible to provide a button and connect it to the MCU pins, as instructed in [USB-A_schematic.pdf]:
|
||||
|
||||
```text
|
||||
PA0 / pin 6 --> button --> GND
|
||||
```
|
||||
|
||||
In that case the mentioned patch would not be required.
|
||||
|
||||
## Development environment setup
|
||||
|
||||
Environment: Fedora 29 x64, Linux 4.19.9
|
||||
|
||||
See <https://docs.solokeys.io/solo/building/> for the original guide. Here details not included there will be covered.
|
||||
|
||||
### Install ARM tools Linux
|
||||
|
||||
1. Download current [ARM tools] package: [gcc-arm-none-eabi-8-2018-q4-major-linux.tar.bz2].
|
||||
|
||||
2. Extract the archive.
|
||||
|
||||
3. Add full path to the `./bin` directory as first entry to the `$PATH` variable,
|
||||
as in `~/gcc-arm/gcc-arm-none-eabi-8-2018-q4-major/bin/:$PATH`.
|
||||
|
||||
### Install ARM tools OsX using brew package manager
|
||||
|
||||
```bash
|
||||
brew tap ArmMbed/homebrew-formulae
|
||||
brew install arm-none-eabi-gcc
|
||||
```
|
||||
|
||||
### Install flashing software
|
||||
|
||||
ST provides a CLI flashing tool - `STM32_Programmer_CLI`. It can be downloaded directly from the vendor's site:
|
||||
1. Go to [download site URL](https://www.st.com/content/st_com/en/products/development-tools/software-development-tools/stm32-software-development-tools/stm32-programmers/stm32cubeprog.html), go to bottom page and from STM32CubeProg row select Download button.
|
||||
2. Unzip contents of the archive.
|
||||
3. Run \*Linux setup
|
||||
4. In installation directory go to `./bin` - there the `./STM32_Programmer_CLI` is located
|
||||
5. Add symlink to the STM32 CLI binary to `.local/bin`. Make sure the latter it is in `$PATH`.
|
||||
|
||||
If you're on MacOS X and installed the STM32CubeProg, you need to add the following to your path:
|
||||
|
||||
```bash
|
||||
# ~/.bash_profile
|
||||
export PATH="/Applications/STMicroelectronics/STM32Cube/STM32CubeProgrammer/STM32CubeProgrammer.app/Contents/MacOs/bin/":$PATH
|
||||
```
|
||||
|
||||
## Building and flashing
|
||||
|
||||
### Building
|
||||
|
||||
Please follow <https://docs.solokeys.io/solo/building/>, as the build way changes rapidly.
|
||||
Currently (8.1.19) to build the firmware, following lines should be executed
|
||||
|
||||
```bash
|
||||
# while in the main project directory
|
||||
cd targets/stm32l432
|
||||
make cbor
|
||||
make build-hacker DEBUG=1
|
||||
```
|
||||
|
||||
Note: `DEBUG=2` stops the device initialization, until a serial client will be attached to its virtual port.
|
||||
Do not use it, if you do not plan to do so.
|
||||
|
||||
### Flashing via the Makefile command
|
||||
|
||||
```bash
|
||||
# while in the main project directory
|
||||
# create Python virtual environment with required packages, and activate
|
||||
make venv
|
||||
. venv/bin/activate
|
||||
# Run flashing
|
||||
cd ./targets/stm32l432
|
||||
make flash
|
||||
# which runs:
|
||||
# flash: solo.hex bootloader.hex
|
||||
# python merge_hex.py solo.hex bootloader.hex all.hex (intelhex library required)
|
||||
# STM32_Programmer_CLI -c port=SWD -halt -e all --readunprotect
|
||||
# STM32_Programmer_CLI -c port=SWD -halt -d all.hex -rst
|
||||
```
|
||||
|
||||
### Manual flashing
|
||||
|
||||
In case you already have a firmware to flash (named `all.hex`), please run the following:
|
||||
|
||||
```bash
|
||||
STM32_Programmer_CLI -c port=SWD -halt -e all --readunprotect
|
||||
STM32_Programmer_CLI -c port=SWD -halt -d all.hex -rst
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Internal
|
||||
|
||||
Project-provided tests.
|
||||
|
||||
#### Simulated device
|
||||
|
||||
A simulated device is provided to test the HID layer.
|
||||
|
||||
##### Build
|
||||
|
||||
```bash
|
||||
make clean
|
||||
cd tinycbor
|
||||
make
|
||||
cd ..
|
||||
make env2
|
||||
```
|
||||
|
||||
##### Execution
|
||||
|
||||
```bash
|
||||
# run simulated device (will create a network UDP server)
|
||||
./main
|
||||
# run test 1
|
||||
./env2/bin/python tools/ctap_test.py
|
||||
# run test 2 (or other files in the examples directory)
|
||||
./env2/bin/python python-fido2/examples/credential.py
|
||||
```
|
||||
|
||||
#### Real device
|
||||
|
||||
```bash
|
||||
# while in the main project directory
|
||||
# not passing as of 8.1.19, due to test solution issues
|
||||
make fido2-test
|
||||
```
|
||||
|
||||
### External
|
||||
|
||||
#### FIDO2 test sites
|
||||
|
||||
1. <https://www.passwordless.dev/overview>
|
||||
2. <https://webauthn.bin.coffee/>
|
||||
3. <https://webauthn.org/>
|
||||
|
||||
#### U2F test sites
|
||||
|
||||
1. <https://u2f.bin.coffee/>
|
||||
2. <https://demo.yubico.com/u2f>
|
||||
|
||||
#### FIDO2 standalone clients
|
||||
|
||||
1. <https://github.com/Nitrokey/u2f-ref-code>
|
||||
2. <https://github.com/Yubico/libfido2>
|
||||
3. <https://github.com/Yubico/python-fido2>
|
||||
4. <https://github.com/google/pyu2f>
|
||||
|
||||
## USB serial console reading
|
||||
|
||||
Device opens an USB-emulated serial port to output its messages. While Nucleo board offers such already,
|
||||
the Solo device provides its own.
|
||||
|
||||
- Provided Python tool
|
||||
|
||||
```bash
|
||||
python3 ../../tools/solotool.py monitor /dev/solokey-serial
|
||||
```
|
||||
|
||||
- External application
|
||||
|
||||
```bash
|
||||
sudo picocom -b 115200 /dev/solokey-serial
|
||||
```
|
||||
|
||||
where `/dev/solokey-serial` is an udev symlink to `/dev/ttyACM1`.
|
||||
|
||||
## Other
|
||||
|
||||
### Dumping firmware
|
||||
|
||||
Size is calculated using bash arithmetic.
|
||||
|
||||
```bash
|
||||
STM32_Programmer_CLI -c port=SWD -halt -u 0x0 $((256*1024)) current.hex
|
||||
```
|
||||
|
||||
### Software reset
|
||||
|
||||
```bash
|
||||
STM32_Programmer_CLI -c port=SWD -rst
|
||||
```
|
||||
|
||||
### Installing required Python packages
|
||||
|
||||
Client script requires some Python packages, which could be easily installed locally to the project
|
||||
via the Makefile command. It is sufficient to run:
|
||||
|
||||
```bash
|
||||
make env3
|
||||
```
|
||||
|
||||
[solo-hw#1]: https://github.com/solokeys/solo-hw/issues/1
|
||||
|
||||
[usb plug]: https://upload.wikimedia.org/wikipedia/commons/thumb/6/67/USB.svg/1200px-USB.svg.png
|
||||
|
||||
[usb plug description]: https://en.wikipedia.org/wiki/USB#Receptacle_(socket)_identification
|
||||
|
||||
[usb-a_schematic.pdf]: https://github.com/solokeys/solo-hw/releases/download/1.2/USB-A_schematic.pdf
|
||||
|
||||
[arm tools]: https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads
|
||||
|
||||
[gcc-arm-none-eabi-8-2018-q4-major-linux.tar.bz2]: https://developer.arm.com/-/media/Files/downloads/gnu-rm/8-2018q4/gcc-arm-none-eabi-8-2018-q4-major-linux.tar.bz2?revision=d830f9dd-cd4f-406d-8672-cca9210dd220?product=GNU%20Arm%20Embedded%20Toolchain,64-bit,,Linux,8-2018-q4-major
|
60
docs/solo/porting.md
Normal file
60
docs/solo/porting.md
Normal file
@ -0,0 +1,60 @@
|
||||
# Usage and Porting
|
||||
|
||||
Solo is designed to be used as a library or ported to other platforms easily. Here is an example
|
||||
`main()` function.
|
||||
|
||||
```c
|
||||
int main()
|
||||
{
|
||||
uint8_t hidmsg[64];
|
||||
uint32_t t1 = 0;
|
||||
|
||||
device_init();
|
||||
memset(hidmsg,0,sizeof(hidmsg));
|
||||
|
||||
while(1)
|
||||
{
|
||||
|
||||
if (usbhid_recv(hidmsg) > 0)
|
||||
{
|
||||
ctaphid_handle_packet(hidmsg); // pass into libsolo!
|
||||
memset(hidmsg, 0, sizeof(hidmsg));
|
||||
}
|
||||
|
||||
|
||||
ctaphid_check_timeouts();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
`ctaphid_handle_packet(hidmsg);` is the entrance into the HID layer of libsolo, and will buffer packets and pass them
|
||||
into FIDO2 or U2F layers.
|
||||
|
||||
Everything in the library is cross-platform, but it needs some functions implemented that are usually
|
||||
platform specific. For example, how should libsolo implement an atomic counter? Where should it save state?
|
||||
For all of these platform specific functions, the library contains it's own `weak` definition, so the library will compile and run.
|
||||
LibSolo by default will not try to use an atomic
|
||||
counter or save data persistently -- that needs to be implemented externally.
|
||||
|
||||
If you are using libsolo on another platform,
|
||||
you should take a look at these possibly platform specific functions. They are listed in `fido2/device.h`.
|
||||
If you'd like to reimplement any of the functions, then simply implement the function and compile normally.
|
||||
GCC will replace libsolo's `weak` defined functions (everything in `fido2/device.h`) with your functions. By doing this, you
|
||||
are replacing the function that is used by libsolo.
|
||||
|
||||
To get the library to compile
|
||||
and run, you only need to implement one function for libsolo: `usbhid_send(uint8_t * send)`, which
|
||||
is called by the library to send a 64 byte packet over a USB HID endpoint. In essence, you are giving
|
||||
libsolo a function to write to USB.
|
||||
|
||||
The rest of the definitions in `fido2/device.h` are not required to compile and run so you can
|
||||
immediately hit the ground running and iterative add what else you need. You'll definitely want
|
||||
to continue implementing other functions in `fido2/device.h`. For example, no data will be stored
|
||||
persistently until you define how it can be done!
|
||||
|
||||
For examples, check out the build for STM32L4 and PC (check out `pc/device` and `targets/stm32l432/src/device.c`).
|
||||
|
||||
If there's something that doesn't work for you -- send a pull request! It's better if we can
|
||||
work together off of the same repo and not fork.
|
116
docs/solo/programming.md
Normal file
116
docs/solo/programming.md
Normal file
@ -0,0 +1,116 @@
|
||||
# Programming
|
||||
|
||||
This page documents how to update or program your Solo.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
To program Solo, you'll likely only need to use our Solo tool.
|
||||
|
||||
```python
|
||||
pip3 install solo-python
|
||||
```
|
||||
|
||||
## Updating the firmware
|
||||
|
||||
If you just want to update the firmware, you can run one of the following commands.
|
||||
Make sure your key [is in bootloader mode](/solo/bootloader-mode#solo-bootloader) first.
|
||||
|
||||
```bash
|
||||
solo key update <--secure | --hacker>
|
||||
```
|
||||
|
||||
You can manually install the [latest release](https://github.com/solokeys/solo/releases), or use a build that you made.
|
||||
|
||||
```bash
|
||||
solo program bootloader <firmware.hex | firmware.json>
|
||||
```
|
||||
|
||||
Note you won't be able to use `all.hex` or the `bundle-*.hex` builds, as these include the solo bootloader. You shouldn't
|
||||
risk changing the Solo bootloader unless you want to make it a secure device, or [make other customizations](/solo/customization/).
|
||||
|
||||
## Updating a Hacker to a Secure Solo
|
||||
|
||||
Updating a hacker to be a secure build overwrites the [Solo bootloader](/solo/bootloader-mode#solo-bootloader).
|
||||
So it's important to not mess this up or you may brick your device.
|
||||
|
||||
You can use a firmware build from the [latest release](https://github.com/solokeys/solo/releases) or use
|
||||
a build that you made yourself.
|
||||
|
||||
You need to use a firmware file that has the combined bootloader, application, and attestation key pair (bootloader + firmware + key).
|
||||
This means using the `bundle-*.hex` file or the `bundle.hex` from your build.
|
||||
|
||||
#### *Warning*
|
||||
|
||||
* **If you overwrite the Solo flash with a missing bootloader, it will be bricked**.
|
||||
* **If you program bootloader and firmware with no attestation, you will run into FIDO registration issues**
|
||||
|
||||
We provide two types of bundled builds. The `bundle-hacker-*.hex` build is the hacker build. If you update with this,
|
||||
you will update the bootloader and application, but nothing will be secured. The `bundle-secure-non-solokeys.hex`
|
||||
is a secured build that will lock your device and it will behave just like a Secure Solo. The main difference is that
|
||||
it uses a "default" attestation key in the device, rather than the SoloKeys attestation key. There is no security
|
||||
concern with using our default attestation key, aside from a small privacy implication that services can distinguish it from Solo Secure.
|
||||
|
||||
### Procedure
|
||||
|
||||
1. Boot into DFU mode.
|
||||
|
||||
# Enter Solo bootloader
|
||||
solo program aux enter-bootloader
|
||||
|
||||
# Enter DFU
|
||||
solo program aux enter-dfu
|
||||
|
||||
The device should be turned off.
|
||||
|
||||
2. Program the device
|
||||
|
||||
solo program dfu <bundle-secure-non-solokeys.hex | bundle.hex>
|
||||
|
||||
Double check you programmed it with bootloader + application (or just bootloader).
|
||||
If you messed it up, simply don't do the next step and repeat this step correctly.
|
||||
|
||||
3. Boot the device
|
||||
|
||||
Once Solo boots a secure build, it will lock the flash permantly from debugger access. Also the bootloader
|
||||
will only accept signed firmware updates.
|
||||
|
||||
solo program aux leave-dfu
|
||||
|
||||
If you are having problems with solo tool and DFU mode, you could alternatively try booting into DFU
|
||||
by holding down the button while Solo is in bootloader mode. Then try another programming tool that works
|
||||
with ST DFU:
|
||||
|
||||
* STM32CubeProg
|
||||
* openocd
|
||||
* stlink
|
||||
|
||||
Windows users need to install [libusb](https://sourceforge.net/projects/libusb-win32/files/libusb-win32-releases/1.2.6.0/)
|
||||
for solo-python to work with Solo's DFU.
|
||||
|
||||
|
||||
## Programming a Solo that hasn't been programmed
|
||||
|
||||
A Solo that hasn't been programmed will boot into DFU mode. You can program
|
||||
it by following a bootloader, or combined bootloader + application.
|
||||
|
||||
```
|
||||
solo program dfu <bundle-*.hex | all.hex>
|
||||
```
|
||||
|
||||
Then boot the device. Make sure it has a bootloader to boot to.
|
||||
|
||||
```
|
||||
solo program aux leave-dfu
|
||||
```
|
||||
|
||||
## Disable signed firmware updates
|
||||
|
||||
If you'd like to also permanently disable signed updates, plug in your programmed Solo and run the following:
|
||||
|
||||
```bash
|
||||
# WARNING: No more signed updates.
|
||||
solo program disable-bootloader
|
||||
```
|
||||
|
||||
You won't be able to update to any new releases.
|
||||
|
19
docs/solo/solo-extras.md
Normal file
19
docs/solo/solo-extras.md
Normal file
@ -0,0 +1,19 @@
|
||||
# Solo Extras
|
||||
|
||||
## Random number generation
|
||||
|
||||
Solo contains a True Random Number Generator (TRNG). A TRNG is a hardware based mechanism
|
||||
that leverages natural phenomenon to generate random numbers, which can be better than a traditional
|
||||
RNG that has state and updates deterministically using cryptographic methods.
|
||||
|
||||
You can easily access the TRNG stream on Solo using our python tool [`solo-python`](https://github.com/solokeys/solo-python).
|
||||
|
||||
```
|
||||
solo key rng raw > random.bin
|
||||
```
|
||||
|
||||
Or you can seed the state of the RNG on your kernel (`/dev/random`).
|
||||
|
||||
```
|
||||
solo key rng feedkernel
|
||||
```
|
@ -1,30 +1,33 @@
|
||||
# Summary
|
||||
|
||||
On Linux, by default USB dongles can't be accessed by users, for security reasons. To allow user access, so-called "udev rules" must be installed. (Under Fedora, your key may work without such a rule.)
|
||||
On Linux, by default USB dongles can't be accessed by users, for security reasons. To allow user access, so-called "udev rules" must be installed.
|
||||
|
||||
Create a file like [`70-solokeys-access.rules`](https://github.com/solokeys/solo/blob/master/udev/70-solokeys-access.rules) in your `/etc/udev/rules.d` directory, for instance the following rule should cover normal access (it has to be on one line):
|
||||
For some users, things will work automatically:
|
||||
|
||||
- Fedora seems to use a ["universal" udev rule for FIDO devices](https://github.com/amluto/u2f-hidraw-policy)
|
||||
- Our udev rule made it into [libu2f-host](https://github.com/Yubico/libu2f-host/) v1.1.10
|
||||
- Arch Linux [has this package](https://www.archlinux.org/packages/community/x86_64/libu2f-host/)
|
||||
- [Debian sid](https://packages.debian.org/sid/libu2f-udev) and [Ubuntu Eon](https://packages.ubuntu.com/eoan/libu2f-udev) can use the `libu2f-udev` package
|
||||
- Debian Buster and Ubuntu Disco still distribute v1.1.10, so need the manual rule
|
||||
- FreeBSD has support in [u2f-devd](https://github.com/solokeys/solo/issues/144#issuecomment-500216020)
|
||||
|
||||
There is hope that `udev` itself will adopt the Fedora approach (which is to check for HID usage page `F1D0`, and avoids manually whitelisting each U2F/FIDO2 key): <https://github.com/systemd/systemd/issues/11996>.
|
||||
|
||||
Further progress is tracked in: <https://github.com/solokeys/solo/issues/144>.
|
||||
|
||||
If you still need to setup a rule, a simple way to do it is:
|
||||
|
||||
```
|
||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess", MODE="0660", GROUP="plugdev"
|
||||
```
|
||||
|
||||
Additionally, run the following command after you create this file (it is not necessary to do this again in the future):
|
||||
|
||||
```
|
||||
sudo udevadm control --reload-rules && sudo udevadm trigger
|
||||
```
|
||||
|
||||
A simple way to setup both the udev rule and the udevadm reload is:
|
||||
|
||||
```
|
||||
git clone git@github.com:solokeys/solo.git
|
||||
git clone https://github.com/solokeys/solo.git
|
||||
cd solo/udev
|
||||
make setup
|
||||
```
|
||||
|
||||
We are working on getting user access to Solo keys enabled automatically in common Linux distributions: <https://github.com/solokeys/solo/issues/144>.
|
||||
|
||||
|
||||
Or, manually, create a file like [`70-solokeys-access.rules`](https://github.com/solokeys/solo/blob/master/udev/70-solokeys-access.rules) in your `/etc/udev/rules.d` directory.
|
||||
Additionally, run the following command after you create this file (it is not necessary to do this again in the future):
|
||||
```
|
||||
sudo udevadm control --reload-rules && sudo udevadm trigger
|
||||
```
|
||||
|
||||
# How do udev rules work and why are they needed
|
||||
|
||||
|
44
fido2/Makefile
Normal file
44
fido2/Makefile
Normal file
@ -0,0 +1,44 @@
|
||||
include version.mk
|
||||
|
||||
ifndef APP_CONFIG
|
||||
APP_CONFIG=example_app.h
|
||||
endif
|
||||
|
||||
INC = -I./ -I./extensions
|
||||
INC += -I../tinycbor/src
|
||||
INC += -I../crypto/sha256 -I../crypto/micro-ecc -I../crypto/tiny-AES-c
|
||||
INC += -I../crypto/cifra/src -I../crypto/cifra/src/ext
|
||||
|
||||
INT_CFLAGS = -DAPP_CONFIG=\"$(APP_CONFIG)\"
|
||||
INT_CFLAGS += $(INC)
|
||||
INT_CFLAGS += $(SOLO_VERSION_FLAGS)
|
||||
|
||||
SRC = apdu.c util.c u2f.c test_power.c
|
||||
SRC += stubs.c log.c ctaphid.c ctap.c
|
||||
SRC += ctap_parse.c crypto.c
|
||||
SRC += device.c
|
||||
SRC += version.c
|
||||
SRC += data_migration.c
|
||||
SRC += extensions/extensions.c extensions/solo.c
|
||||
SRC += extensions/wallet.c
|
||||
|
||||
# Crypto libs
|
||||
SRC += ../crypto/sha256/sha256.c ../crypto/micro-ecc/uECC.c ../crypto/tiny-AES-c/aes.c
|
||||
SRC += ../crypto/cifra/src/sha512.c ../crypto/cifra/src/blockwise.c
|
||||
|
||||
OBJ = $(SRC:.c=.o)
|
||||
|
||||
all: libsolo.a
|
||||
|
||||
libsolo.a: $(OBJ)
|
||||
$(AR) cqs $@ $^
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $^ $(INT_CFLAGS) $(CFLAGS) -c -o $@
|
||||
|
||||
../crypto/micro-ecc/uECC.o: ../crypto/micro-ecc/uECC.c
|
||||
$(CC) $^ $(INT_CFLAGS) $(ECC_CFLAGS) -c -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(OBJ) libsolo.a
|
||||
|
136
fido2/apdu.c
Normal file
136
fido2/apdu.c
Normal file
@ -0,0 +1,136 @@
|
||||
// Copyright 2019 SoloKeys Developers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
// iso7816:2013. 5.3.2 Decoding conventions for command bodies
|
||||
|
||||
#include "apdu.h"
|
||||
|
||||
uint16_t apdu_decode(uint8_t *data, size_t len, APDU_STRUCT *apdu)
|
||||
{
|
||||
EXT_APDU_HEADER *hapdu = (EXT_APDU_HEADER *)data;
|
||||
|
||||
apdu->cla = hapdu->cla & 0xef; // mask chaining bit if any
|
||||
apdu->ins = hapdu->ins;
|
||||
apdu->p1 = hapdu->p1;
|
||||
apdu->p2 = hapdu->p2;
|
||||
|
||||
apdu->lc = 0;
|
||||
apdu->data = NULL;
|
||||
apdu->le = 0;
|
||||
apdu->extended_apdu = false;
|
||||
apdu->case_type = 0x00;
|
||||
|
||||
uint8_t b0 = hapdu->lc[0];
|
||||
|
||||
// case 1
|
||||
if (len == 4)
|
||||
{
|
||||
apdu->case_type = 0x01;
|
||||
}
|
||||
|
||||
// case 2S (Le)
|
||||
if (len == 5)
|
||||
{
|
||||
apdu->case_type = 0x02;
|
||||
apdu->le = b0;
|
||||
if (!apdu->le)
|
||||
apdu->le = 0x100;
|
||||
}
|
||||
|
||||
// case 3S (Lc + data)
|
||||
if (len == 5U + b0 && b0 != 0)
|
||||
{
|
||||
apdu->case_type = 0x03;
|
||||
apdu->lc = b0;
|
||||
}
|
||||
|
||||
// case 4S (Lc + data + Le)
|
||||
if (len == 5U + b0 + 1U && b0 != 0)
|
||||
{
|
||||
apdu->case_type = 0x04;
|
||||
apdu->lc = b0;
|
||||
apdu->le = data[len - 1];
|
||||
if (!apdu->le)
|
||||
apdu->le = 0x100;
|
||||
}
|
||||
|
||||
// extended length apdu
|
||||
if (len >= 7 && b0 == 0)
|
||||
{
|
||||
uint16_t extlen = (hapdu->lc[1] << 8) + hapdu->lc[2];
|
||||
|
||||
if (len - 7 < extlen)
|
||||
{
|
||||
return SW_WRONG_LENGTH;
|
||||
}
|
||||
|
||||
// case 2E (Le) - extended
|
||||
if (len == 7)
|
||||
{
|
||||
apdu->case_type = 0x12;
|
||||
apdu->extended_apdu = true;
|
||||
apdu->le = extlen;
|
||||
if (!apdu->le)
|
||||
apdu->le = 0x10000;
|
||||
}
|
||||
|
||||
// case 3E (Lc + data) - extended
|
||||
if (len == 7U + extlen)
|
||||
{
|
||||
apdu->case_type = 0x13;
|
||||
apdu->extended_apdu = true;
|
||||
apdu->lc = extlen;
|
||||
}
|
||||
|
||||
// case 4E (Lc + data + Le) - extended 2-byte Le
|
||||
if (len == 7U + extlen + 2U)
|
||||
{
|
||||
apdu->case_type = 0x14;
|
||||
apdu->extended_apdu = true;
|
||||
apdu->lc = extlen;
|
||||
apdu->le = (data[len - 2] << 8) + data[len - 1];
|
||||
if (!apdu->le)
|
||||
apdu->le = 0x10000;
|
||||
}
|
||||
|
||||
// case 4E (Lc + data + Le) - extended 3-byte Le
|
||||
if (len == 7U + extlen + 3U && data[len - 3] == 0)
|
||||
{
|
||||
apdu->case_type = 0x24;
|
||||
apdu->extended_apdu = true;
|
||||
apdu->lc = extlen;
|
||||
apdu->le = (data[len - 2] << 8) + data[len - 1];
|
||||
if (!apdu->le)
|
||||
apdu->le = 0x10000;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((len > 5) && (len - 5 < hapdu->lc[0]))
|
||||
{
|
||||
return SW_WRONG_LENGTH;
|
||||
}
|
||||
}
|
||||
|
||||
if (!apdu->case_type)
|
||||
{
|
||||
return SW_COND_USE_NOT_SATISFIED;
|
||||
}
|
||||
|
||||
if (apdu->lc)
|
||||
{
|
||||
if (apdu->extended_apdu)
|
||||
{
|
||||
apdu->data = data + 7;
|
||||
} else {
|
||||
apdu->data = data + 5;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
33
fido2/apdu.h
33
fido2/apdu.h
@ -2,6 +2,8 @@
|
||||
#define _APDU_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@ -12,19 +14,50 @@ typedef struct
|
||||
uint8_t lc;
|
||||
} __attribute__((packed)) APDU_HEADER;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t cla;
|
||||
uint8_t ins;
|
||||
uint8_t p1;
|
||||
uint8_t p2;
|
||||
uint8_t lc[3];
|
||||
} __attribute__((packed)) EXT_APDU_HEADER;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t cla;
|
||||
uint8_t ins;
|
||||
uint8_t p1;
|
||||
uint8_t p2;
|
||||
uint16_t lc;
|
||||
uint8_t *data;
|
||||
uint32_t le;
|
||||
bool extended_apdu;
|
||||
uint8_t case_type;
|
||||
} __attribute__((packed)) APDU_STRUCT;
|
||||
|
||||
extern uint16_t apdu_decode(uint8_t *data, size_t len, APDU_STRUCT *apdu);
|
||||
|
||||
#define APDU_FIDO_U2F_REGISTER 0x01
|
||||
#define APDU_FIDO_U2F_AUTHENTICATE 0x02
|
||||
#define APDU_FIDO_U2F_VERSION 0x03
|
||||
#define APDU_FIDO_NFCCTAP_MSG 0x10
|
||||
#define APDU_FIDO_U2F_VENDOR_FIRST 0xc0 // First vendor defined command
|
||||
#define APDU_FIDO_U2F_VENDOR_LAST 0xff // Last vendor defined command
|
||||
#define APDU_SOLO_RESET 0xee
|
||||
|
||||
#define APDU_INS_SELECT 0xA4
|
||||
#define APDU_INS_READ_BINARY 0xB0
|
||||
#define APDU_GET_RESPONSE 0xC0
|
||||
|
||||
#define SW_SUCCESS 0x9000
|
||||
#define SW_GET_RESPONSE 0x6100 // Command successfully executed; 'XX' bytes of data are available and can be requested using GET RESPONSE.
|
||||
#define SW_WRONG_LENGTH 0x6700
|
||||
#define SW_COND_USE_NOT_SATISFIED 0x6985
|
||||
#define SW_FILE_NOT_FOUND 0x6a82
|
||||
#define SW_INCORRECT_P1P2 0x6a86
|
||||
#define SW_INS_INVALID 0x6d00 // Instruction code not supported or invalid
|
||||
#define SW_CLA_INVALID 0x6e00
|
||||
#define SW_INTERNAL_EXCEPTION 0x6f00
|
||||
|
||||
#endif //_APDU_H_
|
||||
|
146
fido2/crypto.c
146
fido2/crypto.c
@ -5,29 +5,33 @@
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
/*
|
||||
* Wrapper for crypto implementation on device
|
||||
* Wrapper for crypto implementation on device.
|
||||
*
|
||||
* Can be replaced with different crypto implementation by
|
||||
* defining EXTERNAL_SOLO_CRYPTO
|
||||
*
|
||||
* */
|
||||
#ifndef EXTERNAL_SOLO_CRYPTO
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
|
||||
#include "util.h"
|
||||
#include "crypto.h"
|
||||
|
||||
#ifdef USE_SOFTWARE_IMPLEMENTATION
|
||||
|
||||
#include "sha256.h"
|
||||
#include "uECC.h"
|
||||
#include "aes.h"
|
||||
#include "ctap.h"
|
||||
#include "device.h"
|
||||
#include "log.h"
|
||||
// stuff for SHA512
|
||||
#include "sha2.h"
|
||||
#include "blockwise.h"
|
||||
#include APP_CONFIG
|
||||
#include "log.h"
|
||||
|
||||
|
||||
#ifdef USING_PC
|
||||
typedef enum
|
||||
{
|
||||
MBEDTLS_ECP_DP_NONE = 0,
|
||||
@ -44,53 +48,56 @@ typedef enum
|
||||
MBEDTLS_ECP_DP_SECP224K1, /*!< 224-bits "Koblitz" curve */
|
||||
MBEDTLS_ECP_DP_SECP256K1, /*!< 256-bits "Koblitz" curve */
|
||||
} mbedtls_ecp_group_id;
|
||||
#endif
|
||||
|
||||
|
||||
const uint8_t attestation_cert_der[];
|
||||
const uint16_t attestation_cert_der_size;
|
||||
const uint8_t attestation_key[];
|
||||
const uint16_t attestation_key_size;
|
||||
|
||||
|
||||
|
||||
static SHA256_CTX sha256_ctx;
|
||||
static cf_sha512_context sha512_ctx;
|
||||
static const struct uECC_Curve_t * _es256_curve = NULL;
|
||||
static const uint8_t * _signing_key = NULL;
|
||||
static int _key_len = 0;
|
||||
|
||||
// Secrets for testing only
|
||||
static uint8_t master_secret[64];
|
||||
|
||||
static uint8_t transport_secret[32];
|
||||
|
||||
|
||||
|
||||
void crypto_sha256_init()
|
||||
void crypto_sha256_init(void)
|
||||
{
|
||||
sha256_init(&sha256_ctx);
|
||||
}
|
||||
|
||||
void crypto_reset_master_secret()
|
||||
void crypto_sha512_init(void)
|
||||
{
|
||||
ctap_generate_rng(master_secret, 64);
|
||||
ctap_generate_rng(transport_secret, 32);
|
||||
cf_sha512_init(&sha512_ctx);
|
||||
}
|
||||
|
||||
void crypto_load_master_secret(uint8_t * key)
|
||||
{
|
||||
#if KEY_SPACE_BYTES < 96
|
||||
#error "need more key bytes"
|
||||
#endif
|
||||
#if KEY_SPACE_BYTES < 96
|
||||
#error "need more key bytes"
|
||||
#endif
|
||||
memmove(master_secret, key, 64);
|
||||
memmove(transport_secret, key+64, 32);
|
||||
}
|
||||
|
||||
void crypto_reset_master_secret(void)
|
||||
{
|
||||
memset(master_secret, 0, 64);
|
||||
memset(transport_secret, 0, 32);
|
||||
ctap_generate_rng(master_secret, 64);
|
||||
ctap_generate_rng(transport_secret, 32);
|
||||
}
|
||||
|
||||
|
||||
void crypto_sha256_update(uint8_t * data, size_t len)
|
||||
{
|
||||
sha256_update(&sha256_ctx, data, len);
|
||||
}
|
||||
|
||||
void crypto_sha512_update(const uint8_t * data, size_t len) {
|
||||
cf_sha512_update(&sha512_ctx, data, len);
|
||||
}
|
||||
|
||||
void crypto_sha256_update_secret()
|
||||
{
|
||||
sha256_update(&sha256_ctx, master_secret, 32);
|
||||
@ -101,26 +108,32 @@ void crypto_sha256_final(uint8_t * hash)
|
||||
sha256_final(&sha256_ctx, hash);
|
||||
}
|
||||
|
||||
void crypto_sha512_final(uint8_t * hash)
|
||||
{
|
||||
// NB: there is also cf_sha512_digest
|
||||
cf_sha512_digest_final(&sha512_ctx, hash);
|
||||
}
|
||||
|
||||
void crypto_sha256_hmac_init(uint8_t * key, uint32_t klen, uint8_t * hmac)
|
||||
{
|
||||
uint8_t buf[64];
|
||||
int i;
|
||||
unsigned int i;
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
if (key == CRYPTO_MASTER_KEY)
|
||||
{
|
||||
key = master_secret;
|
||||
klen = sizeof(master_secret);
|
||||
klen = sizeof(master_secret)/2;
|
||||
}
|
||||
else if (key == CRYPTO_TRANSPORT_KEY)
|
||||
{
|
||||
key = transport_secret;
|
||||
klen = 32;
|
||||
}
|
||||
|
||||
|
||||
if(klen > 64)
|
||||
{
|
||||
printf2(TAG_ERR,"Error, key size must be <= 64\n");
|
||||
printf2(TAG_ERR, "Error, key size must be <= 64\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@ -138,19 +151,24 @@ void crypto_sha256_hmac_init(uint8_t * key, uint32_t klen, uint8_t * hmac)
|
||||
void crypto_sha256_hmac_final(uint8_t * key, uint32_t klen, uint8_t * hmac)
|
||||
{
|
||||
uint8_t buf[64];
|
||||
int i;
|
||||
unsigned int i;
|
||||
crypto_sha256_final(hmac);
|
||||
memset(buf, 0, sizeof(buf));
|
||||
if (key == CRYPTO_MASTER_KEY)
|
||||
{
|
||||
key = master_secret;
|
||||
klen = sizeof(master_secret);
|
||||
klen = sizeof(master_secret)/2;
|
||||
}
|
||||
else if (key == CRYPTO_TRANSPORT_KEY2)
|
||||
{
|
||||
key = transport_secret;
|
||||
klen = 32;
|
||||
}
|
||||
|
||||
|
||||
if(klen > 64)
|
||||
{
|
||||
printf2(TAG_ERR,"Error, key size must be <= 64\n");
|
||||
printf2(TAG_ERR, "Error, key size must be <= 64\n");
|
||||
exit(1);
|
||||
}
|
||||
memmove(buf, key, klen);
|
||||
@ -167,16 +185,16 @@ void crypto_sha256_hmac_final(uint8_t * key, uint32_t klen, uint8_t * hmac)
|
||||
}
|
||||
|
||||
|
||||
void crypto_ecc256_init()
|
||||
void crypto_ecc256_init(void)
|
||||
{
|
||||
uECC_set_rng((uECC_RNG_Function)ctap_generate_rng);
|
||||
_es256_curve = uECC_secp256r1();
|
||||
}
|
||||
|
||||
|
||||
void crypto_ecc256_load_attestation_key()
|
||||
void crypto_ecc256_load_attestation_key(void)
|
||||
{
|
||||
_signing_key = attestation_key;
|
||||
_signing_key = device_get_attestation_key();
|
||||
_key_len = 32;
|
||||
}
|
||||
|
||||
@ -184,7 +202,7 @@ void crypto_ecc256_sign(uint8_t * data, int len, uint8_t * sig)
|
||||
{
|
||||
if ( uECC_sign(_signing_key, data, len, sig, _es256_curve) == 0)
|
||||
{
|
||||
printf2(TAG_ERR,"error, uECC failed\n");
|
||||
printf2(TAG_ERR, "error, uECC failed\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
@ -221,19 +239,19 @@ void crypto_ecdsa_sign(uint8_t * data, int len, uint8_t * sig, int MBEDTLS_ECP_I
|
||||
if (_key_len != 32) goto fail;
|
||||
break;
|
||||
default:
|
||||
printf2(TAG_ERR,"error, invalid ECDSA alg specifier\n");
|
||||
printf2(TAG_ERR, "error, invalid ECDSA alg specifier\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ( uECC_sign(_signing_key, data, len, sig, curve) == 0)
|
||||
{
|
||||
printf2(TAG_ERR,"error, uECC failed\n");
|
||||
printf2(TAG_ERR, "error, uECC failed\n");
|
||||
exit(1);
|
||||
}
|
||||
return;
|
||||
|
||||
fail:
|
||||
printf2(TAG_ERR,"error, invalid key length\n");
|
||||
printf2(TAG_ERR, "error, invalid key length\n");
|
||||
exit(1);
|
||||
|
||||
}
|
||||
@ -243,8 +261,11 @@ void generate_private_key(uint8_t * data, int len, uint8_t * data2, int len2, ui
|
||||
crypto_sha256_hmac_init(CRYPTO_MASTER_KEY, 0, privkey);
|
||||
crypto_sha256_update(data, len);
|
||||
crypto_sha256_update(data2, len2);
|
||||
crypto_sha256_update(master_secret, 32);
|
||||
crypto_sha256_update(master_secret, 32); // TODO AES
|
||||
crypto_sha256_hmac_final(CRYPTO_MASTER_KEY, 0, privkey);
|
||||
|
||||
crypto_aes256_init(master_secret + 32, NULL);
|
||||
crypto_aes256_encrypt(privkey, 32);
|
||||
}
|
||||
|
||||
|
||||
@ -261,6 +282,11 @@ void crypto_ecc256_derive_public_key(uint8_t * data, int len, uint8_t * x, uint8
|
||||
memmove(x,pubkey,32);
|
||||
memmove(y,pubkey+32,32);
|
||||
}
|
||||
void crypto_ecc256_compute_public_key(uint8_t * privkey, uint8_t * pubkey)
|
||||
{
|
||||
uECC_compute_public_key(privkey, pubkey, _es256_curve);
|
||||
}
|
||||
|
||||
|
||||
void crypto_load_external_key(uint8_t * key, int len)
|
||||
{
|
||||
@ -273,7 +299,7 @@ void crypto_ecc256_make_key_pair(uint8_t * pubkey, uint8_t * privkey)
|
||||
{
|
||||
if (uECC_make_key(pubkey, privkey, _es256_curve) != 1)
|
||||
{
|
||||
printf2(TAG_ERR,"Error, uECC_make_key failed\n");
|
||||
printf2(TAG_ERR, "Error, uECC_make_key failed\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
@ -282,7 +308,7 @@ void crypto_ecc256_shared_secret(const uint8_t * pubkey, const uint8_t * privkey
|
||||
{
|
||||
if (uECC_shared_secret(pubkey, privkey, shared_secret, _es256_curve) != 1)
|
||||
{
|
||||
printf2(TAG_ERR,"Error, uECC_shared_secret failed\n");
|
||||
printf2(TAG_ERR, "Error, uECC_shared_secret failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@ -333,42 +359,4 @@ void crypto_aes256_encrypt(uint8_t * buf, int length)
|
||||
}
|
||||
|
||||
|
||||
const uint8_t attestation_cert_der[] =
|
||||
"\x30\x82\x01\xfb\x30\x82\x01\xa1\xa0\x03\x02\x01\x02\x02\x01\x00\x30\x0a\x06\x08"
|
||||
"\x2a\x86\x48\xce\x3d\x04\x03\x02\x30\x2c\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13"
|
||||
"\x02\x55\x53\x31\x0b\x30\x09\x06\x03\x55\x04\x08\x0c\x02\x4d\x44\x31\x10\x30\x0e"
|
||||
"\x06\x03\x55\x04\x0a\x0c\x07\x54\x45\x53\x54\x20\x43\x41\x30\x20\x17\x0d\x31\x38"
|
||||
"\x30\x35\x31\x30\x30\x33\x30\x36\x32\x30\x5a\x18\x0f\x32\x30\x36\x38\x30\x34\x32"
|
||||
"\x37\x30\x33\x30\x36\x32\x30\x5a\x30\x7c\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13"
|
||||
"\x02\x55\x53\x31\x0b\x30\x09\x06\x03\x55\x04\x08\x0c\x02\x4d\x44\x31\x0f\x30\x0d"
|
||||
"\x06\x03\x55\x04\x07\x0c\x06\x4c\x61\x75\x72\x65\x6c\x31\x15\x30\x13\x06\x03\x55"
|
||||
"\x04\x0a\x0c\x0c\x54\x45\x53\x54\x20\x43\x4f\x4d\x50\x41\x4e\x59\x31\x22\x30\x20"
|
||||
"\x06\x03\x55\x04\x0b\x0c\x19\x41\x75\x74\x68\x65\x6e\x74\x69\x63\x61\x74\x6f\x72"
|
||||
"\x20\x41\x74\x74\x65\x73\x74\x61\x74\x69\x6f\x6e\x31\x14\x30\x12\x06\x03\x55\x04"
|
||||
"\x03\x0c\x0b\x63\x6f\x6e\x6f\x72\x70\x70\x2e\x63\x6f\x6d\x30\x59\x30\x13\x06\x07"
|
||||
"\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00"
|
||||
"\x04\x45\xa9\x02\xc1\x2e\x9c\x0a\x33\xfa\x3e\x84\x50\x4a\xb8\x02\xdc\x4d\xb9\xaf"
|
||||
"\x15\xb1\xb6\x3a\xea\x8d\x3f\x03\x03\x55\x65\x7d\x70\x3f\xb4\x02\xa4\x97\xf4\x83"
|
||||
"\xb8\xa6\xf9\x3c\xd0\x18\xad\x92\x0c\xb7\x8a\x5a\x3e\x14\x48\x92\xef\x08\xf8\xca"
|
||||
"\xea\xfb\x32\xab\x20\xa3\x62\x30\x60\x30\x46\x06\x03\x55\x1d\x23\x04\x3f\x30\x3d"
|
||||
"\xa1\x30\xa4\x2e\x30\x2c\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31"
|
||||
"\x0b\x30\x09\x06\x03\x55\x04\x08\x0c\x02\x4d\x44\x31\x10\x30\x0e\x06\x03\x55\x04"
|
||||
"\x0a\x0c\x07\x54\x45\x53\x54\x20\x43\x41\x82\x09\x00\xf7\xc9\xec\x89\xf2\x63\x94"
|
||||
"\xd9\x30\x09\x06\x03\x55\x1d\x13\x04\x02\x30\x00\x30\x0b\x06\x03\x55\x1d\x0f\x04"
|
||||
"\x04\x03\x02\x04\xf0\x30\x0a\x06\x08\x2a\x86\x48\xce\x3d\x04\x03\x02\x03\x48\x00"
|
||||
"\x30\x45\x02\x20\x18\x38\xb0\x45\x03\x69\xaa\xa7\xb7\x38\x62\x01\xaf\x24\x97\x5e"
|
||||
"\x7e\x74\x64\x1b\xa3\x7b\xf7\xe6\xd3\xaf\x79\x28\xdb\xdc\xa5\x88\x02\x21\x00\xcd"
|
||||
"\x06\xf1\xe3\xab\x16\x21\x8e\xd8\xc0\x14\xaf\x09\x4f\x5b\x73\xef\x5e\x9e\x4b\xe7"
|
||||
"\x35\xeb\xdd\x9b\x6d\x8f\x7d\xf3\xc4\x3a\xd7";
|
||||
|
||||
|
||||
const uint16_t attestation_cert_der_size = sizeof(attestation_cert_der)-1;
|
||||
|
||||
|
||||
const uint8_t attestation_key[] = "\xcd\x67\xaa\x31\x0d\x09\x1e\xd1\x6e\x7e\x98\x92\xaa\x07\x0e\x19\x94\xfc\xd7\x14\xae\x7c\x40\x8f\xb9\x46\xb7\x2e\x5f\xe7\x5d\x30";
|
||||
const uint16_t attestation_key_size = sizeof(attestation_key)-1;
|
||||
|
||||
|
||||
#else
|
||||
#error "No crypto implementation defined"
|
||||
#endif
|
||||
|
@ -9,8 +9,6 @@
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define USE_SOFTWARE_IMPLEMENTATION
|
||||
|
||||
void crypto_sha256_init();
|
||||
void crypto_sha256_update(uint8_t * data, size_t len);
|
||||
void crypto_sha256_update_secret();
|
||||
@ -23,9 +21,9 @@ void crypto_sha512_init();
|
||||
void crypto_sha512_update(const uint8_t * data, size_t len);
|
||||
void crypto_sha512_final(uint8_t * hash);
|
||||
|
||||
|
||||
void crypto_ecc256_init();
|
||||
void crypto_ecc256_derive_public_key(uint8_t * data, int len, uint8_t * x, uint8_t * y);
|
||||
void crypto_ecc256_compute_public_key(uint8_t * privkey, uint8_t * pubkey);
|
||||
|
||||
void crypto_ecc256_load_key(uint8_t * data, int len, uint8_t * data2, int len2);
|
||||
void crypto_ecc256_load_attestation_key();
|
||||
@ -38,6 +36,7 @@ void generate_private_key(uint8_t * data, int len, uint8_t * data2, int len2, ui
|
||||
void crypto_ecc256_make_key_pair(uint8_t * pubkey, uint8_t * privkey);
|
||||
void crypto_ecc256_shared_secret(const uint8_t * pubkey, const uint8_t * privkey, uint8_t * shared_secret);
|
||||
|
||||
#define CRYPTO_TRANSPORT_KEY2 ((uint8_t*)2)
|
||||
#define CRYPTO_TRANSPORT_KEY ((uint8_t*)1)
|
||||
#define CRYPTO_MASTER_KEY ((uint8_t*)0)
|
||||
|
||||
@ -52,10 +51,4 @@ void crypto_reset_master_secret();
|
||||
void crypto_load_master_secret(uint8_t * key);
|
||||
|
||||
|
||||
extern const uint8_t attestation_cert_der[];
|
||||
extern const uint16_t attestation_cert_der_size;
|
||||
|
||||
extern const uint8_t attestation_key[];
|
||||
extern const uint16_t attestation_key_size;
|
||||
|
||||
#endif
|
||||
|
1138
fido2/ctap.c
1138
fido2/ctap.c
File diff suppressed because it is too large
Load Diff
75
fido2/ctap.h
75
fido2/ctap.h
@ -16,12 +16,11 @@
|
||||
#define CTAP_CLIENT_PIN 0x06
|
||||
#define CTAP_RESET 0x07
|
||||
#define GET_NEXT_ASSERTION 0x08
|
||||
#define CTAP_CBOR_CRED_MGMT 0x0A
|
||||
#define CTAP_VENDOR_FIRST 0x40
|
||||
#define CTAP_CBOR_CRED_MGMT_PRE 0x41
|
||||
#define CTAP_VENDOR_LAST 0xBF
|
||||
|
||||
// AAGUID For Solo
|
||||
#define CTAP_AAGUID ((uint8_t*)"\x88\x76\x63\x1b\xd4\xa0\x42\x7f\x57\x73\x0e\xc7\x1c\x9e\x02\x79")
|
||||
|
||||
#define MC_clientDataHash 0x01
|
||||
#define MC_rp 0x02
|
||||
#define MC_user 0x03
|
||||
@ -40,6 +39,19 @@
|
||||
#define GA_pinAuth 0x06
|
||||
#define GA_pinProtocol 0x07
|
||||
|
||||
#define CM_cmd 0x01
|
||||
#define CM_cmdMetadata 0x01
|
||||
#define CM_cmdRPBegin 0x02
|
||||
#define CM_cmdRPNext 0x03
|
||||
#define CM_cmdRKBegin 0x04
|
||||
#define CM_cmdRKNext 0x05
|
||||
#define CM_cmdRKDelete 0x06
|
||||
#define CM_subCommandParams 0x02
|
||||
#define CM_subCommandRpId 0x01
|
||||
#define CM_subCommandCred 0x02
|
||||
#define CM_pinProtocol 0x03
|
||||
#define CM_pinAuth 0x04
|
||||
|
||||
#define CP_pinProtocol 0x01
|
||||
#define CP_subCommand 0x02
|
||||
#define CP_cmdGetRetries 0x01
|
||||
@ -61,6 +73,11 @@
|
||||
#define EXT_HMAC_SECRET_REQUESTED 0x01
|
||||
#define EXT_HMAC_SECRET_PARSED 0x02
|
||||
|
||||
#define EXT_CRED_PROTECT_INVALID 0x00
|
||||
#define EXT_CRED_PROTECT_OPTIONAL 0x01
|
||||
#define EXT_CRED_PROTECT_OPTIONAL_WITH_CREDID 0x02
|
||||
#define EXT_CRED_PROTECT_REQUIRED 0x03
|
||||
|
||||
#define RESP_versions 0x1
|
||||
#define RESP_extensions 0x2
|
||||
#define RESP_aaguid 0x3
|
||||
@ -131,7 +148,7 @@
|
||||
#define PIN_LOCKOUT_ATTEMPTS 8 // Number of attempts total
|
||||
#define PIN_BOOT_ATTEMPTS 3 // number of attempts per boot
|
||||
|
||||
#define CTAP2_UP_DELAY_MS 5000
|
||||
#define CTAP2_UP_DELAY_MS 29000
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@ -144,16 +161,29 @@ typedef struct
|
||||
|
||||
typedef struct {
|
||||
uint8_t tag[CREDENTIAL_TAG_SIZE];
|
||||
uint8_t nonce[CREDENTIAL_NONCE_SIZE];
|
||||
union {
|
||||
uint8_t nonce[CREDENTIAL_NONCE_SIZE];
|
||||
struct {
|
||||
uint8_t _pad[CREDENTIAL_NONCE_SIZE - 4];
|
||||
uint32_t value;
|
||||
}__attribute__((packed)) metadata;
|
||||
}__attribute__((packed)) entropy;
|
||||
uint8_t rpIdHash[32];
|
||||
uint32_t count;
|
||||
}__attribute__((packed)) CredentialId;
|
||||
|
||||
struct Credential {
|
||||
struct __attribute__((packed)) Credential {
|
||||
CredentialId id;
|
||||
CTAP_userEntity user;
|
||||
};
|
||||
typedef struct Credential CTAP_residentKey;
|
||||
typedef struct {
|
||||
CredentialId id;
|
||||
CTAP_userEntity user;
|
||||
|
||||
// Maximum amount of "extra" space in resident key.
|
||||
uint8_t rpId[48];
|
||||
uint8_t rpIdSize;
|
||||
} __attribute__((packed)) CTAP_residentKey;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@ -220,6 +250,7 @@ typedef struct
|
||||
{
|
||||
uint8_t hmac_secret_present;
|
||||
CTAP_hmac_secret hmac_secret;
|
||||
uint32_t cred_protect;
|
||||
} CTAP_extensions;
|
||||
|
||||
typedef struct
|
||||
@ -288,6 +319,26 @@ typedef struct
|
||||
|
||||
} CTAP_getAssertion;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int cmd;
|
||||
struct {
|
||||
uint8_t rpIdHash[32];
|
||||
CTAP_credentialDescriptor credentialDescriptor;
|
||||
} subCommandParams;
|
||||
|
||||
struct {
|
||||
uint8_t cmd;
|
||||
uint8_t subCommandParamsCborCopy[sizeof(CTAP_credentialDescriptor) + 16];
|
||||
} hashed;
|
||||
uint32_t subCommandParamsCborSize;
|
||||
|
||||
uint8_t pinAuth[16];
|
||||
uint8_t pinAuthPresent;
|
||||
int pinProtocol;
|
||||
} CTAP_credMgmt;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int pinProtocol;
|
||||
@ -306,7 +357,12 @@ typedef struct
|
||||
|
||||
|
||||
struct _getAssertionState {
|
||||
CTAP_authDataHeader authData;
|
||||
// Room for both authData struct and extensions
|
||||
struct {
|
||||
CTAP_authDataHeader authData;
|
||||
uint8_t extensions[80];
|
||||
} __attribute__((packed)) buf;
|
||||
CTAP_extensions extensions;
|
||||
uint8_t clientDataHash[CLIENT_DATA_HASH_SIZE];
|
||||
CTAP_credentialDescriptor creds[ALLOW_LIST_MAX_SIZE];
|
||||
uint8_t lastcmd;
|
||||
@ -359,5 +415,8 @@ uint16_t ctap_key_len(uint8_t index);
|
||||
extern uint8_t PIN_TOKEN[PIN_TOKEN_SIZE];
|
||||
extern uint8_t KEY_AGREEMENT_PUB[64];
|
||||
|
||||
void lock_device_permanently();
|
||||
|
||||
void ctap_load_external_keys(uint8_t * keybytes);
|
||||
|
||||
#endif
|
||||
|
@ -49,6 +49,7 @@
|
||||
#define CTAP2_ERR_PIN_POLICY_VIOLATION 0x37
|
||||
#define CTAP2_ERR_PIN_TOKEN_EXPIRED 0x38
|
||||
#define CTAP2_ERR_REQUEST_TOO_LARGE 0x39
|
||||
#define CTAP2_ERR_ACTION_TIMEOUT 0x3A
|
||||
#define CTAP1_ERR_OTHER 0x7F
|
||||
#define CTAP2_ERR_SPEC_LAST 0xDF
|
||||
#define CTAP2_ERR_EXTENSION_FIRST 0xE0
|
||||
|
@ -666,8 +666,8 @@ uint8_t ctap_parse_extensions(CborValue * val, CTAP_extensions * ext)
|
||||
if (ret == CborErrorOutOfMemory)
|
||||
{
|
||||
printf2(TAG_ERR,"Error, rp map key is too large. Ignoring.\n");
|
||||
cbor_value_advance(&map);
|
||||
cbor_value_advance(&map);
|
||||
check_ret( cbor_value_advance(&map) );
|
||||
check_ret( cbor_value_advance(&map) );
|
||||
continue;
|
||||
}
|
||||
check_ret(ret);
|
||||
@ -698,6 +698,14 @@ uint8_t ctap_parse_extensions(CborValue * val, CTAP_extensions * ext)
|
||||
printf1(TAG_RED, "warning: hmac_secret request ignored for being wrong type\r\n");
|
||||
}
|
||||
}
|
||||
else if (strncmp(key, "credProtect",11) == 0) {
|
||||
if (cbor_value_get_type(&map) == CborIntegerType) {
|
||||
ret = cbor_value_get_int(&map, (int*)&ext->cred_protect);
|
||||
check_ret(ret);
|
||||
} else {
|
||||
printf1(TAG_RED, "warning: credProtect request ignored for being wrong type\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
ret = cbor_value_advance(&map);
|
||||
check_ret(ret);
|
||||
@ -715,6 +723,7 @@ uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encod
|
||||
CborValue it,map;
|
||||
|
||||
memset(MC, 0, sizeof(CTAP_makeCredential));
|
||||
MC->up = 0xff;
|
||||
ret = cbor_parser_init(request, length, CborValidateCanonicalFormat, &parser, &it);
|
||||
check_retr(ret);
|
||||
|
||||
@ -870,7 +879,7 @@ uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encod
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
cbor_value_advance(&map);
|
||||
ret = cbor_value_advance(&map);
|
||||
check_ret(ret);
|
||||
}
|
||||
|
||||
@ -929,7 +938,15 @@ uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor *
|
||||
|
||||
buflen = sizeof(type);
|
||||
ret = cbor_value_copy_text_string(&val, type, &buflen, NULL);
|
||||
check_ret(ret);
|
||||
if (ret == CborErrorOutOfMemory)
|
||||
{
|
||||
cred->type = PUB_KEY_CRED_UNKNOWN;
|
||||
}
|
||||
else
|
||||
{
|
||||
check_ret(ret);
|
||||
}
|
||||
|
||||
|
||||
if (strncmp(type, "public-key",11) == 0)
|
||||
{
|
||||
@ -990,6 +1007,163 @@ uint8_t parse_allow_list(CTAP_getAssertion * GA, CborValue * it)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t parse_cred_mgmt_subcommandparams(CborValue * val, CTAP_credMgmt * CM)
|
||||
{
|
||||
size_t map_length;
|
||||
int key;
|
||||
int ret;
|
||||
unsigned int i;
|
||||
CborValue map;
|
||||
size_t sz = 32;
|
||||
|
||||
if (cbor_value_get_type(val) != CborMapType)
|
||||
{
|
||||
printf2(TAG_ERR,"error, wrong type\n");
|
||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||
}
|
||||
|
||||
|
||||
ret = cbor_value_enter_container(val,&map);
|
||||
check_ret(ret);
|
||||
|
||||
const uint8_t * start_byte = cbor_value_get_next_byte(&map) - 1;
|
||||
|
||||
ret = cbor_value_get_map_length(val, &map_length);
|
||||
check_ret(ret);
|
||||
|
||||
for (i = 0; i < map_length; i++)
|
||||
{
|
||||
if (cbor_value_get_type(&map) != CborIntegerType)
|
||||
{
|
||||
printf2(TAG_ERR,"Error, expecting integer type for map key, got %s\n", cbor_value_get_type_string(&map));
|
||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||
}
|
||||
ret = cbor_value_get_int(&map, &key);
|
||||
check_ret(ret);
|
||||
ret = cbor_value_advance(&map);
|
||||
check_ret(ret);
|
||||
switch(key)
|
||||
{
|
||||
case CM_subCommandRpId:
|
||||
ret = cbor_value_copy_byte_string(&map, CM->subCommandParams.rpIdHash, &sz, NULL);
|
||||
if (ret == CborErrorOutOfMemory)
|
||||
{
|
||||
printf2(TAG_ERR,"Error, map key is too large\n");
|
||||
return CTAP2_ERR_LIMIT_EXCEEDED;
|
||||
}
|
||||
check_ret(ret);
|
||||
break;
|
||||
case CM_subCommandCred:
|
||||
ret = parse_credential_descriptor(&map, &CM->subCommandParams.credentialDescriptor);
|
||||
check_ret(ret);;
|
||||
break;
|
||||
}
|
||||
ret = cbor_value_advance(&map);
|
||||
check_ret(ret);
|
||||
}
|
||||
|
||||
const uint8_t * end_byte = cbor_value_get_next_byte(&map);
|
||||
|
||||
uint32_t length = (uint32_t)end_byte - (uint32_t)start_byte;
|
||||
if (length > sizeof(CM->hashed.subCommandParamsCborCopy))
|
||||
{
|
||||
return CTAP2_ERR_LIMIT_EXCEEDED;
|
||||
}
|
||||
// Copy the details that were hashed so they can be verified later.
|
||||
memmove(CM->hashed.subCommandParamsCborCopy, start_byte, length);
|
||||
CM->subCommandParamsCborSize = length;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t ctap_parse_cred_mgmt(CTAP_credMgmt * CM, uint8_t * request, int length)
|
||||
{
|
||||
int ret;
|
||||
unsigned int i;
|
||||
int key;
|
||||
size_t map_length;
|
||||
CborParser parser;
|
||||
CborValue it,map;
|
||||
|
||||
memset(CM, 0, sizeof(CTAP_credMgmt));
|
||||
ret = cbor_parser_init(request, length, CborValidateCanonicalFormat, &parser, &it);
|
||||
check_ret(ret);
|
||||
|
||||
CborType type = cbor_value_get_type(&it);
|
||||
if (type != CborMapType)
|
||||
{
|
||||
printf2(TAG_ERR,"Error, expecting cbor map\n");
|
||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||
}
|
||||
|
||||
|
||||
ret = cbor_value_enter_container(&it,&map);
|
||||
check_ret(ret);
|
||||
|
||||
ret = cbor_value_get_map_length(&it, &map_length);
|
||||
check_ret(ret);
|
||||
|
||||
printf1(TAG_PARSE, "CM map has %d elements\n", map_length);
|
||||
|
||||
for (i = 0; i < map_length; i++)
|
||||
{
|
||||
type = cbor_value_get_type(&map);
|
||||
if (type != CborIntegerType)
|
||||
{
|
||||
printf2(TAG_ERR,"Error, expecting int for map key\n");
|
||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||
}
|
||||
ret = cbor_value_get_int_checked(&map, &key);
|
||||
check_ret(ret);
|
||||
|
||||
ret = cbor_value_advance(&map);
|
||||
check_ret(ret);
|
||||
|
||||
switch(key)
|
||||
{
|
||||
case CM_cmd:
|
||||
printf1(TAG_PARSE, "CM_cmd\n");
|
||||
if (cbor_value_get_type(&map) == CborIntegerType)
|
||||
{
|
||||
ret = cbor_value_get_int_checked(&map, &CM->cmd);
|
||||
check_ret(ret);
|
||||
CM->hashed.cmd = CM->cmd;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||
}
|
||||
break;
|
||||
case CM_subCommandParams:
|
||||
printf1(TAG_PARSE, "CM_subCommandParams\n");
|
||||
ret = parse_cred_mgmt_subcommandparams(&map, CM);
|
||||
check_ret(ret);
|
||||
break;
|
||||
case CM_pinProtocol:
|
||||
printf1(TAG_PARSE, "CM_pinProtocol\n");
|
||||
if (cbor_value_get_type(&map) == CborIntegerType)
|
||||
{
|
||||
ret = cbor_value_get_int_checked(&map, &CM->pinProtocol);
|
||||
check_ret(ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||
}
|
||||
break;
|
||||
case CM_pinAuth:
|
||||
printf1(TAG_PARSE, "CM_pinAuth\n");
|
||||
ret = parse_fixed_byte_string(&map, CM->pinAuth, 16);
|
||||
check_retr(ret);
|
||||
CM->pinAuthPresent = 1;
|
||||
break;
|
||||
}
|
||||
ret = cbor_value_advance(&map);
|
||||
check_ret(ret);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int length)
|
||||
{
|
||||
@ -1002,6 +1176,7 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int
|
||||
|
||||
memset(GA, 0, sizeof(CTAP_getAssertion));
|
||||
GA->creds = getAssertionState.creds; // Save stack memory
|
||||
GA->up = 0xff;
|
||||
|
||||
ret = cbor_parser_init(request, length, CborValidateCanonicalFormat, &parser, &it);
|
||||
check_ret(ret);
|
||||
@ -1122,7 +1297,7 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int
|
||||
return ret;
|
||||
}
|
||||
|
||||
cbor_value_advance(&map);
|
||||
ret = cbor_value_advance(&map);
|
||||
check_ret(ret);
|
||||
}
|
||||
|
||||
@ -1343,11 +1518,21 @@ uint8_t ctap_parse_client_pin(CTAP_clientPin * CP, uint8_t * request, int length
|
||||
break;
|
||||
case CP_getKeyAgreement:
|
||||
printf1(TAG_CP,"CP_getKeyAgreement\n");
|
||||
if (cbor_value_get_type(&map) != CborBooleanType)
|
||||
{
|
||||
printf2(TAG_ERR,"Error, expecting cbor boolean\n");
|
||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||
}
|
||||
ret = cbor_value_get_boolean(&map, &CP->getKeyAgreement);
|
||||
check_ret(ret);
|
||||
break;
|
||||
case CP_getRetries:
|
||||
printf1(TAG_CP,"CP_getRetries\n");
|
||||
if (cbor_value_get_type(&map) != CborBooleanType)
|
||||
{
|
||||
printf2(TAG_ERR,"Error, expecting cbor boolean\n");
|
||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||
}
|
||||
ret = cbor_value_get_boolean(&map, &CP->getRetries);
|
||||
check_ret(ret);
|
||||
break;
|
||||
|
@ -35,6 +35,7 @@ uint8_t parse_cose_key(CborValue * it, COSE_key * cose);
|
||||
|
||||
uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encoder, uint8_t * request, int length);
|
||||
uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int length);
|
||||
uint8_t ctap_parse_cred_mgmt(CTAP_credMgmt * CM, uint8_t * request, int length);
|
||||
uint8_t ctap_parse_client_pin(CTAP_clientPin * CP, uint8_t * request, int length);
|
||||
uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor * cred);
|
||||
|
||||
|
378
fido2/ctaphid.c
378
fido2/ctaphid.c
@ -16,6 +16,7 @@
|
||||
#include "util.h"
|
||||
#include "log.h"
|
||||
#include "extensions.h"
|
||||
#include "version.h"
|
||||
|
||||
// move custom SHA512 command out,
|
||||
// and the following headers too
|
||||
@ -274,7 +275,7 @@ static void ctaphid_write(CTAPHID_WRITE_BUFFER * wb, void * _data, int len)
|
||||
if (wb->offset > 0)
|
||||
{
|
||||
memset(wb->buf + wb->offset, 0, HID_MESSAGE_SIZE - wb->offset);
|
||||
ctaphid_write_block(wb->buf);
|
||||
usbhid_send(wb->buf);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -303,7 +304,7 @@ static void ctaphid_write(CTAPHID_WRITE_BUFFER * wb, void * _data, int len)
|
||||
wb->bytes_written += 1;
|
||||
if (wb->offset == HID_MESSAGE_SIZE)
|
||||
{
|
||||
ctaphid_write_block(wb->buf);
|
||||
usbhid_send(wb->buf);
|
||||
wb->offset = 0;
|
||||
}
|
||||
}
|
||||
@ -538,11 +539,17 @@ extern void _check_ret(CborError ret, int line, const char * filename);
|
||||
#define check_hardcore(r) _check_ret(r,__LINE__, __FILE__);\
|
||||
if ((r) != CborNoError) exit(1);
|
||||
|
||||
|
||||
uint8_t ctaphid_custom_command(int len, CTAP_RESPONSE * ctap_resp, CTAPHID_WRITE_BUFFER * wb);
|
||||
|
||||
|
||||
extern void solo_lock_if_not_already();
|
||||
|
||||
uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
|
||||
{
|
||||
uint8_t cmd;
|
||||
uint8_t cmd = 0;
|
||||
uint32_t cid;
|
||||
int len;
|
||||
int len = 0;
|
||||
#ifndef DISABLE_CTAPHID_CBOR
|
||||
int status;
|
||||
#endif
|
||||
@ -552,6 +559,10 @@ uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
|
||||
CTAP_RESPONSE ctap_resp;
|
||||
|
||||
int bufstatus = ctaphid_buffer_packet(pkt_raw, &cmd, &cid, &len);
|
||||
ctaphid_write_buffer_init(&wb);
|
||||
|
||||
wb.cid = cid;
|
||||
wb.cmd = cmd;
|
||||
|
||||
if (bufstatus == HID_IGNORE)
|
||||
{
|
||||
@ -587,9 +598,6 @@ uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
|
||||
case CTAPHID_PING:
|
||||
printf1(TAG_HID,"CTAPHID_PING\n");
|
||||
|
||||
ctaphid_write_buffer_init(&wb);
|
||||
wb.cid = cid;
|
||||
wb.cmd = CTAPHID_PING;
|
||||
wb.bcnt = len;
|
||||
timestamp();
|
||||
ctaphid_write(&wb, ctap_buffer, len);
|
||||
@ -602,13 +610,9 @@ uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
|
||||
case CTAPHID_WINK:
|
||||
printf1(TAG_HID,"CTAPHID_WINK\n");
|
||||
|
||||
ctaphid_write_buffer_init(&wb);
|
||||
|
||||
device_wink();
|
||||
|
||||
wb.cid = cid;
|
||||
wb.cmd = CTAPHID_WINK;
|
||||
|
||||
ctaphid_write(&wb,NULL,0);
|
||||
|
||||
break;
|
||||
@ -633,10 +637,10 @@ uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
|
||||
ctap_response_init(&ctap_resp);
|
||||
status = ctap_request(ctap_buffer, len, &ctap_resp);
|
||||
|
||||
ctaphid_write_buffer_init(&wb);
|
||||
wb.cid = cid;
|
||||
wb.cmd = CTAPHID_CBOR;
|
||||
wb.bcnt = (ctap_resp.length+1);
|
||||
wb.cid = cid;
|
||||
wb.cmd = cmd;
|
||||
|
||||
|
||||
|
||||
timestamp();
|
||||
@ -666,10 +670,10 @@ uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
|
||||
ctap_response_init(&ctap_resp);
|
||||
u2f_request((struct u2f_request_apdu*)ctap_buffer, &ctap_resp);
|
||||
|
||||
ctaphid_write_buffer_init(&wb);
|
||||
wb.cid = cid;
|
||||
wb.cmd = CTAPHID_MSG;
|
||||
wb.bcnt = (ctap_resp.length);
|
||||
wb.cid = cid;
|
||||
wb.cmd = cmd;
|
||||
|
||||
|
||||
ctaphid_write(&wb, ctap_resp.data, ctap_resp.length);
|
||||
ctaphid_write(&wb, NULL, 0);
|
||||
@ -679,209 +683,14 @@ uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
|
||||
printf1(TAG_HID,"CTAPHID_CANCEL\n");
|
||||
is_busy = 0;
|
||||
break;
|
||||
#if defined(IS_BOOTLOADER)
|
||||
case CTAPHID_BOOT:
|
||||
printf1(TAG_HID,"CTAPHID_BOOT\n");
|
||||
ctap_response_init(&ctap_resp);
|
||||
u2f_set_writeback_buffer(&ctap_resp);
|
||||
is_busy = bootloader_bridge(len, ctap_buffer);
|
||||
|
||||
ctaphid_write_buffer_init(&wb);
|
||||
wb.cid = cid;
|
||||
wb.cmd = CTAPHID_BOOT;
|
||||
wb.bcnt = (ctap_resp.length + 1);
|
||||
ctaphid_write(&wb, &is_busy, 1);
|
||||
ctaphid_write(&wb, ctap_resp.data, ctap_resp.length);
|
||||
ctaphid_write(&wb, NULL, 0);
|
||||
is_busy = 0;
|
||||
break;
|
||||
#endif
|
||||
#if defined(SOLO_HACKER)
|
||||
case CTAPHID_ENTERBOOT:
|
||||
printf1(TAG_HID,"CTAPHID_ENTERBOOT\n");
|
||||
boot_solo_bootloader();
|
||||
ctaphid_write_buffer_init(&wb);
|
||||
wb.cid = cid;
|
||||
wb.cmd = CTAPHID_ENTERBOOT;
|
||||
wb.bcnt = 0;
|
||||
ctaphid_write(&wb, NULL, 0);
|
||||
is_busy = 0;
|
||||
break;
|
||||
case CTAPHID_ENTERSTBOOT:
|
||||
printf1(TAG_HID,"CTAPHID_ENTERBOOT\n");
|
||||
boot_st_bootloader();
|
||||
break;
|
||||
#endif
|
||||
#if !defined(IS_BOOTLOADER)
|
||||
case CTAPHID_GETRNG:
|
||||
printf1(TAG_HID,"CTAPHID_GETRNG\n");
|
||||
ctap_response_init(&ctap_resp);
|
||||
ctaphid_write_buffer_init(&wb);
|
||||
wb.cid = cid;
|
||||
wb.cmd = CTAPHID_GETRNG;
|
||||
wb.bcnt = ctap_buffer[0];
|
||||
if (!wb.bcnt)
|
||||
wb.bcnt = 57;
|
||||
memset(ctap_buffer,0,wb.bcnt);
|
||||
ctap_generate_rng(ctap_buffer, wb.bcnt);
|
||||
ctaphid_write(&wb, &ctap_buffer, wb.bcnt);
|
||||
ctaphid_write(&wb, NULL, 0);
|
||||
is_busy = 0;
|
||||
break;
|
||||
#endif
|
||||
#if defined(SOLO_HACKER) && (DEBUG_LEVEL > 0) && (!IS_BOOTLOADER == 1)
|
||||
case CTAPHID_PROBE:
|
||||
|
||||
/*
|
||||
* Expects CBOR-serialized data of the form
|
||||
* {"subcommand": "hash_type", "data": b"the_data"}
|
||||
* with hash_type in SHA256, SHA512
|
||||
*/
|
||||
|
||||
// some random logging
|
||||
printf1(TAG_HID,"CTAPHID_PROBE\n");
|
||||
// initialise CTAP response object
|
||||
ctap_response_init(&ctap_resp);
|
||||
// initialise write buffer
|
||||
ctaphid_write_buffer_init(&wb);
|
||||
wb.cid = cid;
|
||||
wb.cmd = CTAPHID_PROBE;
|
||||
|
||||
// prepare parsing (or halt)
|
||||
int ret;
|
||||
CborParser parser;
|
||||
CborValue it, map;
|
||||
ret = cbor_parser_init(
|
||||
ctap_buffer, (size_t) buffer_len(),
|
||||
// strictly speaking, CTAP is not RFC canonical...
|
||||
CborValidateCanonicalFormat,
|
||||
&parser, &it);
|
||||
check_hardcore(ret);
|
||||
|
||||
CborType type = cbor_value_get_type(&it);
|
||||
if (type != CborMapType) exit(1);
|
||||
|
||||
ret = cbor_value_enter_container(&it,&map);
|
||||
check_hardcore(ret);
|
||||
|
||||
size_t map_length = 0;
|
||||
ret = cbor_value_get_map_length(&it, &map_length);
|
||||
if (map_length != 2) exit(1);
|
||||
|
||||
// parse subcommand (or halt)
|
||||
CborValue val;
|
||||
ret = cbor_value_map_find_value(&it, "subcommand", &val);
|
||||
check_hardcore(ret);
|
||||
if (!cbor_value_is_text_string(&val))
|
||||
exit(1);
|
||||
|
||||
int sha_version = 0;
|
||||
bool found = false;
|
||||
if (!found) {
|
||||
ret = cbor_value_text_string_equals(
|
||||
&val, "SHA256", &found);
|
||||
check_hardcore(ret);
|
||||
if (found)
|
||||
sha_version = 256;
|
||||
}
|
||||
if (!found) {
|
||||
ret = cbor_value_text_string_equals(
|
||||
&val, "SHA512", &found);
|
||||
check_hardcore(ret);
|
||||
if (found)
|
||||
sha_version = 512;
|
||||
}
|
||||
if (sha_version == 0)
|
||||
exit(1);
|
||||
|
||||
// parse data (or halt)
|
||||
ret = cbor_value_map_find_value(&it, "data", &val);
|
||||
check_hardcore(ret);
|
||||
if (!cbor_value_is_byte_string(&val))
|
||||
exit(1);
|
||||
|
||||
size_t data_length = 0;
|
||||
ret = cbor_value_calculate_string_length(&val, &data_length);
|
||||
check_hardcore(ret);
|
||||
if (data_length > 6*1024)
|
||||
exit(1);
|
||||
|
||||
unsigned char data[6*1024];
|
||||
ret = cbor_value_copy_byte_string (
|
||||
&val, &data[0], &data_length, &val);
|
||||
check_hardcore(ret);
|
||||
|
||||
// execute subcommand
|
||||
if (sha_version == 256) {
|
||||
// calculate hash
|
||||
crypto_sha256_init();
|
||||
crypto_sha256_update(data, data_length);
|
||||
crypto_sha256_final(ctap_buffer);
|
||||
// write output
|
||||
wb.bcnt = CF_SHA256_HASHSZ; // 32 bytes
|
||||
ctaphid_write(&wb, &ctap_buffer, CF_SHA256_HASHSZ);
|
||||
}
|
||||
|
||||
if (sha_version == 512) {
|
||||
// calculate hash
|
||||
crypto_sha512_init();
|
||||
crypto_sha512_update(data, data_length);
|
||||
crypto_sha512_final(ctap_buffer);
|
||||
// write output
|
||||
wb.bcnt = CF_SHA512_HASHSZ; // 64 bytes
|
||||
ctaphid_write(&wb, &ctap_buffer, CF_SHA512_HASHSZ);
|
||||
}
|
||||
|
||||
// finalize
|
||||
ctaphid_write(&wb, NULL, 0);
|
||||
is_busy = 0;
|
||||
break;
|
||||
|
||||
/*
|
||||
case CTAPHID_SHA256:
|
||||
// some random logging
|
||||
printf1(TAG_HID,"CTAPHID_SHA256\n");
|
||||
// initialise CTAP response object
|
||||
ctap_response_init(&ctap_resp);
|
||||
// initialise write buffer
|
||||
ctaphid_write_buffer_init(&wb);
|
||||
wb.cid = cid;
|
||||
wb.cmd = CTAPHID_SHA256;
|
||||
wb.bcnt = CF_SHA256_HASHSZ; // 32 bytes
|
||||
// calculate hash
|
||||
crypto_sha256_init();
|
||||
crypto_sha256_update(ctap_buffer, buffer_len());
|
||||
crypto_sha256_final(ctap_buffer);
|
||||
// copy to output
|
||||
ctaphid_write(&wb, &ctap_buffer, CF_SHA256_HASHSZ);
|
||||
ctaphid_write(&wb, NULL, 0);
|
||||
is_busy = 0;
|
||||
break;
|
||||
case CTAPHID_SHA512:
|
||||
// some random logging
|
||||
printf1(TAG_HID,"CTAPHID_SHA512\n");
|
||||
// initialise CTAP response object
|
||||
ctap_response_init(&ctap_resp);
|
||||
// initialise write buffer
|
||||
ctaphid_write_buffer_init(&wb);
|
||||
wb.cid = cid;
|
||||
wb.cmd = CTAPHID_SHA512;
|
||||
wb.bcnt = CF_SHA512_HASHSZ; // 64 bytes
|
||||
// calculate hash
|
||||
crypto_sha512_init();
|
||||
crypto_sha512_update(ctap_buffer, buffer_len());
|
||||
crypto_sha512_final(ctap_buffer);
|
||||
// copy to output
|
||||
ctaphid_write(&wb, &ctap_buffer, CF_SHA512_HASHSZ);
|
||||
ctaphid_write(&wb, NULL, 0);
|
||||
is_busy = 0;
|
||||
break;
|
||||
*/
|
||||
#endif
|
||||
default:
|
||||
printf2(TAG_ERR,"error, unimplemented HID cmd: %02x\r\n", buffer_cmd());
|
||||
ctaphid_send_error(cid, CTAP1_ERR_INVALID_COMMAND);
|
||||
break;
|
||||
if (ctaphid_custom_command(len, &ctap_resp, &wb) != 0){
|
||||
is_busy = 0;
|
||||
}else{
|
||||
printf2(TAG_ERR, "error, unimplemented HID cmd: %02x\r\n", buffer_cmd());
|
||||
ctaphid_send_error(cid, CTAP1_ERR_INVALID_COMMAND);
|
||||
}
|
||||
}
|
||||
cid_del(cid);
|
||||
buffer_reset();
|
||||
@ -891,3 +700,136 @@ uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
|
||||
else return 0;
|
||||
|
||||
}
|
||||
|
||||
uint8_t ctaphid_custom_command(int len, CTAP_RESPONSE * ctap_resp, CTAPHID_WRITE_BUFFER * wb)
|
||||
{
|
||||
ctap_response_init(ctap_resp);
|
||||
|
||||
#if !defined(IS_BOOTLOADER) && (defined(SOLO_EXPERIMENTAL))
|
||||
uint32_t param;
|
||||
#endif
|
||||
#if defined(IS_BOOTLOADER)
|
||||
uint8_t is_busy;
|
||||
#endif
|
||||
|
||||
switch(wb->cmd)
|
||||
{
|
||||
#if defined(IS_BOOTLOADER)
|
||||
case CTAPHID_BOOT:
|
||||
printf1(TAG_HID,"CTAPHID_BOOT\n");
|
||||
u2f_set_writeback_buffer(ctap_resp);
|
||||
is_busy = bootloader_bridge(len, ctap_buffer);
|
||||
wb->bcnt = 1 + ctap_resp->length;
|
||||
|
||||
ctaphid_write(wb, &is_busy, 1);
|
||||
ctaphid_write(wb, ctap_resp->data, ctap_resp->length);
|
||||
ctaphid_write(wb, NULL, 0);
|
||||
return 1;
|
||||
#endif
|
||||
#if defined(SOLO)
|
||||
case CTAPHID_ENTERBOOT:
|
||||
printf1(TAG_HID,"CTAPHID_ENTERBOOT\n");
|
||||
boot_solo_bootloader();
|
||||
wb->bcnt = 0;
|
||||
ctaphid_write(wb, NULL, 0);
|
||||
return 1;
|
||||
#endif
|
||||
#if defined(SOLO)
|
||||
case CTAPHID_REBOOT:
|
||||
device_reboot();
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
#if !defined(IS_BOOTLOADER)
|
||||
case CTAPHID_GETRNG:
|
||||
printf1(TAG_HID,"CTAPHID_GETRNG\n");
|
||||
wb->bcnt = ctap_buffer[0];
|
||||
if (!wb->bcnt)
|
||||
wb->bcnt = 57;
|
||||
memset(ctap_buffer,0,wb->bcnt);
|
||||
ctap_generate_rng(ctap_buffer, wb->bcnt);
|
||||
ctaphid_write(wb, ctap_buffer, wb->bcnt);
|
||||
ctaphid_write(wb, NULL, 0);
|
||||
return 1;
|
||||
break;
|
||||
#endif
|
||||
|
||||
case CTAPHID_GETVERSION:
|
||||
printf1(TAG_HID,"CTAPHID_GETVERSION\n");
|
||||
wb->bcnt = 4;
|
||||
ctap_buffer[0] = SOLO_VERSION_MAJ;
|
||||
ctap_buffer[1] = SOLO_VERSION_MIN;
|
||||
ctap_buffer[2] = SOLO_VERSION_PATCH;
|
||||
#if defined(SOLO)
|
||||
ctap_buffer[3] = solo_is_locked();
|
||||
#else
|
||||
ctap_buffer[3] = 0;
|
||||
#endif
|
||||
ctaphid_write(wb, ctap_buffer, 4);
|
||||
ctaphid_write(wb, NULL, 0);
|
||||
return 1;
|
||||
break;
|
||||
|
||||
// Remove on next release
|
||||
#if !defined(IS_BOOTLOADER) && defined(SOLO)
|
||||
case 0x99:
|
||||
solo_lock_if_not_already();
|
||||
wb->bcnt = 0;
|
||||
ctaphid_write(wb, NULL, 0);
|
||||
return 1;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if !defined(IS_BOOTLOADER) && (defined(SOLO_EXPERIMENTAL))
|
||||
case CTAPHID_LOADKEY:
|
||||
/**
|
||||
* Load external key. Useful for enabling backups.
|
||||
* bytes: 4 4 96
|
||||
* payload: version [maj rev patch RFU]| counter_replacement (BE) | master_key |
|
||||
*
|
||||
* Counter should be increased by a large amount, e.g. (0x10000000)
|
||||
* to outdo any previously lost/broken keys.
|
||||
*/
|
||||
printf1(TAG_HID,"CTAPHID_LOADKEY\n");
|
||||
if (len != 104)
|
||||
{
|
||||
printf2(TAG_ERR,"Error, invalid length.\n");
|
||||
ctaphid_send_error(wb->cid, CTAP1_ERR_INVALID_LENGTH);
|
||||
return 1;
|
||||
}
|
||||
param = ctap_buffer[0] << 16;
|
||||
param |= ctap_buffer[1] << 8;
|
||||
param |= ctap_buffer[2] << 0;
|
||||
if (param != 0){
|
||||
ctaphid_send_error(wb->cid, CTAP2_ERR_UNSUPPORTED_OPTION);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Ask for THREE button presses
|
||||
if (ctap_user_presence_test(8000) > 0)
|
||||
if (ctap_user_presence_test(2000) > 0)
|
||||
if (ctap_user_presence_test(2000) > 0)
|
||||
{
|
||||
ctap_load_external_keys(ctap_buffer + 8);
|
||||
param = ctap_buffer[7];
|
||||
param |= ctap_buffer[6] << 8;
|
||||
param |= ctap_buffer[5] << 16;
|
||||
param |= ctap_buffer[4] << 24;
|
||||
ctap_atomic_count(param);
|
||||
|
||||
wb->bcnt = 0;
|
||||
|
||||
ctaphid_write(wb, NULL, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf2(TAG_ERR, "Error, invalid length.\n");
|
||||
ctaphid_send_error(wb->cid, CTAP2_ERR_OPERATION_DENIED);
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -27,7 +27,10 @@
|
||||
#define CTAPHID_BOOT (TYPE_INIT | 0x50)
|
||||
#define CTAPHID_ENTERBOOT (TYPE_INIT | 0x51)
|
||||
#define CTAPHID_ENTERSTBOOT (TYPE_INIT | 0x52)
|
||||
#define CTAPHID_REBOOT (TYPE_INIT | 0x53)
|
||||
#define CTAPHID_GETRNG (TYPE_INIT | 0x60)
|
||||
#define CTAPHID_GETVERSION (TYPE_INIT | 0x61)
|
||||
#define CTAPHID_LOADKEY (TYPE_INIT | 0x62)
|
||||
// reserved for debug, not implemented except for HACKER and DEBUG_LEVEl > 0
|
||||
#define CTAPHID_PROBE (TYPE_INIT | 0x70)
|
||||
|
||||
@ -57,6 +60,8 @@
|
||||
|
||||
#define CTAP_CAPABILITIES (CAPABILITY_WINK | CAPABILITY_CBOR)
|
||||
|
||||
#define HID_MESSAGE_SIZE 64
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t cid;
|
||||
|
90
fido2/data_migration.c
Normal file
90
fido2/data_migration.c
Normal file
@ -0,0 +1,90 @@
|
||||
// Copyright 2019 SoloKeys Developers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
#include "data_migration.h"
|
||||
#include "log.h"
|
||||
#include "device.h"
|
||||
#include "crypto.h"
|
||||
|
||||
// TODO move from macro to function/assert for better readability?
|
||||
#define check(x) assert(state_prev_0xff->x == state_tmp_ptr->x);
|
||||
#define check_buf(x) assert(memcmp(state_prev_0xff->x, state_tmp_ptr->x, sizeof(state_tmp_ptr->x)) == 0);
|
||||
|
||||
bool migrate_from_FF_to_01(AuthenticatorState_0xFF* state_prev_0xff, AuthenticatorState_0x01* state_tmp_ptr){
|
||||
// Calculate PIN hash, and replace PIN raw storage with it; add version to structure
|
||||
// other ingredients do not change
|
||||
if (state_tmp_ptr->data_version != 0xFF)
|
||||
return false;
|
||||
|
||||
static_assert(sizeof(AuthenticatorState_0xFF) <= sizeof(AuthenticatorState_0x01), "New state structure is smaller, than current one, which is not handled");
|
||||
|
||||
if (ctap_generate_rng(state_tmp_ptr->PIN_SALT, sizeof(state_tmp_ptr->PIN_SALT)) != 1) {
|
||||
printf2(TAG_ERR, "Error, rng failed\n");
|
||||
return false;
|
||||
}
|
||||
if (state_prev_0xff->is_pin_set){
|
||||
crypto_sha256_init();
|
||||
crypto_sha256_update(state_prev_0xff->pin_code, state_prev_0xff->pin_code_length);
|
||||
uint8_t intermediateHash[32];
|
||||
crypto_sha256_final(intermediateHash);
|
||||
|
||||
crypto_sha256_init();
|
||||
crypto_sha256_update(intermediateHash, 16);
|
||||
memset(intermediateHash, 0, sizeof(intermediateHash));
|
||||
crypto_sha256_update(state_tmp_ptr->PIN_SALT, sizeof(state_tmp_ptr->PIN_SALT));
|
||||
crypto_sha256_final(state_tmp_ptr->PIN_CODE_HASH);
|
||||
}
|
||||
|
||||
assert(state_tmp_ptr->_reserved == state_prev_0xff->pin_code_length);
|
||||
state_tmp_ptr->_reserved = 0xFF;
|
||||
state_tmp_ptr->data_version = 1;
|
||||
|
||||
check(is_initialized);
|
||||
check(is_pin_set);
|
||||
check(remaining_tries);
|
||||
check(rk_stored);
|
||||
check_buf(key_lens);
|
||||
check_buf(key_space);
|
||||
assert(state_tmp_ptr->data_version != 0xFF);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void save_migrated_state(AuthenticatorState *state_tmp_ptr) {
|
||||
memmove(&STATE, state_tmp_ptr, sizeof(AuthenticatorState));
|
||||
authenticator_write_state(state_tmp_ptr);
|
||||
}
|
||||
|
||||
void do_migration_if_required(AuthenticatorState* state_current){
|
||||
// Currently handles only state structures with the same size, or bigger
|
||||
// FIXME rework to raw buffers with fixed size to allow state structure size decrease
|
||||
if(!state_current->is_initialized)
|
||||
return;
|
||||
|
||||
AuthenticatorState state_tmp;
|
||||
AuthenticatorState state_previous;
|
||||
authenticator_read_state(&state_previous);
|
||||
authenticator_read_state(&state_tmp);
|
||||
if(state_current->data_version == 0xFF){
|
||||
printf2(TAG_ERR, "Running migration\n");
|
||||
bool success = migrate_from_FF_to_01((AuthenticatorState_0xFF *) &state_previous, &state_tmp);
|
||||
if (!success){
|
||||
printf2(TAG_ERR, "Failed migration from 0xFF to 1\n");
|
||||
// FIXME discuss migration failure behavior
|
||||
goto return_cleanup;
|
||||
}
|
||||
dump_hex1(TAG_ERR, (void*)&state_tmp, sizeof(state_tmp));
|
||||
dump_hex1(TAG_ERR, (void*)&state_previous, sizeof(state_previous));
|
||||
save_migrated_state(&state_tmp);
|
||||
}
|
||||
|
||||
assert(state_current->data_version == STATE_VERSION);
|
||||
|
||||
return_cleanup:
|
||||
memset(&state_tmp, 0, sizeof(AuthenticatorState));
|
||||
memset(&state_previous, 0, sizeof(AuthenticatorState));
|
||||
}
|
15
fido2/data_migration.h
Normal file
15
fido2/data_migration.h
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright 2019 SoloKeys Developers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
#ifndef FIDO2_PR_DATA_MIGRATION_H
|
||||
#define FIDO2_PR_DATA_MIGRATION_H
|
||||
|
||||
#include "storage.h"
|
||||
|
||||
void do_migration_if_required(AuthenticatorState* state_current);
|
||||
|
||||
#endif //FIDO2_PR_DATA_MIGRATION_H
|
225
fido2/device.c
Normal file
225
fido2/device.c
Normal file
@ -0,0 +1,225 @@
|
||||
// Copyright 2019 SoloKeys Developers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
/** device.c
|
||||
*
|
||||
* This contains (weak) implementations
|
||||
* to get FIDO2 working initially on a device. They probably
|
||||
* aren't what you want to keep, but are designed to be replaced
|
||||
* with some other platform specific implementation.
|
||||
*
|
||||
* For real examples, see the STM32L4 implementation and the PC implementation of device.c.
|
||||
*
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "ctaphid.h"
|
||||
#include "log.h"
|
||||
#include APP_CONFIG
|
||||
|
||||
#define RK_NUM 50
|
||||
|
||||
struct ResidentKeyStore {
|
||||
CTAP_residentKey rks[RK_NUM];
|
||||
} RK_STORE;
|
||||
|
||||
|
||||
static bool _up_disabled = false;
|
||||
|
||||
static uint8_t _attestation_cert_der[] =
|
||||
"\x30\x82\x01\xfb\x30\x82\x01\xa1\xa0\x03\x02\x01\x02\x02\x01\x00\x30\x0a\x06\x08"
|
||||
"\x2a\x86\x48\xce\x3d\x04\x03\x02\x30\x2c\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13"
|
||||
"\x02\x55\x53\x31\x0b\x30\x09\x06\x03\x55\x04\x08\x0c\x02\x4d\x44\x31\x10\x30\x0e"
|
||||
"\x06\x03\x55\x04\x0a\x0c\x07\x54\x45\x53\x54\x20\x43\x41\x30\x20\x17\x0d\x31\x38"
|
||||
"\x30\x35\x31\x30\x30\x33\x30\x36\x32\x30\x5a\x18\x0f\x32\x30\x36\x38\x30\x34\x32"
|
||||
"\x37\x30\x33\x30\x36\x32\x30\x5a\x30\x7c\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13"
|
||||
"\x02\x55\x53\x31\x0b\x30\x09\x06\x03\x55\x04\x08\x0c\x02\x4d\x44\x31\x0f\x30\x0d"
|
||||
"\x06\x03\x55\x04\x07\x0c\x06\x4c\x61\x75\x72\x65\x6c\x31\x15\x30\x13\x06\x03\x55"
|
||||
"\x04\x0a\x0c\x0c\x54\x45\x53\x54\x20\x43\x4f\x4d\x50\x41\x4e\x59\x31\x22\x30\x20"
|
||||
"\x06\x03\x55\x04\x0b\x0c\x19\x41\x75\x74\x68\x65\x6e\x74\x69\x63\x61\x74\x6f\x72"
|
||||
"\x20\x41\x74\x74\x65\x73\x74\x61\x74\x69\x6f\x6e\x31\x14\x30\x12\x06\x03\x55\x04"
|
||||
"\x03\x0c\x0b\x63\x6f\x6e\x6f\x72\x70\x70\x2e\x63\x6f\x6d\x30\x59\x30\x13\x06\x07"
|
||||
"\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00"
|
||||
"\x04\x45\xa9\x02\xc1\x2e\x9c\x0a\x33\xfa\x3e\x84\x50\x4a\xb8\x02\xdc\x4d\xb9\xaf"
|
||||
"\x15\xb1\xb6\x3a\xea\x8d\x3f\x03\x03\x55\x65\x7d\x70\x3f\xb4\x02\xa4\x97\xf4\x83"
|
||||
"\xb8\xa6\xf9\x3c\xd0\x18\xad\x92\x0c\xb7\x8a\x5a\x3e\x14\x48\x92\xef\x08\xf8\xca"
|
||||
"\xea\xfb\x32\xab\x20\xa3\x62\x30\x60\x30\x46\x06\x03\x55\x1d\x23\x04\x3f\x30\x3d"
|
||||
"\xa1\x30\xa4\x2e\x30\x2c\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31"
|
||||
"\x0b\x30\x09\x06\x03\x55\x04\x08\x0c\x02\x4d\x44\x31\x10\x30\x0e\x06\x03\x55\x04"
|
||||
"\x0a\x0c\x07\x54\x45\x53\x54\x20\x43\x41\x82\x09\x00\xf7\xc9\xec\x89\xf2\x63\x94"
|
||||
"\xd9\x30\x09\x06\x03\x55\x1d\x13\x04\x02\x30\x00\x30\x0b\x06\x03\x55\x1d\x0f\x04"
|
||||
"\x04\x03\x02\x04\xf0\x30\x0a\x06\x08\x2a\x86\x48\xce\x3d\x04\x03\x02\x03\x48\x00"
|
||||
"\x30\x45\x02\x20\x18\x38\xb0\x45\x03\x69\xaa\xa7\xb7\x38\x62\x01\xaf\x24\x97\x5e"
|
||||
"\x7e\x74\x64\x1b\xa3\x7b\xf7\xe6\xd3\xaf\x79\x28\xdb\xdc\xa5\x88\x02\x21\x00\xcd"
|
||||
"\x06\xf1\xe3\xab\x16\x21\x8e\xd8\xc0\x14\xaf\x09\x4f\x5b\x73\xef\x5e\x9e\x4b\xe7"
|
||||
"\x35\xeb\xdd\x9b\x6d\x8f\x7d\xf3\xc4\x3a\xd7";
|
||||
|
||||
|
||||
__attribute__((weak)) void device_attestation_read_cert_der(uint8_t * dst){
|
||||
memmove(dst, _attestation_cert_der, device_attestation_cert_der_get_size());
|
||||
}
|
||||
|
||||
__attribute__((weak)) uint8_t * device_get_attestation_key(){
|
||||
static uint8_t attestation_key[] =
|
||||
"\xcd\x67\xaa\x31\x0d\x09\x1e\xd1\x6e\x7e\x98\x92\xaa"
|
||||
"\x07\x0e\x19\x94\xfc\xd7\x14\xae\x7c\x40\x8f\xb9\x46"
|
||||
"\xb7\x2e\x5f\xe7\x5d\x30";
|
||||
return attestation_key;
|
||||
}
|
||||
|
||||
__attribute__((weak)) uint16_t device_attestation_cert_der_get_size(){
|
||||
return sizeof(_attestation_cert_der)-1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) void device_reboot()
|
||||
{
|
||||
printf1(TAG_RED, "REBOOT command recieved!\r\n");
|
||||
exit(100);
|
||||
}
|
||||
|
||||
__attribute__((weak)) void device_set_status(uint32_t status)
|
||||
{
|
||||
static uint32_t __device_status = 0;
|
||||
if (status != CTAPHID_STATUS_IDLE && __device_status != status)
|
||||
{
|
||||
ctaphid_update_status(status);
|
||||
}
|
||||
__device_status = status;
|
||||
}
|
||||
|
||||
|
||||
__attribute__((weak)) void usbhid_close(){/**/}
|
||||
|
||||
|
||||
__attribute__((weak)) void device_init(int argc, char *argv[]){/**/}
|
||||
|
||||
__attribute__((weak)) void device_disable_up(bool disable)
|
||||
{
|
||||
_up_disabled = disable;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int ctap_user_presence_test(uint32_t d)
|
||||
{
|
||||
if (_up_disabled)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int ctap_user_verification(uint8_t arg)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) uint32_t ctap_atomic_count(uint32_t amount)
|
||||
{
|
||||
static uint32_t counter1 = 25;
|
||||
counter1 += (amount + 1);
|
||||
return counter1;
|
||||
}
|
||||
|
||||
|
||||
__attribute__((weak)) int ctap_generate_rng(uint8_t * dst, size_t num)
|
||||
{
|
||||
int i;
|
||||
printf1(TAG_ERR, "Insecure RNG being used.\r\n");
|
||||
for (i = 0; i < num; i++){
|
||||
dst[i] = (uint8_t)rand();
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((weak)) int device_is_nfc()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((weak)) void device_wink()
|
||||
{
|
||||
printf1(TAG_GREEN,"*WINK*\n");
|
||||
}
|
||||
|
||||
__attribute__((weak)) void device_set_clock_rate(DEVICE_CLOCK_RATE param){/**/}
|
||||
|
||||
static AuthenticatorState _tmp_state = {0};
|
||||
__attribute__((weak)) int authenticator_read_state(AuthenticatorState * s){
|
||||
if (_tmp_state.is_initialized != INITIALIZED_MARKER){
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
memmove(s, &_tmp_state, sizeof(AuthenticatorState));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((weak)) void authenticator_write_state(AuthenticatorState * s){
|
||||
memmove(&_tmp_state, s, sizeof(AuthenticatorState));
|
||||
}
|
||||
|
||||
__attribute__((weak)) void ctap_reset_rk()
|
||||
{
|
||||
memset(&RK_STORE,0xff,sizeof(RK_STORE));
|
||||
}
|
||||
|
||||
__attribute__((weak)) uint32_t ctap_rk_size()
|
||||
{
|
||||
return RK_NUM;
|
||||
}
|
||||
|
||||
|
||||
__attribute__((weak)) void ctap_store_rk(int index, CTAP_residentKey * rk)
|
||||
{
|
||||
if (index < RK_NUM)
|
||||
{
|
||||
memmove(RK_STORE.rks + index, rk, sizeof(CTAP_residentKey));
|
||||
}
|
||||
else
|
||||
{
|
||||
printf1(TAG_ERR,"Out of bounds for store_rk\r\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
__attribute__((weak)) void ctap_delete_rk(int index)
|
||||
{
|
||||
CTAP_residentKey rk;
|
||||
memset(&rk, 0xff, sizeof(CTAP_residentKey));
|
||||
|
||||
if (index < RK_NUM)
|
||||
{
|
||||
memmove(RK_STORE.rks + index, &rk, sizeof(CTAP_residentKey));
|
||||
}
|
||||
else
|
||||
{
|
||||
printf1(TAG_ERR,"Out of bounds for delete_rk\r\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
__attribute__((weak)) void ctap_load_rk(int index, CTAP_residentKey * rk)
|
||||
{
|
||||
memmove(rk, RK_STORE.rks + index, sizeof(CTAP_residentKey));
|
||||
}
|
||||
|
||||
__attribute__((weak)) void ctap_overwrite_rk(int index, CTAP_residentKey * rk)
|
||||
{
|
||||
if (index < RK_NUM)
|
||||
{
|
||||
memmove(RK_STORE.rks + index, rk, sizeof(CTAP_residentKey));
|
||||
}
|
||||
else
|
||||
{
|
||||
printf1(TAG_ERR,"Out of bounds for store_rk\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((weak)) void device_read_aaguid(uint8_t * dst){
|
||||
uint8_t * aaguid = (uint8_t *)"\x00\x76\x63\x1b\xd4\xa0\x42\x7f\x57\x73\x0e\xc7\x1c\x9e\x02\x79";
|
||||
memmove(dst, aaguid, 16);
|
||||
}
|
||||
|
224
fido2/device.h
224
fido2/device.h
@ -9,81 +9,166 @@
|
||||
|
||||
#include "storage.h"
|
||||
|
||||
void device_init(int argc, char *argv[]);
|
||||
|
||||
/** Return a millisecond timestamp. Does not need to be synchronized to anything.
|
||||
* *Optional* to compile, but will not calculate delays correctly without a correct implementation.
|
||||
*/
|
||||
uint32_t millis();
|
||||
|
||||
void delay(uint32_t ms);
|
||||
|
||||
// HID message size in bytes
|
||||
#define HID_MESSAGE_SIZE 64
|
||||
|
||||
void usbhid_init();
|
||||
|
||||
int usbhid_recv(uint8_t * msg);
|
||||
|
||||
/** Called by HIDUSB layer to write bytes to the USB HID interface endpoint.
|
||||
* Will write 64 bytes at a time.
|
||||
*
|
||||
* @param msg Pointer to a 64 byte buffer containing a payload to be sent via USB HID.
|
||||
*
|
||||
* **Required** to compile and work for FIDO application.
|
||||
*/
|
||||
void usbhid_send(uint8_t * msg);
|
||||
|
||||
void usbhid_close();
|
||||
|
||||
void main_loop_delay();
|
||||
/** Reboot / power reset the device.
|
||||
* **Optional** this is not used for FIDO2, and simply won't do anything if not implemented.
|
||||
*/
|
||||
void device_reboot();
|
||||
|
||||
void heartbeat();
|
||||
/** Read AuthenticatorState from nonvolatile memory.
|
||||
* @param s pointer to AuthenticatorState buffer to be overwritten with contents from NV memory.
|
||||
* @return 0 - state stored is NOT initialized.
|
||||
* 1 - state stored is initialized.
|
||||
*
|
||||
* *Optional* this is required to make persistant updates to FIDO2 State (PIN and device master secret).
|
||||
* Without it, changes simply won't be persistant.
|
||||
*/
|
||||
int authenticator_read_state(AuthenticatorState * s);
|
||||
|
||||
|
||||
void authenticator_read_state(AuthenticatorState * );
|
||||
|
||||
void authenticator_read_backup_state(AuthenticatorState * );
|
||||
|
||||
// Return 1 yes backup is init'd, else 0
|
||||
//void authenticator_initialize()
|
||||
int authenticator_is_backup_initialized();
|
||||
|
||||
void authenticator_write_state(AuthenticatorState *, int backup);
|
||||
|
||||
// Called each main loop. Doesn't need to do anything.
|
||||
void device_manage();
|
||||
/** Store changes in the authenticator state to nonvolatile memory.
|
||||
* @param s pointer to valid Authenticator state to write to NV memory.
|
||||
*
|
||||
* *Optional* this is required to make persistant updates to FIDO2 State (PIN and device master secret).
|
||||
* Without it, changes simply won't be persistant.
|
||||
*/
|
||||
void authenticator_write_state(AuthenticatorState * s);
|
||||
|
||||
// sets status that's uses for sending status updates ~100ms.
|
||||
// A timer should be set up to call `ctaphid_update_status`
|
||||
|
||||
/** Updates status of the status of the FIDO2 layer application, which
|
||||
* can be used for polling updates in the USBHID layer.
|
||||
*
|
||||
* @param status is one of the following, which can be used appropriately by USB HID layer.
|
||||
#define CTAPHID_STATUS_IDLE 0
|
||||
#define CTAPHID_STATUS_PROCESSING 1
|
||||
#define CTAPHID_STATUS_UPNEEDED 2
|
||||
*
|
||||
* *Optional* to compile and run, but will be required to be used for proper FIDO2 operation with some platforms.
|
||||
*/
|
||||
void device_set_status(uint32_t status);
|
||||
|
||||
// Returns if button is currently pressed
|
||||
/** Returns true if button is currently pressed. Debouncing does not need to be handled. Should not block.
|
||||
* @return 1 if button is currently pressed.
|
||||
*
|
||||
* *Optional* to compile and run, but just returns one by default.
|
||||
*/
|
||||
int device_is_button_pressed();
|
||||
|
||||
// Test for user presence
|
||||
// Return 1 for user is present, 0 user not present, -1 if cancel is requested.
|
||||
//
|
||||
// Return 2 for disabled, 1 for user is present, 0 user not present, -1 if cancel is requested.
|
||||
/** Test for user presence.
|
||||
* Perform test that user is present. Returns status on user presence. This is used by FIDO and U2F layer
|
||||
* to check if an operation should continue, or if the UP flag should be set.
|
||||
*
|
||||
* @param delay number of milliseconds to delay waiting for user before timeout.
|
||||
*
|
||||
* @return 2 - User presence is disabled. Operation should continue, but UP flag not set.
|
||||
* 1 - User presence confirmed. Operation should continue, and UP flag is set.
|
||||
* 0 - User presence is not confirmed. Operation should be denied.
|
||||
* -1 - Operation was canceled. Do not continue, reset transaction state.
|
||||
*
|
||||
* *Optional*, the default implementation will return 1, unless a FIDO2 operation calls for no UP, where this will then return 2.
|
||||
*/
|
||||
int ctap_user_presence_test(uint32_t delay);
|
||||
|
||||
// Generate @num bytes of random numbers to @dest
|
||||
// return 1 if success, error otherwise
|
||||
/** Disable the next user presence test. This is called by FIDO2 layer when a transaction
|
||||
* requests UP to be disabled. The next call to ctap_user_presence_test should return 2,
|
||||
* and then UP should be enabled again.
|
||||
*
|
||||
* @param request_active indicate to activate (true) or disable (false) UP.
|
||||
*
|
||||
* *Optional*, the default implementation will provide expected behaviour with the default ctap_user_presence_test(...).
|
||||
*/
|
||||
void device_disable_up(bool request_active);
|
||||
|
||||
/** Generate random numbers. Random numbers should be good enough quality for
|
||||
* cryptographic use.
|
||||
*
|
||||
* @param dst the buffer to write into.
|
||||
* @param num the number of bytes to generate and write to dst.
|
||||
*
|
||||
* @return 1 if successful, or else the RNG failed.
|
||||
*
|
||||
* *Optional*, if not implemented, the random numbers will be from rand() and an error will be logged.
|
||||
*/
|
||||
int ctap_generate_rng(uint8_t * dst, size_t num);
|
||||
|
||||
// Increment atomic counter and return it.
|
||||
// Must support two counters, @sel selects counter0 or counter1.
|
||||
uint32_t ctap_atomic_count(int sel);
|
||||
/** Increment an atomic (non-volatile) counter and return the value.
|
||||
*
|
||||
* @param amount a non-zero amount to increment the counter by.
|
||||
*
|
||||
* *Optional*, if not implemented, the counter will not be persistant.
|
||||
*/
|
||||
uint32_t ctap_atomic_count(uint32_t amount);
|
||||
|
||||
// Verify the user
|
||||
// return 1 if user is verified, 0 if not
|
||||
int ctap_user_verification(uint8_t arg);
|
||||
|
||||
// Must be implemented by application
|
||||
// data is HID_MESSAGE_SIZE long in bytes
|
||||
void ctaphid_write_block(uint8_t * data);
|
||||
|
||||
|
||||
// Resident key
|
||||
/** Delete all resident keys.
|
||||
*
|
||||
* *Optional*, if not implemented, operates on non-persistant RK's.
|
||||
*/
|
||||
void ctap_reset_rk();
|
||||
|
||||
/** Return the maximum amount of resident keys that can be stored.
|
||||
* @return max number of resident keys that can be stored, including already stored RK's.
|
||||
*
|
||||
* *Optional*, if not implemented, returns 50.
|
||||
*/
|
||||
uint32_t ctap_rk_size();
|
||||
|
||||
/** Store a resident key into an index between [ 0, ctap_rk_size() ).
|
||||
* Storage should be in non-volatile memory.
|
||||
*
|
||||
* @param index between RK index range.
|
||||
* @param rk pointer to valid rk structure that should be written to NV memory.
|
||||
*
|
||||
* *Optional*, if not implemented, operates on non-persistant RK's.
|
||||
*/
|
||||
void ctap_store_rk(int index,CTAP_residentKey * rk);
|
||||
|
||||
/** Delete a resident key from an index.
|
||||
* @param index to delete resident key from. Has no effect if no RK exists at index.
|
||||
*
|
||||
* *Optional*, if not implemented, operates on non-persistant RK's.
|
||||
*/
|
||||
void ctap_delete_rk(int index);
|
||||
|
||||
/** Read a resident key from an index into memory
|
||||
* @param index to read resident key from.
|
||||
* @param rk pointer to resident key structure to write into with RK.
|
||||
*
|
||||
* *Optional*, if not implemented, operates on non-persistant RK's.
|
||||
*/
|
||||
void ctap_load_rk(int index,CTAP_residentKey * rk);
|
||||
|
||||
/** Overwrite the RK located in index with a new RK.
|
||||
* @param index to write resident key to.
|
||||
* @param rk pointer to valid rk structure that should be written to NV memory, and replace existing RK there.
|
||||
*
|
||||
* *Optional*, if not implemented, operates on non-persistant RK's.
|
||||
*/
|
||||
void ctap_overwrite_rk(int index,CTAP_residentKey * rk);
|
||||
|
||||
// For Solo hacker
|
||||
void boot_solo_bootloader();
|
||||
void boot_st_bootloader();
|
||||
|
||||
// HID wink command
|
||||
/** Called by HID layer to indicate that a wink behavior should be performed.
|
||||
* Should not block, and the wink behavior should occur in parallel to FIDO operations.
|
||||
*
|
||||
* *Optional*.
|
||||
*/
|
||||
void device_wink();
|
||||
|
||||
typedef enum {
|
||||
@ -92,19 +177,50 @@ typedef enum {
|
||||
DEVICE_FAST = 2,
|
||||
} DEVICE_CLOCK_RATE;
|
||||
|
||||
// Set the clock rate for the device.
|
||||
// Three modes are targetted for Solo.
|
||||
// 0: Lowest clock rate for NFC.
|
||||
// 1: fastest clock rate supported at a low power setting for NFC FIDO.
|
||||
// 2: fastest clock rate. Generally for USB interface.
|
||||
/**
|
||||
* Set the clock rate for the device. This gets called only when the device is running in NFC mode.
|
||||
* Before Register and authenticate operations, the clock rate will be set to (1), and otherwise back to (0).
|
||||
* @param param
|
||||
0: Lowest clock rate for NFC.
|
||||
1: fastest clock rate supported at a low power setting for NFC FIDO.
|
||||
2: fastest clock rate. Generally for USB interface.
|
||||
* *Optional*, by default nothing happens.
|
||||
*/
|
||||
void device_set_clock_rate(DEVICE_CLOCK_RATE param);
|
||||
|
||||
// Returns NFC_IS_NA, NFC_IS_ACTIVE, or NFC_IS_AVAILABLE
|
||||
#define NFC_IS_NA 0
|
||||
#define NFC_IS_ACTIVE 1
|
||||
#define NFC_IS_AVAILABLE 2
|
||||
|
||||
/** Returns NFC status of the device.
|
||||
* @return 0 - NFC is not available.
|
||||
* 1 - NFC is active, and is powering the chip for a transaction.
|
||||
* 2 - NFC is available, but not currently being used.
|
||||
*/
|
||||
int device_is_nfc();
|
||||
|
||||
void device_init_button();
|
||||
|
||||
/** Return pointer to attestation key.
|
||||
* @return pointer to attestation private key, raw encoded. For P256, this is 32 bytes.
|
||||
*/
|
||||
uint8_t * device_get_attestation_key();
|
||||
|
||||
/** Read the device's attestation certificate into buffer @dst.
|
||||
* @param dst the destination to write the certificate.
|
||||
*
|
||||
* The size of the certificate can be retrieved using `device_attestation_cert_der_get_size()`.
|
||||
*/
|
||||
void device_attestation_read_cert_der(uint8_t * dst);
|
||||
|
||||
/** Returns the size in bytes of attestation_cert_der.
|
||||
* @return number of bytes in attestation_cert_der, not including any C string null byte.
|
||||
*/
|
||||
uint16_t device_attestation_cert_der_get_size();
|
||||
|
||||
/** Read the device's 16 byte AAGUID into a buffer.
|
||||
* @param dst buffer to write 16 byte AAGUID into.
|
||||
* */
|
||||
void device_read_aaguid(uint8_t * dst);
|
||||
|
||||
#endif
|
||||
|
41
fido2/example_app.h
Normal file
41
fido2/example_app.h
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright 2019 SoloKeys Developers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
#ifndef SRC_APP_H_
|
||||
#define SRC_APP_H_
|
||||
#include <stdbool.h>
|
||||
|
||||
#define USING_DEV_BOARD
|
||||
|
||||
#define USING_PC
|
||||
|
||||
#define ENABLE_U2F
|
||||
#define ENABLE_U2F_EXTENSIONS
|
||||
//#define BRIDGE_TO_WALLET
|
||||
|
||||
void printing_init();
|
||||
|
||||
extern bool use_udp;
|
||||
|
||||
// 0xRRGGBB
|
||||
#define LED_INIT_VALUE 0x000800
|
||||
#define LED_WINK_VALUE 0x000008
|
||||
#define LED_MAX_SCALER 30
|
||||
#define LED_MIN_SCALER 1
|
||||
// # of ms between each change in LED
|
||||
#define HEARTBEAT_PERIOD 100
|
||||
// Each LED channel will be multiplied by a integer between LED_MAX_SCALER
|
||||
// and LED_MIN_SCALER to cause the slow pulse. E.g.
|
||||
// #define LED_INIT_VALUE 0x301000
|
||||
// #define LED_MAX_SCALER 30
|
||||
// #define LED_MIN_SCALER 1
|
||||
// #define HEARTBEAT_PERIOD 8
|
||||
// Will pulse from 0x301000 to 0x903000 to 0x301000 ...
|
||||
// Which will take ~8 * (30)*2 ms
|
||||
|
||||
|
||||
#endif /* SRC_APP_H_ */
|
@ -95,7 +95,7 @@ int8_t wallet_pin(uint8_t subcmd, uint8_t * pinAuth, uint8_t * arg1, uint8_t * a
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
printf1(TAG_WALLET,"Success. Pin = %s\n", STATE.pin_code);
|
||||
// printf1(TAG_WALLET,"Success. Pin = %s\n", STATE.pin_code);
|
||||
|
||||
break;
|
||||
case CP_cmdChangePin:
|
||||
|
@ -50,6 +50,8 @@ struct logtag tagtable[] = {
|
||||
{TAG_EXT,"[1;37mEXT[0m"},
|
||||
{TAG_NFC,"[1;38mNFC[0m"},
|
||||
{TAG_NFC_APDU, "NAPDU"},
|
||||
{TAG_CCID, "CCID"},
|
||||
{TAG_CM, "CRED_MGMT"},
|
||||
};
|
||||
|
||||
|
||||
|
@ -7,7 +7,10 @@
|
||||
#ifndef _LOG_H
|
||||
#define _LOG_H
|
||||
|
||||
#ifdef APP_CONFIG
|
||||
#include APP_CONFIG
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef DEBUG_LEVEL
|
||||
@ -44,12 +47,14 @@ typedef enum
|
||||
TAG_EXT = (1 << 18),
|
||||
TAG_NFC = (1 << 19),
|
||||
TAG_NFC_APDU = (1 << 20),
|
||||
TAG_CCID = (1 << 21),
|
||||
TAG_CM = (1 << 22),
|
||||
|
||||
TAG_NO_TAG = (1UL << 30),
|
||||
TAG_FILENO = (1UL << 31)
|
||||
} LOG_TAG;
|
||||
|
||||
#if DEBUG_LEVEL > 0
|
||||
#if defined(DEBUG_LEVEL) && DEBUG_LEVEL > 0
|
||||
|
||||
void set_logging_mask(uint32_t mask);
|
||||
#define printf1(tag,fmt, ...) LOG(tag & ~(TAG_FILENO), NULL, 0, fmt, ##__VA_ARGS__)
|
||||
|
@ -11,6 +11,9 @@
|
||||
|
||||
#define KEY_SPACE_BYTES 128
|
||||
#define MAX_KEYS (1)
|
||||
#define PIN_SALT_LEN (32)
|
||||
#define STATE_VERSION (1)
|
||||
|
||||
|
||||
#define BACKUP_MARKER 0x5A
|
||||
#define INITIALIZED_MARKER 0xA5
|
||||
@ -19,20 +22,40 @@
|
||||
#define ERR_KEY_SPACE_TAKEN (-2)
|
||||
#define ERR_KEY_SPACE_EMPTY (-2)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// Pin information
|
||||
uint8_t is_initialized;
|
||||
uint8_t is_pin_set;
|
||||
uint8_t pin_code[NEW_PIN_ENC_MIN_SIZE];
|
||||
int pin_code_length;
|
||||
int8_t remaining_tries;
|
||||
|
||||
uint16_t rk_stored;
|
||||
|
||||
uint16_t key_lens[MAX_KEYS];
|
||||
uint8_t key_space[KEY_SPACE_BYTES];
|
||||
} AuthenticatorState_0xFF;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// Pin information
|
||||
uint8_t is_initialized;
|
||||
uint8_t is_pin_set;
|
||||
uint8_t pin_code[NEW_PIN_ENC_MIN_SIZE];
|
||||
int pin_code_length;
|
||||
uint8_t PIN_CODE_HASH[32];
|
||||
uint8_t PIN_SALT[PIN_SALT_LEN];
|
||||
int _reserved;
|
||||
int8_t remaining_tries;
|
||||
|
||||
uint16_t rk_stored;
|
||||
|
||||
uint16_t key_lens[MAX_KEYS];
|
||||
uint8_t key_space[KEY_SPACE_BYTES];
|
||||
} AuthenticatorState;
|
||||
uint8_t data_version;
|
||||
} AuthenticatorState_0x01;
|
||||
|
||||
typedef AuthenticatorState_0x01 AuthenticatorState;
|
||||
|
||||
|
||||
typedef struct
|
||||
|
74
fido2/u2f.c
74
fido2/u2f.c
@ -26,6 +26,7 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c
|
||||
int8_t u2f_response_writeback(const uint8_t * buf, uint16_t len);
|
||||
void u2f_reset_response();
|
||||
|
||||
void make_auth_tag(uint8_t * rpIdHash, uint8_t * nonce, uint32_t count, uint8_t * tag);
|
||||
|
||||
static CTAP_RESPONSE * _u2f_resp = NULL;
|
||||
|
||||
@ -113,14 +114,14 @@ end:
|
||||
printf1(TAG_U2F,"u2f resp: "); dump_hex1(TAG_U2F, _u2f_resp->data, _u2f_resp->length);
|
||||
}
|
||||
|
||||
void u2f_request_nfc(uint8_t * req, int len, CTAP_RESPONSE * resp)
|
||||
void u2f_request_nfc(uint8_t * header, uint8_t * data, int datalen, CTAP_RESPONSE * resp)
|
||||
{
|
||||
if (len < 5 || !req)
|
||||
if (!header)
|
||||
return;
|
||||
|
||||
uint32_t alen = req[4];
|
||||
|
||||
u2f_request_ex((APDU_HEADER *)req, &req[5], alen, resp);
|
||||
device_disable_up(true); // disable presence test
|
||||
u2f_request_ex((APDU_HEADER *)header, data, datalen, resp);
|
||||
device_disable_up(false); // enable presence test
|
||||
}
|
||||
|
||||
void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp)
|
||||
@ -160,9 +161,9 @@ static void dump_signature_der(uint8_t * sig)
|
||||
len = ctap_encode_der_sig(sig, sigder);
|
||||
u2f_response_writeback(sigder, len);
|
||||
}
|
||||
static int8_t u2f_load_key(struct u2f_key_handle * kh, uint8_t * appid)
|
||||
static int8_t u2f_load_key(struct u2f_key_handle * kh, uint8_t khl, uint8_t * appid)
|
||||
{
|
||||
crypto_ecc256_load_key((uint8_t*)kh, U2F_KEY_HANDLE_SIZE, NULL, 0);
|
||||
crypto_ecc256_load_key((uint8_t*)kh, khl, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -187,21 +188,41 @@ int8_t u2f_new_keypair(struct u2f_key_handle * kh, uint8_t * appid, uint8_t * pu
|
||||
|
||||
|
||||
// Return 1 if authenticate, 0 if not.
|
||||
int8_t u2f_authenticate_credential(struct u2f_key_handle * kh, uint8_t * appid)
|
||||
int8_t u2f_authenticate_credential(struct u2f_key_handle * kh, uint8_t key_handle_len, uint8_t * appid)
|
||||
{
|
||||
printf1(TAG_U2F, "checked CRED SIZE %d. (FIDO2: %d)\n", key_handle_len, sizeof(CredentialId));
|
||||
uint8_t tag[U2F_KEY_HANDLE_TAG_SIZE];
|
||||
u2f_make_auth_tag(kh, appid, tag);
|
||||
if (memcmp(kh->tag, tag, U2F_KEY_HANDLE_TAG_SIZE) == 0)
|
||||
|
||||
if (key_handle_len == sizeof(CredentialId))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
printf1(TAG_U2F, "FIDO2 key handle detected.\n");
|
||||
CredentialId * cred = (CredentialId *) kh;
|
||||
// FIDO2 credential.
|
||||
|
||||
if (memcmp(cred->rpIdHash, appid, 32) != 0)
|
||||
{
|
||||
printf1(TAG_U2F, "APPID does not match rpIdHash.\n");
|
||||
return 0;
|
||||
}
|
||||
make_auth_tag(appid, (uint8_t*)&cred->entropy, cred->count, tag);
|
||||
|
||||
if (memcmp(cred->tag, tag, CREDENTIAL_TAG_SIZE) == 0){
|
||||
return 1;
|
||||
}
|
||||
|
||||
}else if (key_handle_len == U2F_KEY_HANDLE_SIZE)
|
||||
{
|
||||
printf1(TAG_U2F, "key handle + appid not authentic\n");
|
||||
printf1(TAG_U2F, "calc tag: \n"); dump_hex1(TAG_U2F,tag, U2F_KEY_HANDLE_TAG_SIZE);
|
||||
printf1(TAG_U2F, "inp tag: \n"); dump_hex1(TAG_U2F,kh->tag, U2F_KEY_HANDLE_TAG_SIZE);
|
||||
return 0;
|
||||
u2f_make_auth_tag(kh, appid, tag);
|
||||
if (memcmp(kh->tag, tag, U2F_KEY_HANDLE_TAG_SIZE) == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
printf1(TAG_U2F, "key handle + appid not authentic\n");
|
||||
printf1(TAG_U2F, "calc tag: \n"); dump_hex1(TAG_U2F,tag, U2F_KEY_HANDLE_TAG_SIZE);
|
||||
printf1(TAG_U2F, "inp tag: \n"); dump_hex1(TAG_U2F,kh->tag, U2F_KEY_HANDLE_TAG_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -216,7 +237,7 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c
|
||||
if (control == U2F_AUTHENTICATE_CHECK)
|
||||
{
|
||||
printf1(TAG_U2F, "CHECK-ONLY\r\n");
|
||||
if (u2f_authenticate_credential(&req->kh, req->app))
|
||||
if (u2f_authenticate_credential(&req->kh, req->khl, req->app))
|
||||
{
|
||||
return U2F_SW_CONDITIONS_NOT_SATISFIED;
|
||||
}
|
||||
@ -227,9 +248,8 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c
|
||||
}
|
||||
if (
|
||||
(control != U2F_AUTHENTICATE_SIGN && control != U2F_AUTHENTICATE_SIGN_NO_USER) ||
|
||||
req->khl != U2F_KEY_HANDLE_SIZE ||
|
||||
(!u2f_authenticate_credential(&req->kh, req->app)) || // Order of checks is important
|
||||
u2f_load_key(&req->kh, req->app) != 0
|
||||
(!u2f_authenticate_credential(&req->kh, req->khl, req->app)) || // Order of checks is important
|
||||
u2f_load_key(&req->kh, req->khl, req->app) != 0
|
||||
|
||||
)
|
||||
{
|
||||
@ -279,14 +299,19 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c
|
||||
static int16_t u2f_register(struct u2f_register_request * req)
|
||||
{
|
||||
uint8_t i[] = {0x0,U2F_EC_FMT_UNCOMPRESSED};
|
||||
|
||||
uint8_t cert[1024];
|
||||
struct u2f_key_handle key_handle;
|
||||
uint8_t pubkey[64];
|
||||
uint8_t hash[32];
|
||||
uint8_t * sig = (uint8_t*)req;
|
||||
|
||||
|
||||
const uint16_t attest_size = attestation_cert_der_size;
|
||||
const uint16_t attest_size = device_attestation_cert_der_get_size();
|
||||
|
||||
if (attest_size > sizeof(cert)){
|
||||
printf2(TAG_ERR,"Certificate is too large for buffer\r\n");
|
||||
return U2F_SW_INSUFFICIENT_MEMORY;
|
||||
}
|
||||
|
||||
if ( ! ctap_user_presence_test(750))
|
||||
{
|
||||
@ -321,7 +346,8 @@ static int16_t u2f_register(struct u2f_register_request * req)
|
||||
u2f_response_writeback(i,1);
|
||||
u2f_response_writeback((uint8_t*)&key_handle,U2F_KEY_HANDLE_SIZE);
|
||||
|
||||
u2f_response_writeback(attestation_cert_der,attest_size);
|
||||
device_attestation_read_cert_der(cert);
|
||||
u2f_response_writeback(cert,attest_size);
|
||||
|
||||
dump_signature_der(sig);
|
||||
|
||||
|
@ -101,9 +101,9 @@ void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp);
|
||||
// u2f_request send a U2F message to NFC protocol
|
||||
// @req data with iso7816 apdu message
|
||||
// @len data length
|
||||
void u2f_request_nfc(uint8_t * req, int len, CTAP_RESPONSE * resp);
|
||||
void u2f_request_nfc(uint8_t * header, uint8_t * data, int datalen, CTAP_RESPONSE * resp);
|
||||
|
||||
int8_t u2f_authenticate_credential(struct u2f_key_handle * kh, uint8_t * appid);
|
||||
int8_t u2f_authenticate_credential(struct u2f_key_handle * kh, uint8_t key_handle_len, uint8_t * appid);
|
||||
|
||||
int8_t u2f_response_writeback(const uint8_t * buf, uint16_t len);
|
||||
void u2f_reset_response();
|
||||
|
18
fido2/version.c
Normal file
18
fido2/version.c
Normal file
@ -0,0 +1,18 @@
|
||||
#include "version.h"
|
||||
#include "app.h"
|
||||
|
||||
const version_t firmware_version
|
||||
#ifdef SOLO
|
||||
__attribute__ ((section (".flag"))) __attribute__ ((__used__))
|
||||
#endif
|
||||
= {
|
||||
.major = SOLO_VERSION_MAJ,
|
||||
.minor = SOLO_VERSION_MIN,
|
||||
.patch = SOLO_VERSION_PATCH,
|
||||
.reserved = 0
|
||||
};
|
||||
|
||||
|
||||
// from tinycbor, for a quick static_assert
|
||||
#include <compilersupport_p.h>
|
||||
cbor_static_assert(sizeof(version_t) == 4);
|
@ -17,5 +17,23 @@
|
||||
#define SOLO_VERSION __STR(SOLO_VERSION_MAJ) "." __STR(SOLO_VERSION_MIN) "." __STR(SOLO_VERSION_PATCH)
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct {
|
||||
union{
|
||||
uint32_t raw;
|
||||
struct {
|
||||
uint8_t major;
|
||||
uint8_t minor;
|
||||
uint8_t patch;
|
||||
uint8_t reserved;
|
||||
};
|
||||
};
|
||||
} version_t;
|
||||
|
||||
bool is_newer(const version_t* const newer, const version_t* const older);
|
||||
const version_t firmware_version ;
|
||||
|
||||
|
||||
#endif
|
||||
|
9
fido2/version.mk
Normal file
9
fido2/version.mk
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
SOLO_VERSION_FULL?=$(shell git describe)
|
||||
SOLO_VERSION:=$(shell python -c 'print("$(SOLO_VERSION_FULL)".split("-")[0])')
|
||||
SOLO_VERSION_MAJ:=$(shell python -c 'print("$(SOLO_VERSION)".split(".")[0])')
|
||||
SOLO_VERSION_MIN:=$(shell python -c 'print("$(SOLO_VERSION)".split(".")[1])')
|
||||
SOLO_VERSION_PAT:=$(shell python -c 'print("$(SOLO_VERSION)".split(".")[2])')
|
||||
|
||||
SOLO_VERSION_FLAGS := -DSOLO_VERSION_MAJ=$(SOLO_VERSION_MAJ) -DSOLO_VERSION_MIN=$(SOLO_VERSION_MIN) \
|
||||
-DSOLO_VERSION_PATCH=$(SOLO_VERSION_PAT) -DSOLO_VERSION=\"$(SOLO_VERSION_FULL)\"
|
@ -1,14 +1,10 @@
|
||||
#!/bin/bash -xe
|
||||
version=$1
|
||||
|
||||
version=${1:-master}
|
||||
|
||||
export PREFIX=/opt/gcc-arm-none-eabi-8-2018-q4-major/bin/
|
||||
export PREFIX=/opt/gcc-arm-none-eabi-8-2019-q3-update/bin/
|
||||
|
||||
cd /solo/targets/stm32l432
|
||||
git fetch --tags
|
||||
git checkout ${version}
|
||||
git submodule update --init --recursive
|
||||
version=$(git describe)
|
||||
ls
|
||||
|
||||
make cbor
|
||||
|
||||
@ -16,13 +12,12 @@ out_dir="/builds"
|
||||
|
||||
function build() {
|
||||
part=${1}
|
||||
variant=${2}
|
||||
output=${3:-${part}}
|
||||
what="${part}-${variant}"
|
||||
output=${2}
|
||||
what="${part}"
|
||||
|
||||
make full-clean
|
||||
|
||||
make ${what}
|
||||
make ${what} VERSION_FULL=${version}
|
||||
|
||||
out_hex="${what}-${version}.hex"
|
||||
out_sha2="${what}-${version}.sha2"
|
||||
@ -32,20 +27,27 @@ function build() {
|
||||
cp ${out_hex} ${out_sha2} ${out_dir}
|
||||
}
|
||||
|
||||
build bootloader nonverifying
|
||||
build bootloader verifying
|
||||
build firmware hacker solo
|
||||
build firmware hacker-debug-1 solo
|
||||
build firmware hacker-debug-2 solo
|
||||
build firmware secure solo
|
||||
build bootloader-nonverifying bootloader
|
||||
build bootloader-verifying bootloader
|
||||
build firmware solo
|
||||
build firmware-debug-1 solo
|
||||
build firmware-debug-2 solo
|
||||
build firmware solo
|
||||
|
||||
pip install -U pip
|
||||
pip install -U solo-python
|
||||
cd ${out_dir}
|
||||
|
||||
bundle="bundle-hacker-${version}"
|
||||
/opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-hacker-${version}.hex ${bundle}.hex
|
||||
/opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-${version}.hex ${bundle}.hex
|
||||
sha256sum ${bundle}.hex > ${bundle}.sha2
|
||||
|
||||
bundle="bundle-hacker-debug-1-${version}"
|
||||
/opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-hacker-debug-1-${version}.hex ${bundle}.hex
|
||||
/opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-debug-1-${version}.hex ${bundle}.hex
|
||||
sha256sum ${bundle}.hex > ${bundle}.sha2
|
||||
|
||||
bundle="bundle-hacker-debug-2-${version}"
|
||||
/opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-hacker-debug-2-${version}.hex ${bundle}.hex
|
||||
/opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-debug-2-${version}.hex ${bundle}.hex
|
||||
sha256sum ${bundle}.hex > ${bundle}.sha2
|
||||
|
||||
bundle="bundle-secure-non-solokeys-${version}"
|
||||
/opt/conda/bin/solo mergehex --lock bootloader-verifying-${version}.hex firmware-${version}.hex ${bundle}.hex
|
||||
sha256sum ${bundle}.hex > ${bundle}.sha2
|
||||
|
@ -15,11 +15,13 @@
|
||||
"authenticationAlgorithm": 1,
|
||||
"publicKeyAlgAndEncoding": 260,
|
||||
"attestationTypes": [
|
||||
15879,
|
||||
15880
|
||||
15879
|
||||
],
|
||||
"userVerificationDetails": [
|
||||
[
|
||||
{
|
||||
"userVerification": 1
|
||||
},
|
||||
{
|
||||
"userVerification": 4
|
||||
}
|
||||
|
41
metadata/SoloTap-FIDO2-CTAP2-Authenticator.json
Normal file
41
metadata/SoloTap-FIDO2-CTAP2-Authenticator.json
Normal file
File diff suppressed because one or more lines are too long
41
metadata/Somu-FIDO2-CTAP2-Authenticator.json
Normal file
41
metadata/Somu-FIDO2-CTAP2-Authenticator.json
Normal file
File diff suppressed because one or more lines are too long
@ -11,7 +11,14 @@ nav:
|
||||
- FIDO2 Implementation: solo/fido2-impl.md
|
||||
- Metadata Statements: solo/metadata-statements.md
|
||||
- Build instructions: solo/building.md
|
||||
- Programming instructions: solo/programming.md
|
||||
- Bootloader mode: solo/bootloader-mode.md
|
||||
- Customization: solo/customization.md
|
||||
- Solo Extras: solo/solo-extras.md
|
||||
- Application Ideas: solo/application-ideas.md
|
||||
- Running on Nucleo32 board: solo/nucleo32-board.md
|
||||
- Signed update process: solo/signed-updates.md
|
||||
- Usage and Porting guide: solo/porting.md
|
||||
- Code documentation: solo/code-overview.md
|
||||
- Contributing Code: solo/contributing.md
|
||||
- Contributing Docs: solo/documenting.md
|
||||
|
204
pc/device.c
204
pc/device.c
@ -11,7 +11,6 @@
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
@ -25,7 +24,7 @@
|
||||
|
||||
#define RK_NUM 50
|
||||
|
||||
bool use_udp = true;
|
||||
static bool use_udp = true;
|
||||
|
||||
struct ResidentKeyStore {
|
||||
CTAP_residentKey rks[RK_NUM];
|
||||
@ -33,17 +32,6 @@ struct ResidentKeyStore {
|
||||
|
||||
void authenticator_initialize();
|
||||
|
||||
uint32_t __device_status = 0;
|
||||
void device_set_status(uint32_t status)
|
||||
{
|
||||
if (status != CTAPHID_STATUS_IDLE && __device_status != status)
|
||||
{
|
||||
ctaphid_update_status(status);
|
||||
}
|
||||
__device_status = status;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int udp_server()
|
||||
{
|
||||
@ -103,6 +91,7 @@ int udp_recv(int fd, uint8_t * buf, int size)
|
||||
perror( "recvfrom failed" );
|
||||
exit(1);
|
||||
}
|
||||
printf1(TAG_DUMP, ">>"); dump_hex1(TAG_DUMP, buf, length);
|
||||
return length;
|
||||
}
|
||||
|
||||
@ -119,6 +108,8 @@ void udp_send(int fd, uint8_t * buf, int size)
|
||||
perror( "sendto failed" );
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf1(TAG_DUMP, "<<"); dump_hex1(TAG_DUMP, buf, size);
|
||||
}
|
||||
|
||||
|
||||
@ -184,7 +175,6 @@ int usbhid_recv(uint8_t * msg)
|
||||
return l;
|
||||
}
|
||||
|
||||
// Send 64 byte USB HID message
|
||||
void usbhid_send(uint8_t * msg)
|
||||
{
|
||||
if (use_udp)
|
||||
@ -201,6 +191,8 @@ void usbhid_send(uint8_t * msg)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void usbhid_close()
|
||||
{
|
||||
close(fd);
|
||||
@ -264,14 +256,6 @@ void device_init(int argc, char *argv[])
|
||||
}
|
||||
|
||||
|
||||
void main_loop_delay()
|
||||
{
|
||||
struct timespec ts;
|
||||
ts.tv_sec = 0;
|
||||
ts.tv_nsec = 1000*1000*100;
|
||||
nanosleep(&ts,NULL);
|
||||
}
|
||||
|
||||
void delay(uint32_t ms)
|
||||
{
|
||||
struct timespec ts;
|
||||
@ -281,45 +265,6 @@ void delay(uint32_t ms)
|
||||
}
|
||||
|
||||
|
||||
void heartbeat()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ctaphid_write_block(uint8_t * data)
|
||||
{
|
||||
/*printf("<< "); dump_hex(data, 64);*/
|
||||
usbhid_send(data);
|
||||
}
|
||||
|
||||
|
||||
int ctap_user_presence_test(uint32_t d)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ctap_user_verification(uint8_t arg)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
uint32_t ctap_atomic_count(int sel)
|
||||
{
|
||||
static uint32_t counter1 = 25;
|
||||
/*return 713;*/
|
||||
if (sel == 0)
|
||||
{
|
||||
printf1(TAG_RED,"counter1: %d\n", counter1);
|
||||
return counter1++;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf2(TAG_ERR,"counter2 not imple\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int ctap_generate_rng(uint8_t * dst, size_t num)
|
||||
{
|
||||
int ret;
|
||||
@ -341,10 +286,9 @@ int ctap_generate_rng(uint8_t * dst, size_t num)
|
||||
|
||||
|
||||
const char * state_file = "authenticator_state.bin";
|
||||
const char * backup_file = "authenticator_state2.bin";
|
||||
const char * rk_file = "resident_keys.bin";
|
||||
|
||||
void authenticator_read_state(AuthenticatorState * state)
|
||||
int authenticator_read_state(AuthenticatorState * state)
|
||||
{
|
||||
FILE * f;
|
||||
int ret;
|
||||
@ -363,104 +307,35 @@ void authenticator_read_state(AuthenticatorState * state)
|
||||
perror("fwrite");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (state->is_initialized == INITIALIZED_MARKER)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
void authenticator_read_backup_state(AuthenticatorState * state )
|
||||
|
||||
void authenticator_write_state(AuthenticatorState * state)
|
||||
{
|
||||
FILE * f;
|
||||
int ret;
|
||||
|
||||
f = fopen(backup_file, "rb");
|
||||
f = fopen(state_file, "wb+");
|
||||
if (f== NULL)
|
||||
{
|
||||
perror("fopen");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ret = fread(state, 1, sizeof(AuthenticatorState), f);
|
||||
ret = fwrite(state, 1, sizeof(AuthenticatorState), f);
|
||||
fclose(f);
|
||||
if(ret != sizeof(AuthenticatorState))
|
||||
{
|
||||
perror("fwrite");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void authenticator_write_state(AuthenticatorState * state, int backup)
|
||||
{
|
||||
FILE * f;
|
||||
int ret;
|
||||
|
||||
if (! backup)
|
||||
{
|
||||
f = fopen(state_file, "wb+");
|
||||
if (f== NULL)
|
||||
{
|
||||
perror("fopen");
|
||||
exit(1);
|
||||
}
|
||||
ret = fwrite(state, 1, sizeof(AuthenticatorState), f);
|
||||
fclose(f);
|
||||
if (ret != sizeof(AuthenticatorState))
|
||||
{
|
||||
perror("fwrite");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
f = fopen(backup_file, "wb+");
|
||||
if (f== NULL)
|
||||
{
|
||||
perror("fopen");
|
||||
exit(1);
|
||||
}
|
||||
ret = fwrite(state, 1, sizeof(AuthenticatorState), f);
|
||||
fclose(f);
|
||||
if (ret != sizeof(AuthenticatorState))
|
||||
{
|
||||
perror("fwrite");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return 1 yes backup is init'd, else 0
|
||||
int authenticator_is_backup_initialized()
|
||||
{
|
||||
uint8_t header[16];
|
||||
AuthenticatorState * state = (AuthenticatorState*) header;
|
||||
FILE * f;
|
||||
int ret;
|
||||
|
||||
printf("state file exists\n");
|
||||
f = fopen(backup_file, "rb");
|
||||
if (f== NULL)
|
||||
{
|
||||
printf("Warning, backup file doesn't exist\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = fread(header, 1, sizeof(header), f);
|
||||
fclose(f);
|
||||
if(ret != sizeof(header))
|
||||
if (ret != sizeof(AuthenticatorState))
|
||||
{
|
||||
perror("fwrite");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return state->is_initialized == INITIALIZED_MARKER;
|
||||
|
||||
}
|
||||
|
||||
// Return 1 yes backup is init'd, else 0
|
||||
/*int authenticator_is_initialized()*/
|
||||
/*{*/
|
||||
|
||||
|
||||
/*}*/
|
||||
|
||||
static void sync_rk()
|
||||
{
|
||||
@ -540,44 +415,18 @@ void authenticator_initialize()
|
||||
exit(1);
|
||||
}
|
||||
|
||||
f = fopen(backup_file, "wb+");
|
||||
if (f== NULL)
|
||||
{
|
||||
perror("fopen");
|
||||
exit(1);
|
||||
}
|
||||
mem = malloc(sizeof(AuthenticatorState));
|
||||
memset(mem,0xff,sizeof(AuthenticatorState));
|
||||
ret = fwrite(mem, 1, sizeof(AuthenticatorState), f);
|
||||
free(mem);
|
||||
fclose(f);
|
||||
if (ret != sizeof(AuthenticatorState))
|
||||
{
|
||||
perror("fwrite");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// resident_keys
|
||||
memset(&RK_STORE,0xff,sizeof(RK_STORE));
|
||||
sync_rk();
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void device_manage()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ctap_reset_rk()
|
||||
{
|
||||
memset(&RK_STORE,0xff,sizeof(RK_STORE));
|
||||
sync_rk();
|
||||
|
||||
}
|
||||
|
||||
uint32_t ctap_rk_size()
|
||||
@ -600,6 +449,12 @@ void ctap_store_rk(int index, CTAP_residentKey * rk)
|
||||
|
||||
}
|
||||
|
||||
void ctap_delete_rk(int index)
|
||||
{
|
||||
CTAP_residentKey rk;
|
||||
memset(&rk, 0xff, sizeof(CTAP_residentKey));
|
||||
memmove(RK_STORE.rks + index, &rk, sizeof(CTAP_residentKey));
|
||||
}
|
||||
|
||||
void ctap_load_rk(int index, CTAP_residentKey * rk)
|
||||
{
|
||||
@ -619,12 +474,9 @@ void ctap_overwrite_rk(int index, CTAP_residentKey * rk)
|
||||
}
|
||||
}
|
||||
|
||||
void device_wink()
|
||||
{
|
||||
printf("*WINK*\n");
|
||||
}
|
||||
|
||||
int device_is_nfc()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "cbor.h"
|
||||
#include "device.h"
|
||||
@ -17,9 +18,12 @@
|
||||
#include "util.h"
|
||||
#include "log.h"
|
||||
#include "ctap.h"
|
||||
#include APP_CONFIG
|
||||
#include "app.h"
|
||||
|
||||
|
||||
void device_init(int argc, char *argv[]);
|
||||
int usbhid_recv(uint8_t * msg);
|
||||
|
||||
#if !defined(TEST)
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
@ -29,23 +33,25 @@ int main(int argc, char *argv[])
|
||||
|
||||
set_logging_mask(
|
||||
/*0*/
|
||||
//TAG_GEN|
|
||||
// TAG_GEN|
|
||||
// TAG_MC |
|
||||
// TAG_GA |
|
||||
TAG_WALLET |
|
||||
TAG_STOR |
|
||||
//TAG_NFC_APDU |
|
||||
TAG_NFC |
|
||||
//TAG_CP |
|
||||
// TAG_CP |
|
||||
// TAG_CTAP|
|
||||
//TAG_HID|
|
||||
// TAG_HID|
|
||||
TAG_U2F|
|
||||
//TAG_PARSE |
|
||||
// TAG_PARSE |
|
||||
//TAG_TIME|
|
||||
// TAG_DUMP|
|
||||
// TAG_DUMP2|
|
||||
TAG_GREEN|
|
||||
TAG_RED|
|
||||
TAG_EXT|
|
||||
TAG_CCID|
|
||||
TAG_ERR
|
||||
);
|
||||
|
||||
@ -56,13 +62,6 @@ int main(int argc, char *argv[])
|
||||
|
||||
while(1)
|
||||
{
|
||||
if (millis() - t1 > HEARTBEAT_PERIOD)
|
||||
{
|
||||
heartbeat();
|
||||
t1 = millis();
|
||||
}
|
||||
|
||||
device_manage();
|
||||
|
||||
if (usbhid_recv(hidmsg) > 0)
|
||||
{
|
||||
@ -72,14 +71,16 @@ int main(int argc, char *argv[])
|
||||
else
|
||||
{
|
||||
}
|
||||
|
||||
ctaphid_check_timeouts();
|
||||
|
||||
struct timespec ts;
|
||||
ts.tv_sec = 0;
|
||||
ts.tv_nsec = 1000*1000*10;
|
||||
nanosleep(&ts,NULL);
|
||||
}
|
||||
|
||||
// Should never get here
|
||||
usbhid_close();
|
||||
printf1(TAG_GREEN, "done\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
@ -2,8 +2,9 @@ ifndef DEBUG
|
||||
DEBUG=0
|
||||
endif
|
||||
|
||||
APPMAKE=build/application.mk
|
||||
BOOTMAKE=build/bootloader.mk
|
||||
VERSION_FULL?=$(shell git describe)
|
||||
APPMAKE=build/application.mk VERSION_FULL=${VERSION_FULL}
|
||||
BOOTMAKE=build/bootloader.mk VERSION_FULL=${VERSION_FULL}
|
||||
|
||||
merge_hex=solo mergehex
|
||||
|
||||
@ -12,17 +13,14 @@ merge_hex=solo mergehex
|
||||
|
||||
# The following are the main targets for reproducible builds.
|
||||
# TODO: better explanation
|
||||
firmware-hacker:
|
||||
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=0 EXTRA_DEFINES='-DSOLO_HACKER -DFLASH_ROP=0'
|
||||
firmware:
|
||||
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=0
|
||||
|
||||
firmware-hacker-debug-1:
|
||||
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=1 EXTRA_DEFINES='-DSOLO_HACKER -DFLASH_ROP=0'
|
||||
firmware-debug-1:
|
||||
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=1
|
||||
|
||||
firmware-hacker-debug-2:
|
||||
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=2 EXTRA_DEFINES='-DSOLO_HACKER -DFLASH_ROP=0'
|
||||
|
||||
firmware-secure:
|
||||
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=0 EXTRA_DEFINES='-DUSE_SOLOKEYS_CERT -DFLASH_ROP=2'
|
||||
firmware-debug-2:
|
||||
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=2
|
||||
|
||||
bootloader-nonverifying:
|
||||
$(MAKE) -f $(BOOTMAKE) -j8 bootloader.hex PREFIX=$(PREFIX) EXTRA_DEFINES='-DSOLO_HACKER' DEBUG=0
|
||||
@ -59,7 +57,6 @@ boot-no-sig:
|
||||
|
||||
build-release-locked: cbor clean2 boot-sig-checking clean all-locked
|
||||
$(VENV) $(merge_hex) solo.hex bootloader.hex all.hex
|
||||
rm -f solo.hex bootloader.hex # don't program solo.hex ...
|
||||
|
||||
build-release: cbor clean2 boot-sig-checking clean all
|
||||
$(VENV) $(merge_hex) solo.hex bootloader.hex all.hex
|
||||
@ -88,13 +85,12 @@ flash_dfu: solo.hex bootloader.hex
|
||||
# STM32_Programmer_CLI -c port=usb1 -halt -e all --readunprotect
|
||||
STM32_Programmer_CLI -c port=usb1 -halt -rdu -d all.hex
|
||||
|
||||
flashboot: solo.hex bootloader.hex
|
||||
$(VENV) $(merge_hex) solo.hex bootloader.hex all.hex
|
||||
flashboot: bootloader.hex
|
||||
STM32_Programmer_CLI -c port=SWD -halt -e all --readunprotect
|
||||
STM32_Programmer_CLI -c port=SWD -halt -d bootloader.hex -rst
|
||||
|
||||
flash-firmware:
|
||||
arm-none-eabi-size -A solo.elf
|
||||
$(SZ) -A solo.elf
|
||||
solo program aux enter-bootloader
|
||||
solo program bootloader solo.hex
|
||||
|
||||
|
@ -19,6 +19,12 @@
|
||||
#include "ctap_errors.h"
|
||||
#include "log.h"
|
||||
|
||||
volatile version_t current_firmware_version __attribute__ ((section (".flag2"))) __attribute__ ((__used__)) = {
|
||||
.major = SOLO_VERSION_MAJ,
|
||||
.minor = SOLO_VERSION_MIN,
|
||||
.patch = SOLO_VERSION_PATCH,
|
||||
.reserved = 0
|
||||
};
|
||||
|
||||
extern uint8_t REBOOT_FLAG;
|
||||
|
||||
@ -44,20 +50,21 @@ typedef struct {
|
||||
uint8_t payload[255 - 10];
|
||||
} __attribute__((packed)) BootloaderReq;
|
||||
|
||||
uint8_t * last_written_app_address = 0;
|
||||
|
||||
/**
|
||||
* Erase all application pages. **APPLICATION_END_PAGE excluded**.
|
||||
*/
|
||||
static void erase_application()
|
||||
{
|
||||
int page;
|
||||
last_written_app_address = (uint8_t*) 0;
|
||||
for(page = APPLICATION_START_PAGE; page < APPLICATION_END_PAGE; page++)
|
||||
{
|
||||
flash_erase_page(page);
|
||||
}
|
||||
}
|
||||
|
||||
#define LAST_ADDR (APPLICATION_END_ADDR-2048 + 8)
|
||||
#define LAST_PAGE (APPLICATION_END_PAGE-1)
|
||||
static void disable_bootloader()
|
||||
{
|
||||
// Clear last 4 bytes of the last application page-1, which is 108th
|
||||
@ -103,6 +110,41 @@ int is_bootloader_disabled()
|
||||
return *auth == 0;
|
||||
}
|
||||
|
||||
#include "version.h"
|
||||
bool is_firmware_version_newer_or_equal()
|
||||
{
|
||||
|
||||
if (last_written_app_address == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
printf1(TAG_BOOT,"Current firmware version: %u.%u.%u.%u (%02x.%02x.%02x.%02x)\r\n",
|
||||
current_firmware_version.major, current_firmware_version.minor, current_firmware_version.patch, current_firmware_version.reserved,
|
||||
current_firmware_version.major, current_firmware_version.minor, current_firmware_version.patch, current_firmware_version.reserved
|
||||
);
|
||||
volatile version_t * new_version = ((volatile version_t *) (last_written_app_address-8+4));
|
||||
printf1(TAG_BOOT,"Uploaded firmware version: %u.%u.%u.%u (%02x.%02x.%02x.%02x)\r\n",
|
||||
new_version->major, new_version->minor, new_version->patch, new_version->reserved,
|
||||
new_version->major, new_version->minor, new_version->patch, new_version->reserved
|
||||
);
|
||||
|
||||
const bool allowed = is_newer((const version_t *)new_version, (const version_t *)¤t_firmware_version) || current_firmware_version.raw == 0xFFFFFFFF;
|
||||
if (allowed){
|
||||
printf1(TAG_BOOT, "Update allowed, setting new firmware version as current.\r\n");
|
||||
// current_firmware_version.raw = new_version.raw;
|
||||
uint8_t page[PAGE_SIZE];
|
||||
memmove(page, (uint8_t*)BOOT_VERSION_ADDR, PAGE_SIZE);
|
||||
memmove(page, (version_t *)new_version, 4);
|
||||
printf1(TAG_BOOT, "Writing\r\n");
|
||||
flash_erase_page(BOOT_VERSION_PAGE);
|
||||
flash_write(BOOT_VERSION_ADDR, page, PAGE_SIZE);
|
||||
printf1(TAG_BOOT, "Finish\r\n");
|
||||
} else {
|
||||
printf1(TAG_BOOT, "Firmware older - update not allowed.\r\n");
|
||||
}
|
||||
return allowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute bootloader commands
|
||||
* @param klen key length - length of the bootloader request
|
||||
@ -125,10 +167,7 @@ int bootloader_bridge(int klen, uint8_t * keyh)
|
||||
return CTAP1_ERR_INVALID_LENGTH;
|
||||
}
|
||||
#ifndef SOLO_HACKER
|
||||
uint8_t * pubkey = (uint8_t*)"\xd2\xa4\x2f\x8f\xb2\x31\x1c\xc1\xf7\x0c\x7e\x64\x32\xfb\xbb\xb4\xa3\xdd\x32\x20"
|
||||
"\x0f\x1b\x88\x9c\xda\x62\xc2\x83\x25\x93\xdd\xb8\x75\x9d\xf9\x86\xee\x03\x6c\xce"
|
||||
"\x34\x47\x71\x36\xb3\xb2\xad\x6d\x12\xb7\xbe\x49\x3e\x20\xa4\x61\xac\xc7\x71\xc7"
|
||||
"\x1f\xa8\x14\xf2";
|
||||
extern uint8_t *pubkey_boot;
|
||||
|
||||
const struct uECC_Curve_t * curve = NULL;
|
||||
#endif
|
||||
@ -137,6 +176,7 @@ int bootloader_bridge(int klen, uint8_t * keyh)
|
||||
uint32_t addr = ((*((uint32_t*)req->addr)) & 0xffffff) | 0x8000000;
|
||||
|
||||
uint32_t * ptr = (uint32_t *)addr;
|
||||
uint32_t current_address;
|
||||
|
||||
switch(req->op){
|
||||
case BootWrite:
|
||||
@ -163,14 +203,20 @@ int bootloader_bridge(int klen, uint8_t * keyh)
|
||||
printf2(TAG_ERR, "Error, boot check bypassed\n");
|
||||
exit(1);
|
||||
}
|
||||
current_address = addr + len;
|
||||
if (current_address < (uint32_t) last_written_app_address) {
|
||||
printf2(TAG_ERR, "Error, only ascending writes allowed.\n");
|
||||
has_erased = 0;
|
||||
return CTAP2_ERR_NOT_ALLOWED;
|
||||
}
|
||||
last_written_app_address = (uint8_t*) current_address;
|
||||
|
||||
// Do the actual write
|
||||
flash_write((uint32_t)ptr,req->payload, len);
|
||||
|
||||
|
||||
break;
|
||||
case BootDone:
|
||||
// Writing to flash finished. Request code validation.
|
||||
printf1(TAG_BOOT, "BootDone: ");
|
||||
printf1(TAG_BOOT, "BootDone: \r\n");
|
||||
#ifndef SOLO_HACKER
|
||||
if (len != 64)
|
||||
{
|
||||
@ -185,17 +231,23 @@ int bootloader_bridge(int klen, uint8_t * keyh)
|
||||
crypto_sha256_final(hash);
|
||||
curve = uECC_secp256r1();
|
||||
// Verify incoming signature made over the SHA256 hash
|
||||
if (! uECC_verify(pubkey,
|
||||
hash,
|
||||
32,
|
||||
req->payload,
|
||||
curve))
|
||||
if (
|
||||
!uECC_verify(pubkey_boot, hash, 32, req->payload, curve)
|
||||
)
|
||||
{
|
||||
printf1(TAG_BOOT, "Signature invalid\r\n");
|
||||
return CTAP2_ERR_OPERATION_DENIED;
|
||||
}
|
||||
if (!is_firmware_version_newer_or_equal()){
|
||||
printf1(TAG_BOOT, "Firmware older - update not allowed.\r\n");
|
||||
printf1(TAG_BOOT, "Rebooting...\r\n");
|
||||
REBOOT_FLAG = 1;
|
||||
return CTAP2_ERR_OPERATION_DENIED;
|
||||
}
|
||||
#endif
|
||||
// Set the application validated, and mark for reboot.
|
||||
authorize_application();
|
||||
|
||||
REBOOT_FLAG = 1;
|
||||
break;
|
||||
case BootCheck:
|
||||
@ -218,6 +270,7 @@ int bootloader_bridge(int klen, uint8_t * keyh)
|
||||
break;
|
||||
case BootReboot:
|
||||
printf1(TAG_BOOT, "BootReboot.\r\n");
|
||||
printf1(TAG_BOOT, "Application authorized: %d.\r\n", is_authorized_to_boot());
|
||||
REBOOT_FLAG = 1;
|
||||
break;
|
||||
case BootDisable:
|
||||
@ -277,3 +330,10 @@ void bootloader_heartbeat()
|
||||
|
||||
led_rgb(((val * g)<<8) | ((val*r) << 16) | (val*b));
|
||||
}
|
||||
|
||||
uint32_t ctap_atomic_count(uint32_t amount)
|
||||
{
|
||||
static uint32_t count = 1000;
|
||||
count += (amount + 1);
|
||||
return count;
|
||||
}
|
@ -9,7 +9,7 @@
|
||||
#define _APP_H_
|
||||
#include <stdint.h>
|
||||
#include "version.h"
|
||||
|
||||
#include "solo.h"
|
||||
#define DEBUG_UART USART1
|
||||
|
||||
#ifndef DEBUG_LEVEL
|
||||
@ -21,6 +21,7 @@
|
||||
#define BOOT_TO_DFU 0
|
||||
|
||||
|
||||
#define SOLO 1
|
||||
#define IS_BOOTLOADER 1
|
||||
|
||||
#define ENABLE_U2F_EXTENSIONS
|
||||
@ -64,4 +65,9 @@ int is_authorized_to_boot();
|
||||
int is_bootloader_disabled();
|
||||
void bootloader_heartbeat();
|
||||
|
||||
// Return 1 if Solo is secure/locked.
|
||||
int solo_is_locked();
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -46,9 +46,7 @@ int main()
|
||||
{
|
||||
uint8_t hidmsg[64];
|
||||
uint32_t t1 = 0;
|
||||
#ifdef SOLO_HACKER
|
||||
uint32_t stboot_time = 0;
|
||||
#endif
|
||||
uint32_t boot = 1;
|
||||
|
||||
set_logging_mask(
|
||||
@ -98,7 +96,6 @@ int main()
|
||||
}
|
||||
|
||||
|
||||
#ifdef SOLO_HACKER
|
||||
if (!is_bootloader_disabled())
|
||||
{
|
||||
stboot_time = millis();
|
||||
@ -108,7 +105,6 @@ int main()
|
||||
goto start_bootloader;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (is_authorized_to_boot() && (boot || is_bootloader_disabled()))
|
||||
{
|
||||
@ -119,9 +115,8 @@ int main()
|
||||
|
||||
printf1(TAG_RED,"Not authorized to boot (%08x == %08lx)\r\n", AUTH_WORD_ADDR, *(uint32_t*)AUTH_WORD_ADDR);
|
||||
}
|
||||
#ifdef SOLO_HACKER
|
||||
start_bootloader:
|
||||
#endif
|
||||
|
||||
SystemClock_Config();
|
||||
init_gpio();
|
||||
init_millisecond_timer(0);
|
||||
@ -138,6 +133,14 @@ int main()
|
||||
|
||||
printf1(TAG_GEN,"recv'ing hid msg \n");
|
||||
|
||||
extern volatile version_t current_firmware_version;
|
||||
printf1(TAG_BOOT,"Current firmware version address: %p\r\n", ¤t_firmware_version);
|
||||
printf1(TAG_BOOT,"Current firmware version: %d.%d.%d.%d (%02x.%02x.%02x.%02x)\r\n",
|
||||
current_firmware_version.major, current_firmware_version.minor, current_firmware_version.patch, current_firmware_version.reserved,
|
||||
current_firmware_version.major, current_firmware_version.minor, current_firmware_version.patch, current_firmware_version.reserved
|
||||
);
|
||||
dump_hex1(TAG_BOOT, (uint8_t*)(¤t_firmware_version) - 16, 32);
|
||||
|
||||
|
||||
while(1)
|
||||
{
|
||||
|
6
targets/stm32l432/bootloader/pubkey_bootloader.c
Normal file
6
targets/stm32l432/bootloader/pubkey_bootloader.c
Normal file
@ -0,0 +1,6 @@
|
||||
#include "stdint.h"
|
||||
|
||||
uint8_t * pubkey_boot = (uint8_t*)"\xd2\xa4\x2f\x8f\xb2\x31\x1c\xc1\xf7\x0c\x7e\x64\x32\xfb\xbb\xb4\xa3\xdd\x32\x20"
|
||||
"\x0f\x1b\x88\x9c\xda\x62\xc2\x83\x25\x93\xdd\xb8\x75\x9d\xf9\x86\xee\x03\x6c\xce"
|
||||
"\x34\x47\x71\x36\xb3\xb2\xad\x6d\x12\xb7\xbe\x49\x3e\x20\xa4\x61\xac\xc7\x71\xc7"
|
||||
"\x1f\xa8\x14\xf2";
|
8
targets/stm32l432/bootloader/version_check.c
Normal file
8
targets/stm32l432/bootloader/version_check.c
Normal file
@ -0,0 +1,8 @@
|
||||
#include "version.h"
|
||||
|
||||
// FIXME test version check function
|
||||
bool is_newer(const version_t* const newer, const version_t* const older){
|
||||
return (newer->major > older->major) ||
|
||||
(newer->major == older->major && newer->minor > older->minor) ||
|
||||
(newer->major == older->major && newer->minor == older->minor && newer->patch >= older->patch);
|
||||
}
|
@ -2,14 +2,16 @@ include build/common.mk
|
||||
|
||||
# ST related
|
||||
SRC = src/main.c src/init.c src/redirect.c src/flash.c src/rng.c src/led.c src/device.c
|
||||
SRC += src/fifo.c src/crypto.c src/attestation.c src/nfc.c src/ams.c src/sense.c
|
||||
SRC += src/fifo.c src/attestation.c src/nfc.c src/ams.c src/sense.c
|
||||
SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c
|
||||
SRC += $(DRIVER_LIBS) $(USB_LIB)
|
||||
|
||||
# FIDO2 lib
|
||||
SRC += ../../fido2/util.c ../../fido2/u2f.c ../../fido2/test_power.c
|
||||
SRC += ../../fido2/apdu.c ../../fido2/util.c ../../fido2/u2f.c ../../fido2/test_power.c
|
||||
SRC += ../../fido2/stubs.c ../../fido2/log.c ../../fido2/ctaphid.c ../../fido2/ctap.c
|
||||
SRC += ../../fido2/ctap_parse.c ../../fido2/main.c
|
||||
SRC += ../../fido2/ctap_parse.c ../../fido2/crypto.c
|
||||
SRC += ../../fido2/version.c
|
||||
SRC += ../../fido2/data_migration.c
|
||||
SRC += ../../fido2/extensions/extensions.c ../../fido2/extensions/solo.c
|
||||
SRC += ../../fido2/extensions/wallet.c
|
||||
|
||||
@ -20,7 +22,9 @@ SRC += ../../crypto/cifra/src/sha512.c ../../crypto/cifra/src/blockwise.c
|
||||
OBJ1=$(SRC:.c=.o)
|
||||
OBJ=$(OBJ1:.s=.o)
|
||||
|
||||
INC = -Isrc/ -Isrc/cmsis/ -Ilib/ -Ilib/usbd/ -I../../fido2/ -I../../fido2/extensions
|
||||
INC = -Isrc/ -Isrc/cmsis/ -Ilib/ -Ilib/usbd/
|
||||
|
||||
INC+= -I../../fido2/ -I../../fido2/extensions
|
||||
INC += -I../../tinycbor/src -I../../crypto/sha256 -I../../crypto/micro-ecc
|
||||
INC += -I../../crypto/tiny-AES-c
|
||||
INC += -I../../crypto/cifra/src -I../../crypto/cifra/src/ext
|
||||
@ -64,11 +68,9 @@ all: $(TARGET).elf
|
||||
../../crypto/micro-ecc/uECC.o: ../../crypto/micro-ecc/uECC.c
|
||||
$(CC) $^ $(HW) -O3 $(ECC_CFLAGS) -o $@
|
||||
|
||||
%.o: %.s
|
||||
$(CC) $^ $(HW) -Os $(CFLAGS) -o $@
|
||||
|
||||
%.elf: $(OBJ)
|
||||
$(CC) $^ $(HW) $(LDFLAGS) -o $@
|
||||
@echo "Built version: $(VERSION_FLAGS)"
|
||||
|
||||
%.hex: %.elf
|
||||
$(SZ) $^
|
||||
@ -82,4 +84,5 @@ cbor:
|
||||
cd ../../tinycbor/ && make clean
|
||||
cd ../../tinycbor/ && make CC="$(CC)" AR=$(AR) \
|
||||
LDFLAGS="$(LDFLAGS_LIB)" \
|
||||
CFLAGS="$(CFLAGS) -Os"
|
||||
CFLAGS="$(CFLAGS) -Os -DCBOR_PARSER_MAX_RECURSIONS=3"
|
||||
|
||||
|
@ -2,14 +2,16 @@ include build/common.mk
|
||||
|
||||
# ST related
|
||||
SRC = bootloader/main.c bootloader/bootloader.c
|
||||
SRC += bootloader/pubkey_bootloader.c bootloader/version_check.c
|
||||
SRC += src/init.c src/redirect.c src/flash.c src/rng.c src/led.c src/device.c
|
||||
SRC += src/fifo.c src/crypto.c src/attestation.c src/sense.c
|
||||
SRC += src/fifo.c src/attestation.c src/sense.c
|
||||
SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c
|
||||
SRC += $(DRIVER_LIBS) $(USB_LIB)
|
||||
|
||||
# FIDO2 lib
|
||||
SRC += ../../fido2/util.c ../../fido2/u2f.c ../../fido2/extensions/extensions.c
|
||||
SRC += ../../fido2/stubs.c ../../fido2/log.c ../../fido2/ctaphid.c ../../fido2/ctap.c
|
||||
SRC += ../../fido2/crypto.c
|
||||
|
||||
# Crypto libs
|
||||
SRC += ../../crypto/sha256/sha256.c ../../crypto/micro-ecc/uECC.c
|
||||
@ -65,6 +67,7 @@ all: $(TARGET).elf
|
||||
|
||||
%.elf: $(OBJ)
|
||||
$(CC) $^ $(HW) $(LDFLAGS) -o $@
|
||||
$(SZ) $@
|
||||
|
||||
%.hex: %.elf
|
||||
$(CP) -O ihex $^ $(TARGET).hex
|
||||
|
@ -1,28 +1,35 @@
|
||||
include ../../fido2/version.mk
|
||||
|
||||
CC=$(PREFIX)arm-none-eabi-gcc
|
||||
CP=$(PREFIX)arm-none-eabi-objcopy
|
||||
SZ=$(PREFIX)arm-none-eabi-size
|
||||
AR=$(PREFIX)arm-none-eabi-ar
|
||||
AS=$(PREFIX)arm-none-eabi-as
|
||||
|
||||
DRIVER_LIBS := lib/stm32l4xx_hal_pcd.c lib/stm32l4xx_hal_pcd_ex.c lib/stm32l4xx_ll_gpio.c \
|
||||
lib/stm32l4xx_ll_rcc.c lib/stm32l4xx_ll_rng.c lib/stm32l4xx_ll_tim.c \
|
||||
lib/stm32l4xx_ll_usb.c lib/stm32l4xx_ll_utils.c lib/stm32l4xx_ll_pwr.c \
|
||||
lib/stm32l4xx_ll_usart.c lib/stm32l4xx_ll_spi.c
|
||||
lib/stm32l4xx_ll_usart.c lib/stm32l4xx_ll_spi.c lib/stm32l4xx_ll_exti.c
|
||||
|
||||
USB_LIB := lib/usbd/usbd_cdc.c lib/usbd/usbd_cdc_if.c lib/usbd/usbd_composite.c \
|
||||
lib/usbd/usbd_conf.c lib/usbd/usbd_core.c lib/usbd/usbd_ioreq.c \
|
||||
lib/usbd/usbd_ctlreq.c lib/usbd/usbd_desc.c lib/usbd/usbd_hid.c
|
||||
lib/usbd/usbd_ctlreq.c lib/usbd/usbd_desc.c lib/usbd/usbd_hid.c \
|
||||
lib/usbd/usbd_ccid.c
|
||||
|
||||
VERSION:=$(shell git describe --abbrev=0 )
|
||||
VERSION_FULL:=$(shell git describe)
|
||||
VERSION_MAJ:=$(shell python -c 'print("$(VERSION)".split(".")[0])')
|
||||
VERSION_MIN:=$(shell python -c 'print("$(VERSION)".split(".")[1])')
|
||||
VERSION_PAT:=$(shell python -c 'print("$(VERSION)".split(".")[2])')
|
||||
VERSION_FULL?=$(SOLO_VERSION_FULL)
|
||||
VERSION:=$(SOLO_VERSION)
|
||||
VERSION_MAJ:=$(SOLO_VERSION_MAJ)
|
||||
VERSION_MIN:=$(SOLO_VERSION_MIN)
|
||||
VERSION_PAT:=$(SOLO_VERSION_PAT)
|
||||
|
||||
VERSION_FLAGS= -DSOLO_VERSION_MAJ=$(VERSION_MAJ) -DSOLO_VERSION_MIN=$(VERSION_MIN) \
|
||||
-DSOLO_VERSION_PATCH=$(VERSION_PAT) -DSOLO_VERSION=\"$(VERSION_FULL)\"
|
||||
|
||||
_all:
|
||||
echo $(VERSION_FULL)
|
||||
echo $(VERSION_MAJ)
|
||||
echo $(VERSION_MIN)
|
||||
echo $(VERSION_PAT)
|
||||
echo $(SOLO_VERSION_FULL)
|
||||
echo $(SOLO_VERSION_MAJ)
|
||||
echo $(SOLO_VERSION_MIN)
|
||||
echo $(SOLO_VERSION_PAT)
|
||||
|
||||
%.o: %.s
|
||||
$(AS) -o $@ $^
|
290
targets/stm32l432/lib/stm32l4xx_ll_exti.c
Normal file
290
targets/stm32l432/lib/stm32l4xx_ll_exti.c
Normal file
@ -0,0 +1,290 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file stm32l4xx_ll_exti.c
|
||||
* @author MCD Application Team
|
||||
* @brief EXTI LL module driver.
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* <h2><center>© Copyright (c) 2017 STMicroelectronics.
|
||||
* All rights reserved.</center></h2>
|
||||
*
|
||||
* This software component is licensed by ST under BSD 3-Clause license,
|
||||
* the "License"; You may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at:
|
||||
* opensource.org/licenses/BSD-3-Clause
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
#if defined(USE_FULL_LL_DRIVER)
|
||||
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
#include "stm32l4xx_ll_exti.h"
|
||||
#ifdef USE_FULL_ASSERT
|
||||
#include "stm32_assert.h"
|
||||
#else
|
||||
#define assert_param(expr) ((void)0U)
|
||||
#endif
|
||||
|
||||
/** @addtogroup STM32L4xx_LL_Driver
|
||||
* @{
|
||||
*/
|
||||
|
||||
#if defined (EXTI)
|
||||
|
||||
/** @defgroup EXTI_LL EXTI
|
||||
* @{
|
||||
*/
|
||||
|
||||
/* Private types -------------------------------------------------------------*/
|
||||
/* Private variables ---------------------------------------------------------*/
|
||||
/* Private constants ---------------------------------------------------------*/
|
||||
/* Private macros ------------------------------------------------------------*/
|
||||
/** @addtogroup EXTI_LL_Private_Macros
|
||||
* @{
|
||||
*/
|
||||
|
||||
#define IS_LL_EXTI_LINE_0_31(__VALUE__) (((__VALUE__) & ~LL_EXTI_LINE_ALL_0_31) == 0x00000000U)
|
||||
#define IS_LL_EXTI_LINE_32_63(__VALUE__) (((__VALUE__) & ~LL_EXTI_LINE_ALL_32_63) == 0x00000000U)
|
||||
|
||||
#define IS_LL_EXTI_MODE(__VALUE__) (((__VALUE__) == LL_EXTI_MODE_IT) \
|
||||
|| ((__VALUE__) == LL_EXTI_MODE_EVENT) \
|
||||
|| ((__VALUE__) == LL_EXTI_MODE_IT_EVENT))
|
||||
|
||||
|
||||
#define IS_LL_EXTI_TRIGGER(__VALUE__) (((__VALUE__) == LL_EXTI_TRIGGER_NONE) \
|
||||
|| ((__VALUE__) == LL_EXTI_TRIGGER_RISING) \
|
||||
|| ((__VALUE__) == LL_EXTI_TRIGGER_FALLING) \
|
||||
|| ((__VALUE__) == LL_EXTI_TRIGGER_RISING_FALLING))
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/* Private function prototypes -----------------------------------------------*/
|
||||
|
||||
/* Exported functions --------------------------------------------------------*/
|
||||
/** @addtogroup EXTI_LL_Exported_Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @addtogroup EXTI_LL_EF_Init
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief De-initialize the EXTI registers to their default reset values.
|
||||
* @retval An ErrorStatus enumeration value:
|
||||
* - 0x00: EXTI registers are de-initialized
|
||||
*/
|
||||
uint32_t LL_EXTI_DeInit(void)
|
||||
{
|
||||
/* Interrupt mask register set to default reset values */
|
||||
LL_EXTI_WriteReg(IMR1, 0xFF820000U);
|
||||
/* Event mask register set to default reset values */
|
||||
LL_EXTI_WriteReg(EMR1, 0x00000000U);
|
||||
/* Rising Trigger selection register set to default reset values */
|
||||
LL_EXTI_WriteReg(RTSR1, 0x00000000U);
|
||||
/* Falling Trigger selection register set to default reset values */
|
||||
LL_EXTI_WriteReg(FTSR1, 0x00000000U);
|
||||
/* Software interrupt event register set to default reset values */
|
||||
LL_EXTI_WriteReg(SWIER1, 0x00000000U);
|
||||
/* Pending register clear */
|
||||
LL_EXTI_WriteReg(PR1, 0x007DFFFFU);
|
||||
|
||||
/* Interrupt mask register 2 set to default reset values */
|
||||
#if defined(LL_EXTI_LINE_40)
|
||||
LL_EXTI_WriteReg(IMR2, 0x00000187U);
|
||||
#else
|
||||
LL_EXTI_WriteReg(IMR2, 0x00000087U);
|
||||
#endif
|
||||
/* Event mask register 2 set to default reset values */
|
||||
LL_EXTI_WriteReg(EMR2, 0x00000000U);
|
||||
/* Rising Trigger selection register 2 set to default reset values */
|
||||
LL_EXTI_WriteReg(RTSR2, 0x00000000U);
|
||||
/* Falling Trigger selection register 2 set to default reset values */
|
||||
LL_EXTI_WriteReg(FTSR2, 0x00000000U);
|
||||
/* Software interrupt event register 2 set to default reset values */
|
||||
LL_EXTI_WriteReg(SWIER2, 0x00000000U);
|
||||
/* Pending register 2 clear */
|
||||
LL_EXTI_WriteReg(PR2, 0x00000078U);
|
||||
|
||||
return 0x00u;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the EXTI registers according to the specified parameters in EXTI_InitStruct.
|
||||
* @param EXTI_InitStruct pointer to a @ref LL_EXTI_InitTypeDef structure.
|
||||
* @retval An ErrorStatus enumeration value:
|
||||
* - 0x00: EXTI registers are initialized
|
||||
* - any other calue : wrong configuration
|
||||
*/
|
||||
uint32_t LL_EXTI_Init(LL_EXTI_InitTypeDef *EXTI_InitStruct)
|
||||
{
|
||||
uint32_t status = 0x00u;
|
||||
|
||||
/* Check the parameters */
|
||||
assert_param(IS_LL_EXTI_LINE_0_31(EXTI_InitStruct->Line_0_31));
|
||||
assert_param(IS_LL_EXTI_LINE_32_63(EXTI_InitStruct->Line_32_63));
|
||||
assert_param(IS_FUNCTIONAL_STATE(EXTI_InitStruct->LineCommand));
|
||||
assert_param(IS_LL_EXTI_MODE(EXTI_InitStruct->Mode));
|
||||
|
||||
/* ENABLE LineCommand */
|
||||
if (EXTI_InitStruct->LineCommand != DISABLE)
|
||||
{
|
||||
assert_param(IS_LL_EXTI_TRIGGER(EXTI_InitStruct->Trigger));
|
||||
|
||||
/* Configure EXTI Lines in range from 0 to 31 */
|
||||
if (EXTI_InitStruct->Line_0_31 != LL_EXTI_LINE_NONE)
|
||||
{
|
||||
switch (EXTI_InitStruct->Mode)
|
||||
{
|
||||
case LL_EXTI_MODE_IT:
|
||||
/* First Disable Event on provided Lines */
|
||||
LL_EXTI_DisableEvent_0_31(EXTI_InitStruct->Line_0_31);
|
||||
/* Then Enable IT on provided Lines */
|
||||
LL_EXTI_EnableIT_0_31(EXTI_InitStruct->Line_0_31);
|
||||
break;
|
||||
case LL_EXTI_MODE_EVENT:
|
||||
/* First Disable IT on provided Lines */
|
||||
LL_EXTI_DisableIT_0_31(EXTI_InitStruct->Line_0_31);
|
||||
/* Then Enable Event on provided Lines */
|
||||
LL_EXTI_EnableEvent_0_31(EXTI_InitStruct->Line_0_31);
|
||||
break;
|
||||
case LL_EXTI_MODE_IT_EVENT:
|
||||
/* Directly Enable IT & Event on provided Lines */
|
||||
LL_EXTI_EnableIT_0_31(EXTI_InitStruct->Line_0_31);
|
||||
LL_EXTI_EnableEvent_0_31(EXTI_InitStruct->Line_0_31);
|
||||
break;
|
||||
default:
|
||||
status = 0x01u;
|
||||
break;
|
||||
}
|
||||
if (EXTI_InitStruct->Trigger != LL_EXTI_TRIGGER_NONE)
|
||||
{
|
||||
switch (EXTI_InitStruct->Trigger)
|
||||
{
|
||||
case LL_EXTI_TRIGGER_RISING:
|
||||
/* First Disable Falling Trigger on provided Lines */
|
||||
LL_EXTI_DisableFallingTrig_0_31(EXTI_InitStruct->Line_0_31);
|
||||
/* Then Enable Rising Trigger on provided Lines */
|
||||
LL_EXTI_EnableRisingTrig_0_31(EXTI_InitStruct->Line_0_31);
|
||||
break;
|
||||
case LL_EXTI_TRIGGER_FALLING:
|
||||
/* First Disable Rising Trigger on provided Lines */
|
||||
LL_EXTI_DisableRisingTrig_0_31(EXTI_InitStruct->Line_0_31);
|
||||
/* Then Enable Falling Trigger on provided Lines */
|
||||
LL_EXTI_EnableFallingTrig_0_31(EXTI_InitStruct->Line_0_31);
|
||||
break;
|
||||
case LL_EXTI_TRIGGER_RISING_FALLING:
|
||||
LL_EXTI_EnableRisingTrig_0_31(EXTI_InitStruct->Line_0_31);
|
||||
LL_EXTI_EnableFallingTrig_0_31(EXTI_InitStruct->Line_0_31);
|
||||
break;
|
||||
default:
|
||||
status |= 0x02u;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Configure EXTI Lines in range from 32 to 63 */
|
||||
if (EXTI_InitStruct->Line_32_63 != LL_EXTI_LINE_NONE)
|
||||
{
|
||||
switch (EXTI_InitStruct->Mode)
|
||||
{
|
||||
case LL_EXTI_MODE_IT:
|
||||
/* First Disable Event on provided Lines */
|
||||
LL_EXTI_DisableEvent_32_63(EXTI_InitStruct->Line_32_63);
|
||||
/* Then Enable IT on provided Lines */
|
||||
LL_EXTI_EnableIT_32_63(EXTI_InitStruct->Line_32_63);
|
||||
break;
|
||||
case LL_EXTI_MODE_EVENT:
|
||||
/* First Disable IT on provided Lines */
|
||||
LL_EXTI_DisableIT_32_63(EXTI_InitStruct->Line_32_63);
|
||||
/* Then Enable Event on provided Lines */
|
||||
LL_EXTI_EnableEvent_32_63(EXTI_InitStruct->Line_32_63);
|
||||
break;
|
||||
case LL_EXTI_MODE_IT_EVENT:
|
||||
/* Directly Enable IT & Event on provided Lines */
|
||||
LL_EXTI_EnableIT_32_63(EXTI_InitStruct->Line_32_63);
|
||||
LL_EXTI_EnableEvent_32_63(EXTI_InitStruct->Line_32_63);
|
||||
break;
|
||||
default:
|
||||
status |= 0x04u;
|
||||
break;
|
||||
}
|
||||
if (EXTI_InitStruct->Trigger != LL_EXTI_TRIGGER_NONE)
|
||||
{
|
||||
switch (EXTI_InitStruct->Trigger)
|
||||
{
|
||||
case LL_EXTI_TRIGGER_RISING:
|
||||
/* First Disable Falling Trigger on provided Lines */
|
||||
LL_EXTI_DisableFallingTrig_32_63(EXTI_InitStruct->Line_32_63);
|
||||
/* Then Enable IT on provided Lines */
|
||||
LL_EXTI_EnableRisingTrig_32_63(EXTI_InitStruct->Line_32_63);
|
||||
break;
|
||||
case LL_EXTI_TRIGGER_FALLING:
|
||||
/* First Disable Rising Trigger on provided Lines */
|
||||
LL_EXTI_DisableRisingTrig_32_63(EXTI_InitStruct->Line_32_63);
|
||||
/* Then Enable Falling Trigger on provided Lines */
|
||||
LL_EXTI_EnableFallingTrig_32_63(EXTI_InitStruct->Line_32_63);
|
||||
break;
|
||||
case LL_EXTI_TRIGGER_RISING_FALLING:
|
||||
LL_EXTI_EnableRisingTrig_32_63(EXTI_InitStruct->Line_32_63);
|
||||
LL_EXTI_EnableFallingTrig_32_63(EXTI_InitStruct->Line_32_63);
|
||||
break;
|
||||
default:
|
||||
status = ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* DISABLE LineCommand */
|
||||
else
|
||||
{
|
||||
/* De-configure EXTI Lines in range from 0 to 31 */
|
||||
LL_EXTI_DisableIT_0_31(EXTI_InitStruct->Line_0_31);
|
||||
LL_EXTI_DisableEvent_0_31(EXTI_InitStruct->Line_0_31);
|
||||
/* De-configure EXTI Lines in range from 32 to 63 */
|
||||
LL_EXTI_DisableIT_32_63(EXTI_InitStruct->Line_32_63);
|
||||
LL_EXTI_DisableEvent_32_63(EXTI_InitStruct->Line_32_63);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set each @ref LL_EXTI_InitTypeDef field to default value.
|
||||
* @param EXTI_InitStruct Pointer to a @ref LL_EXTI_InitTypeDef structure.
|
||||
* @retval None
|
||||
*/
|
||||
void LL_EXTI_StructInit(LL_EXTI_InitTypeDef *EXTI_InitStruct)
|
||||
{
|
||||
EXTI_InitStruct->Line_0_31 = LL_EXTI_LINE_NONE;
|
||||
EXTI_InitStruct->Line_32_63 = LL_EXTI_LINE_NONE;
|
||||
EXTI_InitStruct->LineCommand = DISABLE;
|
||||
EXTI_InitStruct->Mode = LL_EXTI_MODE_IT;
|
||||
EXTI_InitStruct->Trigger = LL_EXTI_TRIGGER_FALLING;
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#endif /* defined (EXTI) */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#endif /* USE_FULL_LL_DRIVER */
|
||||
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
1361
targets/stm32l432/lib/stm32l4xx_ll_exti.h
Normal file
1361
targets/stm32l432/lib/stm32l4xx_ll_exti.h
Normal file
File diff suppressed because it is too large
Load Diff
319
targets/stm32l432/lib/usbd/usbd_ccid.c
Normal file
319
targets/stm32l432/lib/usbd/usbd_ccid.c
Normal file
@ -0,0 +1,319 @@
|
||||
#include <stdint.h>
|
||||
#include "usbd_ccid.h"
|
||||
#include "usbd_ctlreq.h"
|
||||
#include "usbd_conf.h"
|
||||
#include "usbd_core.h"
|
||||
|
||||
#include "log.h"
|
||||
|
||||
static uint8_t USBD_CCID_Init (USBD_HandleTypeDef *pdev,
|
||||
uint8_t cfgidx);
|
||||
|
||||
static uint8_t USBD_CCID_DeInit (USBD_HandleTypeDef *pdev,
|
||||
uint8_t cfgidx);
|
||||
|
||||
static uint8_t USBD_CCID_Setup (USBD_HandleTypeDef *pdev,
|
||||
USBD_SetupReqTypedef *req);
|
||||
|
||||
static uint8_t USBD_CCID_DataIn (USBD_HandleTypeDef *pdev,
|
||||
uint8_t epnum);
|
||||
|
||||
static uint8_t USBD_CCID_DataOut (USBD_HandleTypeDef *pdev,
|
||||
uint8_t epnum);
|
||||
|
||||
static uint8_t USBD_CCID_EP0_RxReady (USBD_HandleTypeDef *pdev);
|
||||
|
||||
|
||||
USBD_ClassTypeDef USBD_CCID =
|
||||
{
|
||||
USBD_CCID_Init,
|
||||
USBD_CCID_DeInit,
|
||||
USBD_CCID_Setup,
|
||||
NULL, /* EP0_TxSent, */
|
||||
USBD_CCID_EP0_RxReady,
|
||||
USBD_CCID_DataIn,
|
||||
USBD_CCID_DataOut,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static uint8_t ccidmsg_buf[CCID_DATA_PACKET_SIZE];
|
||||
|
||||
static uint8_t USBD_CCID_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx)
|
||||
{
|
||||
uint8_t ret = 0U;
|
||||
USBD_CCID_HandleTypeDef *hcdc;
|
||||
|
||||
//Y
|
||||
USBD_LL_OpenEP(pdev, CCID_IN_EP, USBD_EP_TYPE_BULK,
|
||||
CCID_DATA_PACKET_SIZE);
|
||||
|
||||
USBD_LL_OpenEP(pdev, CCID_OUT_EP, USBD_EP_TYPE_BULK,
|
||||
CCID_DATA_PACKET_SIZE);
|
||||
|
||||
pdev->ep_in[CCID_IN_EP & 0xFU].is_used = 1U;
|
||||
pdev->ep_out[CCID_OUT_EP & 0xFU].is_used = 1U;
|
||||
|
||||
|
||||
USBD_LL_OpenEP(pdev, CCID_CMD_EP, USBD_EP_TYPE_INTR, CCID_DATA_PACKET_SIZE);
|
||||
pdev->ep_in[CCID_CMD_EP & 0xFU].is_used = 1U;
|
||||
|
||||
// dump_pma_header("ccid.c");
|
||||
|
||||
static USBD_CCID_HandleTypeDef mem;
|
||||
pdev->pClassData = &mem;
|
||||
|
||||
hcdc = (USBD_CCID_HandleTypeDef*) pdev->pClassData;
|
||||
|
||||
// init transfer states
|
||||
hcdc->TxState = 0U;
|
||||
hcdc->RxState = 0U;
|
||||
|
||||
USBD_LL_PrepareReceive(&Solo_USBD_Device, CCID_OUT_EP, ccidmsg_buf,
|
||||
CCID_DATA_PACKET_SIZE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint8_t USBD_CCID_DeInit (USBD_HandleTypeDef *pdev, uint8_t cfgidx)
|
||||
{
|
||||
uint8_t ret = 0U;
|
||||
//N
|
||||
|
||||
USBD_LL_CloseEP(pdev, CCID_IN_EP);
|
||||
pdev->ep_in[CCID_IN_EP & 0xFU].is_used = 0U;
|
||||
|
||||
USBD_LL_CloseEP(pdev, CCID_OUT_EP);
|
||||
pdev->ep_out[CCID_OUT_EP & 0xFU].is_used = 0U;
|
||||
|
||||
USBD_LL_CloseEP(pdev, CCID_CMD_EP);
|
||||
pdev->ep_in[CCID_CMD_EP & 0xFU].is_used = 0U;
|
||||
|
||||
/* DeInit physical Interface components */
|
||||
if(pdev->pClassData != NULL)
|
||||
{
|
||||
pdev->pClassData = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief USBD_CDC_Setup
|
||||
* Handle the CDC specific requests
|
||||
* @param pdev: instance
|
||||
* @param req: usb requests
|
||||
* @retval status
|
||||
*/
|
||||
static uint8_t USBD_CCID_Setup (USBD_HandleTypeDef *pdev,
|
||||
USBD_SetupReqTypedef *req)
|
||||
{
|
||||
USBD_CCID_HandleTypeDef *hcdc = (USBD_CCID_HandleTypeDef*) pdev->pClassData;
|
||||
uint8_t ifalt = 0U;
|
||||
uint16_t status_info = 0U;
|
||||
uint8_t ret = USBD_OK;
|
||||
//N
|
||||
|
||||
switch (req->bmRequest & USB_REQ_TYPE_MASK)
|
||||
{
|
||||
case USB_REQ_TYPE_CLASS :
|
||||
if (req->wLength)
|
||||
{
|
||||
if (req->bmRequest & 0x80U)
|
||||
{
|
||||
USBD_CtlSendData (pdev, (uint8_t *)(void *)hcdc->data, req->wLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
hcdc->CmdOpCode = req->bRequest;
|
||||
hcdc->CmdLength = (uint8_t)req->wLength;
|
||||
|
||||
USBD_CtlPrepareRx (pdev, (uint8_t *)(void *)hcdc->data, req->wLength);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_REQ_TYPE_STANDARD:
|
||||
switch (req->bRequest)
|
||||
{
|
||||
case USB_REQ_GET_STATUS:
|
||||
if (pdev->dev_state == USBD_STATE_CONFIGURED)
|
||||
{
|
||||
USBD_CtlSendData (pdev, (uint8_t *)(void *)&status_info, 2U);
|
||||
}
|
||||
else
|
||||
{
|
||||
USBD_CtlError (pdev, req);
|
||||
ret = USBD_FAIL;
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_REQ_GET_INTERFACE:
|
||||
if (pdev->dev_state == USBD_STATE_CONFIGURED)
|
||||
{
|
||||
USBD_CtlSendData (pdev, &ifalt, 1U);
|
||||
}
|
||||
else
|
||||
{
|
||||
USBD_CtlError (pdev, req);
|
||||
ret = USBD_FAIL;
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_REQ_SET_INTERFACE:
|
||||
if (pdev->dev_state != USBD_STATE_CONFIGURED)
|
||||
{
|
||||
USBD_CtlError (pdev, req);
|
||||
ret = USBD_FAIL;
|
||||
}
|
||||
break;
|
||||
case USB_REQ_GET_DESCRIPTOR:
|
||||
break;
|
||||
default:
|
||||
USBD_CtlError (pdev, req);
|
||||
ret = USBD_FAIL;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
USBD_CtlError (pdev, req);
|
||||
ret = USBD_FAIL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief USBD_CDC_DataIn
|
||||
* Data sent on non-control IN endpoint
|
||||
* @param pdev: device instance
|
||||
* @param epnum: endpoint number
|
||||
* @retval status
|
||||
*/
|
||||
static uint8_t USBD_CCID_DataOut (USBD_HandleTypeDef *pdev, uint8_t epnum)
|
||||
{
|
||||
return USBD_OK;
|
||||
}
|
||||
static uint8_t USBD_CCID_DataIn (USBD_HandleTypeDef *pdev, uint8_t epnum)
|
||||
{
|
||||
USBD_CCID_HandleTypeDef *hcdc = (USBD_CCID_HandleTypeDef*)pdev->pClassData;
|
||||
|
||||
hcdc->TxState = 0U;
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
uint8_t USBD_CCID_TransmitPacket(uint8_t * msg, int len)
|
||||
{
|
||||
/* Update the packet total length */
|
||||
Solo_USBD_Device.ep_in[CCID_IN_EP & 0xFU].total_length = len;
|
||||
|
||||
while (PCD_GET_EP_TX_STATUS(USB, CCID_IN_EP & 0x0f) == USB_EP_TX_VALID)
|
||||
;
|
||||
/* Transmit next packet */
|
||||
USBD_LL_Transmit(&Solo_USBD_Device, CCID_IN_EP, msg,
|
||||
len);
|
||||
|
||||
printf1(TAG_CCID,"<< ");
|
||||
dump_hex1(TAG_CCID, msg, len);
|
||||
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ccid_send_status(CCID_HEADER * c, uint8_t status)
|
||||
{
|
||||
uint8_t msg[CCID_HEADER_SIZE];
|
||||
memset(msg,0,sizeof(msg));
|
||||
|
||||
msg[0] = CCID_SLOT_STATUS_RES;
|
||||
msg[6] = c->seq;
|
||||
msg[7] = status;
|
||||
|
||||
USBD_CCID_TransmitPacket(msg, sizeof(msg));
|
||||
|
||||
}
|
||||
|
||||
void ccid_send_data_block(CCID_HEADER * c, uint8_t status)
|
||||
{
|
||||
uint8_t msg[CCID_HEADER_SIZE];
|
||||
memset(msg,0,sizeof(msg));
|
||||
|
||||
msg[0] = CCID_DATA_BLOCK_RES;
|
||||
msg[6] = c->seq;
|
||||
msg[7] = status;
|
||||
|
||||
USBD_CCID_TransmitPacket(msg, sizeof(msg));
|
||||
|
||||
}
|
||||
|
||||
void handle_ccid(uint8_t * msg, int len)
|
||||
{
|
||||
CCID_HEADER * h = (CCID_HEADER *) msg;
|
||||
switch(h->type)
|
||||
{
|
||||
case CCID_SLOT_STATUS:
|
||||
ccid_send_status(h, CCID_STATUS_ON);
|
||||
break;
|
||||
case CCID_POWER_ON:
|
||||
ccid_send_data_block(h, CCID_STATUS_ON);
|
||||
break;
|
||||
case CCID_POWER_OFF:
|
||||
ccid_send_status(h, CCID_STATUS_OFF);
|
||||
break;
|
||||
default:
|
||||
ccid_send_status(h, CCID_STATUS_ON);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief USBD_CDC_DataOut
|
||||
* Data received on non-control Out endpoint
|
||||
* @param pdev: device instance
|
||||
* @param epnum: endpoint number
|
||||
* @retval status
|
||||
*/
|
||||
uint8_t usb_ccid_recieve_callback(USBD_HandleTypeDef *pdev, uint8_t epnum)
|
||||
{
|
||||
|
||||
USBD_CCID_HandleTypeDef *hcdc = (USBD_CCID_HandleTypeDef*) pdev->pClassData;
|
||||
|
||||
/* Get the received data length */
|
||||
hcdc->RxLength = USBD_LL_GetRxDataSize (pdev, epnum);
|
||||
|
||||
printf1(TAG_CCID, ">> ");
|
||||
dump_hex1(TAG_CCID, ccidmsg_buf, hcdc->RxLength);
|
||||
|
||||
handle_ccid(ccidmsg_buf, hcdc->RxLength);
|
||||
|
||||
USBD_LL_PrepareReceive(&Solo_USBD_Device, CCID_OUT_EP, ccidmsg_buf,
|
||||
CCID_DATA_PACKET_SIZE);
|
||||
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief USBD_CDC_EP0_RxReady
|
||||
* Handle EP0 Rx Ready event
|
||||
* @param pdev: device instance
|
||||
* @retval status
|
||||
*/
|
||||
static uint8_t USBD_CCID_EP0_RxReady (USBD_HandleTypeDef *pdev)
|
||||
{
|
||||
return USBD_OK;
|
||||
}
|
58
targets/stm32l432/lib/usbd/usbd_ccid.h
Normal file
58
targets/stm32l432/lib/usbd/usbd_ccid.h
Normal file
@ -0,0 +1,58 @@
|
||||
#ifndef _USBD_H_
|
||||
#define _USBD_H_
|
||||
|
||||
#include "usbd_ioreq.h"
|
||||
|
||||
#define CCID_HEADER_SIZE 10
|
||||
typedef struct
|
||||
{
|
||||
uint8_t type;
|
||||
uint32_t len;
|
||||
uint8_t slot;
|
||||
uint8_t seq;
|
||||
uint8_t rsvd;
|
||||
uint16_t param;
|
||||
} __attribute__((packed)) CCID_HEADER;
|
||||
|
||||
#define CCID_IN_EP 0x86U /* EP1 for data IN */
|
||||
#define CCID_OUT_EP 0x04U /* EP1 for data OUT */
|
||||
#define CCID_CMD_EP 0x85U /* EP2 for CDC commands */
|
||||
|
||||
#define CCID_DATA_PACKET_SIZE 64
|
||||
|
||||
#define CCID_SET_PARAMS 0x61
|
||||
#define CCID_POWER_ON 0x62
|
||||
#define CCID_POWER_OFF 0x63
|
||||
#define CCID_SLOT_STATUS 0x65
|
||||
#define CCID_SECURE 0x69
|
||||
#define CCID_GET_PARAMS 0x6C
|
||||
#define CCID_RESET_PARAMS 0x6D
|
||||
#define CCID_XFR_BLOCK 0x6F
|
||||
|
||||
#define CCID_STATUS_ON 0x00
|
||||
#define CCID_STATUS_OFF 0x02
|
||||
|
||||
#define CCID_DATA_BLOCK_RES 0x80
|
||||
#define CCID_SLOT_STATUS_RES 0x81
|
||||
#define CCID_PARAMS_RES 0x82
|
||||
|
||||
extern USBD_ClassTypeDef USBD_CCID;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t data[CCID_DATA_PACKET_SIZE / 4U];
|
||||
uint8_t CmdOpCode;
|
||||
uint8_t CmdLength;
|
||||
uint8_t *RxBuffer;
|
||||
uint8_t *TxBuffer;
|
||||
uint32_t RxLength;
|
||||
uint32_t TxLength;
|
||||
|
||||
__IO uint32_t TxState;
|
||||
__IO uint32_t RxState;
|
||||
}
|
||||
USBD_CCID_HandleTypeDef;
|
||||
|
||||
uint8_t usb_ccid_recieve_callback(USBD_HandleTypeDef *pdev, uint8_t epnum);
|
||||
|
||||
#endif
|
@ -195,302 +195,9 @@ USBD_ClassTypeDef USBD_CDC =
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
// USBD_CDC_GetHSCfgDesc,
|
||||
// USBD_CDC_GetFSCfgDesc,
|
||||
// USBD_CDC_GetOtherSpeedCfgDesc,
|
||||
// USBD_CDC_GetDeviceQualifierDescriptor,
|
||||
};
|
||||
|
||||
/* USB CDC device Configuration Descriptor */
|
||||
__ALIGN_BEGIN uint8_t USBD_CDC_CfgHSDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END =
|
||||
{
|
||||
/*Configuration Descriptor*/
|
||||
0x09, /* bLength: Configuration Descriptor size */
|
||||
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
|
||||
USB_CDC_CONFIG_DESC_SIZ, /* wTotalLength:no of returned bytes */
|
||||
0x00,
|
||||
0x02, /* bNumInterfaces: 2 interface */
|
||||
0x01, /* bConfigurationValue: Configuration value */
|
||||
0x00, /* iConfiguration: Index of string descriptor describing the configuration */
|
||||
0xC0, /* bmAttributes: self powered */
|
||||
0x32, /* MaxPower 0 mA */
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
/*Interface Descriptor */
|
||||
0x09, /* bLength: Interface Descriptor size */
|
||||
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
|
||||
/* Interface descriptor type */
|
||||
0x00, /* bInterfaceNumber: Number of Interface */
|
||||
0x00, /* bAlternateSetting: Alternate setting */
|
||||
0x01, /* bNumEndpoints: One endpoints used */
|
||||
0x02, /* bInterfaceClass: Communication Interface Class */
|
||||
0x02, /* bInterfaceSubClass: Abstract Control Model */
|
||||
0x01, /* bInterfaceProtocol: Common AT commands */
|
||||
0x00, /* iInterface: */
|
||||
|
||||
/*Header Functional Descriptor*/
|
||||
0x05, /* bLength: Endpoint Descriptor size */
|
||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||
0x00, /* bDescriptorSubtype: Header Func Desc */
|
||||
0x10, /* bcdCDC: spec release number */
|
||||
0x01,
|
||||
|
||||
/*Call Management Functional Descriptor*/
|
||||
0x05, /* bFunctionLength */
|
||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||
0x01, /* bDescriptorSubtype: Call Management Func Desc */
|
||||
0x00, /* bmCapabilities: D0+D1 */
|
||||
0x01, /* bDataInterface: 1 */
|
||||
|
||||
/*ACM Functional Descriptor*/
|
||||
0x04, /* bFunctionLength */
|
||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||
0x02, /* bDescriptorSubtype: Abstract Control Management desc */
|
||||
0x02, /* bmCapabilities */
|
||||
|
||||
/*Union Functional Descriptor*/
|
||||
0x05, /* bFunctionLength */
|
||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||
0x06, /* bDescriptorSubtype: Union func desc */
|
||||
0x00, /* bMasterInterface: Communication class interface */
|
||||
0x01, /* bSlaveInterface0: Data Class Interface */
|
||||
|
||||
/*Endpoint 2 Descriptor*/
|
||||
0x07, /* bLength: Endpoint Descriptor size */
|
||||
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
|
||||
CDC_CMD_EP, /* bEndpointAddress */
|
||||
0x03, /* bmAttributes: Interrupt */
|
||||
LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */
|
||||
HIBYTE(CDC_CMD_PACKET_SIZE),
|
||||
CDC_HS_BINTERVAL, /* bInterval: */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
/*Data class interface descriptor*/
|
||||
0x09, /* bLength: Endpoint Descriptor size */
|
||||
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */
|
||||
0x01, /* bInterfaceNumber: Number of Interface */
|
||||
0x00, /* bAlternateSetting: Alternate setting */
|
||||
0x02, /* bNumEndpoints: Two endpoints used */
|
||||
0x0A, /* bInterfaceClass: CDC */
|
||||
0x00, /* bInterfaceSubClass: */
|
||||
0x00, /* bInterfaceProtocol: */
|
||||
0x00, /* iInterface: */
|
||||
|
||||
/*Endpoint OUT Descriptor*/
|
||||
0x07, /* bLength: Endpoint Descriptor size */
|
||||
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
|
||||
CDC_OUT_EP, /* bEndpointAddress */
|
||||
0x02, /* bmAttributes: Bulk */
|
||||
LOBYTE(CDC_DATA_HS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
|
||||
HIBYTE(CDC_DATA_HS_MAX_PACKET_SIZE),
|
||||
0x00, /* bInterval: ignore for Bulk transfer */
|
||||
|
||||
/*Endpoint IN Descriptor*/
|
||||
0x07, /* bLength: Endpoint Descriptor size */
|
||||
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
|
||||
CDC_IN_EP, /* bEndpointAddress */
|
||||
0x02, /* bmAttributes: Bulk */
|
||||
LOBYTE(CDC_DATA_HS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
|
||||
HIBYTE(CDC_DATA_HS_MAX_PACKET_SIZE),
|
||||
0x00 /* bInterval: ignore for Bulk transfer */
|
||||
} ;
|
||||
|
||||
|
||||
/* USB CDC device Configuration Descriptor */
|
||||
__ALIGN_BEGIN uint8_t USBD_CDC_CfgFSDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END =
|
||||
{
|
||||
/*Configuration Descriptor*/
|
||||
0x09, /* bLength: Configuration Descriptor size */
|
||||
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
|
||||
USB_CDC_CONFIG_DESC_SIZ, /* wTotalLength:no of returned bytes */
|
||||
0x00,
|
||||
0x02, /* bNumInterfaces: 2 interface */
|
||||
0x01, /* bConfigurationValue: Configuration value */
|
||||
0x00, /* iConfiguration: Index of string descriptor describing the configuration */
|
||||
0xC0, /* bmAttributes: self powered */
|
||||
0x32, /* MaxPower 0 mA */
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
/*Interface Descriptor */
|
||||
0x09, /* bLength: Interface Descriptor size */
|
||||
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
|
||||
/* Interface descriptor type */
|
||||
0x00, /* bInterfaceNumber: Number of Interface */
|
||||
0x00, /* bAlternateSetting: Alternate setting */
|
||||
0x01, /* bNumEndpoints: One endpoints used */
|
||||
0x02, /* bInterfaceClass: Communication Interface Class */
|
||||
0x02, /* bInterfaceSubClass: Abstract Control Model */
|
||||
0x01, /* bInterfaceProtocol: Common AT commands */
|
||||
0x00, /* iInterface: */
|
||||
|
||||
/*Header Functional Descriptor*/
|
||||
0x05, /* bLength: Endpoint Descriptor size */
|
||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||
0x00, /* bDescriptorSubtype: Header Func Desc */
|
||||
0x10, /* bcdCDC: spec release number */
|
||||
0x01,
|
||||
|
||||
/*Call Management Functional Descriptor*/
|
||||
0x05, /* bFunctionLength */
|
||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||
0x01, /* bDescriptorSubtype: Call Management Func Desc */
|
||||
0x00, /* bmCapabilities: D0+D1 */
|
||||
0x01, /* bDataInterface: 1 */
|
||||
|
||||
/*ACM Functional Descriptor*/
|
||||
0x04, /* bFunctionLength */
|
||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||
0x02, /* bDescriptorSubtype: Abstract Control Management desc */
|
||||
0x02, /* bmCapabilities */
|
||||
|
||||
/*Union Functional Descriptor*/
|
||||
0x05, /* bFunctionLength */
|
||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||
0x06, /* bDescriptorSubtype: Union func desc */
|
||||
0x00, /* bMasterInterface: Communication class interface */
|
||||
0x01, /* bSlaveInterface0: Data Class Interface */
|
||||
|
||||
/*Endpoint 2 Descriptor*/
|
||||
0x07, /* bLength: Endpoint Descriptor size */
|
||||
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
|
||||
CDC_CMD_EP, /* bEndpointAddress */
|
||||
0x03, /* bmAttributes: Interrupt */
|
||||
LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */
|
||||
HIBYTE(CDC_CMD_PACKET_SIZE),
|
||||
CDC_FS_BINTERVAL, /* bInterval: */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
/*Data class interface descriptor*/
|
||||
0x09, /* bLength: Endpoint Descriptor size */
|
||||
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */
|
||||
0x01, /* bInterfaceNumber: Number of Interface */
|
||||
0x00, /* bAlternateSetting: Alternate setting */
|
||||
0x02, /* bNumEndpoints: Two endpoints used */
|
||||
0x0A, /* bInterfaceClass: CDC */
|
||||
0x00, /* bInterfaceSubClass: */
|
||||
0x00, /* bInterfaceProtocol: */
|
||||
0x00, /* iInterface: */
|
||||
|
||||
/*Endpoint OUT Descriptor*/
|
||||
0x07, /* bLength: Endpoint Descriptor size */
|
||||
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
|
||||
CDC_OUT_EP, /* bEndpointAddress */
|
||||
0x02, /* bmAttributes: Bulk */
|
||||
LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
|
||||
HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
|
||||
0x00, /* bInterval: ignore for Bulk transfer */
|
||||
|
||||
/*Endpoint IN Descriptor*/
|
||||
0x07, /* bLength: Endpoint Descriptor size */
|
||||
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
|
||||
CDC_IN_EP, /* bEndpointAddress */
|
||||
0x02, /* bmAttributes: Bulk */
|
||||
LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
|
||||
HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
|
||||
0x00 /* bInterval: ignore for Bulk transfer */
|
||||
} ;
|
||||
|
||||
__ALIGN_BEGIN uint8_t USBD_CDC_OtherSpeedCfgDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END =
|
||||
{
|
||||
0x09, /* bLength: Configuation Descriptor size */
|
||||
USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION,
|
||||
USB_CDC_CONFIG_DESC_SIZ,
|
||||
0x00,
|
||||
0x02, /* bNumInterfaces: 2 interfaces */
|
||||
0x01, /* bConfigurationValue: */
|
||||
0x04, /* iConfiguration: */
|
||||
0xC0, /* bmAttributes: */
|
||||
0x32, /* MaxPower 100 mA */
|
||||
|
||||
/*Interface Descriptor */
|
||||
0x09, /* bLength: Interface Descriptor size */
|
||||
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
|
||||
/* Interface descriptor type */
|
||||
0x00, /* bInterfaceNumber: Number of Interface */
|
||||
0x00, /* bAlternateSetting: Alternate setting */
|
||||
0x01, /* bNumEndpoints: One endpoints used */
|
||||
0x02, /* bInterfaceClass: Communication Interface Class */
|
||||
0x02, /* bInterfaceSubClass: Abstract Control Model */
|
||||
0x01, /* bInterfaceProtocol: Common AT commands */
|
||||
0x00, /* iInterface: */
|
||||
|
||||
/*Header Functional Descriptor*/
|
||||
0x05, /* bLength: Endpoint Descriptor size */
|
||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||
0x00, /* bDescriptorSubtype: Header Func Desc */
|
||||
0x10, /* bcdCDC: spec release number */
|
||||
0x01,
|
||||
|
||||
/*Call Management Functional Descriptor*/
|
||||
0x05, /* bFunctionLength */
|
||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||
0x01, /* bDescriptorSubtype: Call Management Func Desc */
|
||||
0x00, /* bmCapabilities: D0+D1 */
|
||||
0x01, /* bDataInterface: 1 */
|
||||
|
||||
/*ACM Functional Descriptor*/
|
||||
0x04, /* bFunctionLength */
|
||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||
0x02, /* bDescriptorSubtype: Abstract Control Management desc */
|
||||
0x02, /* bmCapabilities */
|
||||
|
||||
/*Union Functional Descriptor*/
|
||||
0x05, /* bFunctionLength */
|
||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||
0x06, /* bDescriptorSubtype: Union func desc */
|
||||
0x00, /* bMasterInterface: Communication class interface */
|
||||
0x01, /* bSlaveInterface0: Data Class Interface */
|
||||
|
||||
/*Endpoint 2 Descriptor*/
|
||||
0x07, /* bLength: Endpoint Descriptor size */
|
||||
USB_DESC_TYPE_ENDPOINT , /* bDescriptorType: Endpoint */
|
||||
CDC_CMD_EP, /* bEndpointAddress */
|
||||
0x03, /* bmAttributes: Interrupt */
|
||||
LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */
|
||||
HIBYTE(CDC_CMD_PACKET_SIZE),
|
||||
CDC_FS_BINTERVAL, /* bInterval: */
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
/*Data class interface descriptor*/
|
||||
0x09, /* bLength: Endpoint Descriptor size */
|
||||
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */
|
||||
0x01, /* bInterfaceNumber: Number of Interface */
|
||||
0x00, /* bAlternateSetting: Alternate setting */
|
||||
0x02, /* bNumEndpoints: Two endpoints used */
|
||||
0x0A, /* bInterfaceClass: CDC */
|
||||
0x00, /* bInterfaceSubClass: */
|
||||
0x00, /* bInterfaceProtocol: */
|
||||
0x00, /* iInterface: */
|
||||
|
||||
/*Endpoint OUT Descriptor*/
|
||||
0x07, /* bLength: Endpoint Descriptor size */
|
||||
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
|
||||
CDC_OUT_EP, /* bEndpointAddress */
|
||||
0x02, /* bmAttributes: Bulk */
|
||||
0x40, /* wMaxPacketSize: */
|
||||
0x00,
|
||||
0x00, /* bInterval: ignore for Bulk transfer */
|
||||
|
||||
/*Endpoint IN Descriptor*/
|
||||
0x07, /* bLength: Endpoint Descriptor size */
|
||||
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
|
||||
CDC_IN_EP, /* bEndpointAddress */
|
||||
0x02, /* bmAttributes: Bulk */
|
||||
0x40, /* wMaxPacketSize: */
|
||||
0x00,
|
||||
0x00 /* bInterval */
|
||||
};
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @defgroup USBD_CDC_Private_Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief USBD_CDC_Init
|
||||
@ -782,45 +489,7 @@ static uint8_t USBD_CDC_EP0_RxReady (USBD_HandleTypeDef *pdev)
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief USBD_CDC_GetFSCfgDesc
|
||||
* Return configuration descriptor
|
||||
* @param speed : current device speed
|
||||
* @param length : pointer data length
|
||||
* @retval pointer to descriptor buffer
|
||||
*/
|
||||
/*static uint8_t *USBD_CDC_GetFSCfgDesc (uint16_t *length)
|
||||
{
|
||||
*length = sizeof (USBD_CDC_CfgFSDesc);
|
||||
return USBD_CDC_CfgFSDesc;
|
||||
}
|
||||
*/
|
||||
/**
|
||||
* @brief USBD_CDC_GetHSCfgDesc
|
||||
* Return configuration descriptor
|
||||
* @param speed : current device speed
|
||||
* @param length : pointer data length
|
||||
* @retval pointer to descriptor buffer
|
||||
*/
|
||||
/*static uint8_t *USBD_CDC_GetHSCfgDesc (uint16_t *length)
|
||||
{
|
||||
*length = sizeof (USBD_CDC_CfgHSDesc);
|
||||
return USBD_CDC_CfgHSDesc;
|
||||
}
|
||||
*/
|
||||
/**
|
||||
* @brief USBD_CDC_GetCfgDesc
|
||||
* Return configuration descriptor
|
||||
* @param speed : current device speed
|
||||
* @param length : pointer data length
|
||||
* @retval pointer to descriptor buffer
|
||||
*/
|
||||
/*static uint8_t *USBD_CDC_GetOtherSpeedCfgDesc (uint16_t *length)
|
||||
{
|
||||
*length = sizeof (USBD_CDC_OtherSpeedCfgDesc);
|
||||
return USBD_CDC_OtherSpeedCfgDesc;
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief DeviceQualifierDescriptor
|
||||
* return Device Qualifier descriptor
|
||||
@ -939,22 +608,10 @@ uint8_t USBD_CDC_ReceivePacket(USBD_HandleTypeDef *pdev)
|
||||
/* Suspend or Resume USB Out process */
|
||||
if(pdev->pClassData != NULL)
|
||||
{
|
||||
if(pdev->dev_speed == USBD_SPEED_HIGH )
|
||||
{
|
||||
/* Prepare Out endpoint to receive next packet */
|
||||
USBD_LL_PrepareReceive(pdev,
|
||||
CDC_OUT_EP,
|
||||
hcdc->RxBuffer,
|
||||
CDC_DATA_HS_OUT_PACKET_SIZE);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Prepare Out endpoint to receive next packet */
|
||||
USBD_LL_PrepareReceive(pdev,
|
||||
CDC_OUT_EP,
|
||||
hcdc->RxBuffer,
|
||||
CDC_DATA_FS_OUT_PACKET_SIZE);
|
||||
}
|
||||
return USBD_OK;
|
||||
}
|
||||
else
|
||||
|
@ -2,7 +2,9 @@
|
||||
#include "usbd_desc.h"
|
||||
#include "usbd_hid.h"
|
||||
#include "usbd_cdc.h"
|
||||
#include "usbd_ccid.h"
|
||||
#include "usbd_ctlreq.h"
|
||||
#include "app.h"
|
||||
|
||||
static uint8_t USBD_Composite_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx);
|
||||
|
||||
@ -26,151 +28,265 @@ static uint8_t *USBD_Composite_GetOtherSpeedCfgDesc (uint16_t *length);
|
||||
|
||||
static uint8_t *USBD_Composite_GetDeviceQualifierDescriptor (uint16_t *length);
|
||||
|
||||
#define NUM_INTERFACES 2
|
||||
|
||||
#if NUM_INTERFACES>1
|
||||
#define COMPOSITE_CDC_HID_DESCRIPTOR_SIZE (90)
|
||||
#ifdef ENABLE_CCID
|
||||
#define CCID_SIZE 84
|
||||
#define CCID_NUM_INTERFACE 1
|
||||
#else
|
||||
#define COMPOSITE_CDC_HID_DESCRIPTOR_SIZE (41)
|
||||
#define CCID_NUM_INTERFACE 0
|
||||
#define CCID_SIZE 0
|
||||
#endif
|
||||
|
||||
#define HID_INTF_NUM 0
|
||||
#define CDC_INTF_NUM 1
|
||||
#if DEBUG_LEVEL > 0
|
||||
#define CDC_SIZE (49 + 8 + 9 + 4)
|
||||
#define CDC_NUM_INTERFACE 2
|
||||
#else
|
||||
#define CDC_SIZE 0
|
||||
#define CDC_NUM_INTERFACE 0
|
||||
#endif
|
||||
|
||||
#define HID_SIZE 41
|
||||
|
||||
#define COMPOSITE_CDC_HID_DESCRIPTOR_SIZE (HID_SIZE + CDC_SIZE + CCID_SIZE)
|
||||
#define NUM_INTERFACES (1 + CDC_NUM_INTERFACE + CCID_NUM_INTERFACE)
|
||||
#define NUM_CLASSES 3
|
||||
|
||||
|
||||
#define HID_INTF_NUM 0
|
||||
#define CDC_MASTER_INTF_NUM 1
|
||||
#define CDC_SLAVE_INTF_NUM 2
|
||||
#define CCID_INTF_NUM 3
|
||||
__ALIGN_BEGIN uint8_t COMPOSITE_CDC_HID_DESCRIPTOR[COMPOSITE_CDC_HID_DESCRIPTOR_SIZE] __ALIGN_END =
|
||||
{
|
||||
/*Configuration Descriptor*/
|
||||
0x09, /* bLength: Configuration Descriptor size */
|
||||
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
|
||||
COMPOSITE_CDC_HID_DESCRIPTOR_SIZE, /* wTotalLength:no of returned bytes */
|
||||
0x00,
|
||||
NUM_INTERFACES, /* bNumInterfaces: 1 interface */
|
||||
0x01, /* bConfigurationValue: Configuration value */
|
||||
0x00, /* iConfiguration: Index of string descriptor describing the configuration */
|
||||
0x80, /* bmAttributes: self powered */
|
||||
0x32, /* MaxPower 100 mA */
|
||||
{
|
||||
/*Configuration Descriptor*/
|
||||
0x09, /* bLength: Configuration Descriptor size */
|
||||
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
|
||||
COMPOSITE_CDC_HID_DESCRIPTOR_SIZE, /* wTotalLength:no of returned bytes */
|
||||
0x00,
|
||||
NUM_INTERFACES, /* bNumInterfaces */
|
||||
0x01, /* bConfigurationValue: Configuration value */
|
||||
0x00, /* iConfiguration: Index of string descriptor describing the configuration */
|
||||
0x80, /* bmAttributes: self powered */
|
||||
0x32, /* MaxPower 100 mA */
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
/* */
|
||||
/* HID */
|
||||
/* */
|
||||
/* */
|
||||
/* HID */
|
||||
/* */
|
||||
|
||||
/************** Descriptor of Joystick Mouse interface ****************/
|
||||
0x09, /*bLength: Interface Descriptor size*/
|
||||
USB_DESC_TYPE_INTERFACE,/*bDescriptorType: Interface descriptor type*/
|
||||
HID_INTF_NUM, /*bInterfaceNumber: Number of Interface*/
|
||||
0x00, /*bAlternateSetting: Alternate setting*/
|
||||
0x02, /*bNumEndpoints*/
|
||||
0x03, /*bInterfaceClass: HID*/
|
||||
0x00, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
|
||||
0x00, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
|
||||
2, /*iInterface: Index of string descriptor*/
|
||||
/******************** Descriptor of Joystick Mouse HID ********************/
|
||||
0x09, /*bLength: HID Descriptor size*/
|
||||
HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/
|
||||
0x11, /*bcdHID: HID Class Spec release number*/
|
||||
0x01,
|
||||
0x00, /*bCountryCode: Hardware target country*/
|
||||
0x01, /*bNumDescriptors: Number of HID class descriptors to follow*/
|
||||
0x22, /*bDescriptorType*/
|
||||
HID_FIDO_REPORT_DESC_SIZE,/*wItemLength: Total length of Report descriptor*/
|
||||
0,
|
||||
/******************** Descriptor of Mouse endpoint ********************/
|
||||
0x07, /*bLength: Endpoint Descriptor size*/
|
||||
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
|
||||
HID_EPIN_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
|
||||
0x03, /*bmAttributes: Interrupt endpoint*/
|
||||
HID_EPIN_SIZE, /*wMaxPacketSize: 4 Byte max */
|
||||
0x00,
|
||||
HID_BINTERVAL, /*bInterval: Polling Interval */
|
||||
/************** Descriptor of Joystick Mouse interface ****************/
|
||||
0x09, /*bLength: Interface Descriptor size*/
|
||||
USB_DESC_TYPE_INTERFACE, /*bDescriptorType: Interface descriptor type*/
|
||||
HID_INTF_NUM, /*bInterfaceNumber: Number of Interface*/
|
||||
0x00, /*bAlternateSetting: Alternate setting*/
|
||||
0x02, /*bNumEndpoints*/
|
||||
0x03, /*bInterfaceClass: HID*/
|
||||
0x00, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
|
||||
0x00, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
|
||||
2, /*iInterface: Index of string descriptor*/
|
||||
/******************** Descriptor of Joystick Mouse HID ********************/
|
||||
0x09, /*bLength: HID Descriptor size*/
|
||||
HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/
|
||||
0x11, /*bcdHID: HID Class Spec release number*/
|
||||
0x01,
|
||||
0x00, /*bCountryCode: Hardware target country*/
|
||||
0x01, /*bNumDescriptors: Number of HID class descriptors to follow*/
|
||||
0x22, /*bDescriptorType*/
|
||||
HID_FIDO_REPORT_DESC_SIZE, /*wItemLength: Total length of Report descriptor*/
|
||||
0,
|
||||
/******************** Descriptor of Mouse endpoint ********************/
|
||||
0x07, /*bLength: Endpoint Descriptor size*/
|
||||
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
|
||||
HID_EPIN_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
|
||||
0x03, /*bmAttributes: Interrupt endpoint*/
|
||||
HID_EPIN_SIZE, /*wMaxPacketSize: 4 Byte max */
|
||||
0x00,
|
||||
HID_BINTERVAL, /*bInterval: Polling Interval */
|
||||
|
||||
0x07, /*bLength: Endpoint Descriptor size*/
|
||||
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
|
||||
HID_EPOUT_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
|
||||
0x03, /*bmAttributes: Interrupt endpoint*/
|
||||
HID_EPOUT_SIZE, /*wMaxPacketSize: 4 Byte max */
|
||||
0x00,
|
||||
HID_BINTERVAL, /*bInterval: Polling Interval */
|
||||
0x07, /*bLength: Endpoint Descriptor size*/
|
||||
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
|
||||
HID_EPOUT_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
|
||||
0x03, /*bmAttributes: Interrupt endpoint*/
|
||||
HID_EPOUT_SIZE, /*wMaxPacketSize: 4 Byte max */
|
||||
0x00,
|
||||
HID_BINTERVAL, /*bInterval: Polling Interval */
|
||||
|
||||
#if DEBUG_LEVEL > 0
|
||||
|
||||
/* */
|
||||
/* CDC */
|
||||
/* */
|
||||
// This "IAD" is needed for Windows since it ignores the standard Union Functional Descriptor
|
||||
0x08, // bLength
|
||||
0x0B, // IAD type
|
||||
CDC_MASTER_INTF_NUM, // First interface
|
||||
CDC_SLAVE_INTF_NUM, // Next interface
|
||||
0x02, // bInterfaceClass of the first interface
|
||||
0x02, // bInterfaceSubClass of the first interface
|
||||
0x00, // bInterfaceProtocol of the first interface
|
||||
0x00, // Interface string index
|
||||
|
||||
#if NUM_INTERFACES>1
|
||||
/*Interface Descriptor */
|
||||
0x09, /* bLength: Interface Descriptor size */
|
||||
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
|
||||
/* Interface descriptor type */
|
||||
/*!*/ CDC_MASTER_INTF_NUM, /* bInterfaceNumber: Number of Interface */
|
||||
0x00, /* bAlternateSetting: Alternate setting */
|
||||
0x01, /* bNumEndpoints: 1 endpoint used */
|
||||
0x02, /* bInterfaceClass: Communication Interface Class */
|
||||
0x02, /* bInterfaceSubClass: Abstract Control Model */
|
||||
0x00, /* bInterfaceProtocol: Common AT commands */
|
||||
0x00, /* iInterface: */
|
||||
|
||||
/* */
|
||||
/* CDC */
|
||||
/* */
|
||||
/*Header Functional Descriptor*/
|
||||
0x05, /* bLength: Endpoint Descriptor size */
|
||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||
0x00, /* bDescriptorSubtype: Header Func Desc */
|
||||
0x10, /* bcdCDC: spec release number */
|
||||
0x01,
|
||||
|
||||
/*Call Management Functional Descriptor*/
|
||||
0x05, /* bFunctionLength */
|
||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||
0x01, /* bDescriptorSubtype: Call Management Func Desc */
|
||||
0x00, /* bmCapabilities: D0+D1 */
|
||||
/*!*/ CDC_SLAVE_INTF_NUM, /* bDataInterface: 0 */
|
||||
|
||||
/*Interface Descriptor */
|
||||
0x09, /* bLength: Interface Descriptor size */
|
||||
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
|
||||
/* Interface descriptor type */
|
||||
/*!*/ CDC_INTF_NUM, /* bInterfaceNumber: Number of Interface */
|
||||
0x00, /* bAlternateSetting: Alternate setting */
|
||||
0x03, /* bNumEndpoints: 3 endpoints used */
|
||||
0x02, /* bInterfaceClass: Communication Interface Class */
|
||||
0x02, /* bInterfaceSubClass: Abstract Control Model */
|
||||
0x00, /* bInterfaceProtocol: Common AT commands */
|
||||
0x00, /* iInterface: */
|
||||
/*ACM Functional Descriptor*/
|
||||
0x04, /* bFunctionLength */
|
||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||
0x02, /* bDescriptorSubtype: Abstract Control Management desc */
|
||||
0x02, /* bmCapabilities */
|
||||
|
||||
/*Header Functional Descriptor*/
|
||||
0x05, /* bLength: Endpoint Descriptor size */
|
||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||
0x00, /* bDescriptorSubtype: Header Func Desc */
|
||||
0x10, /* bcdCDC: spec release number */
|
||||
0x01,
|
||||
/*Union Functional Descriptor*/
|
||||
0x05, /* bFunctionLength */
|
||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||
0x06, /* bDescriptorSubtype: Union func desc */
|
||||
/*!*/ CDC_MASTER_INTF_NUM, /* bMasterInterface: Communication class interface */
|
||||
/*!*/ CDC_SLAVE_INTF_NUM, /* bSlaveInterface0: Data Class Interface */
|
||||
|
||||
/*Call Management Functional Descriptor*/
|
||||
0x05, /* bFunctionLength */
|
||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||
0x01, /* bDescriptorSubtype: Call Management Func Desc */
|
||||
0x00, /* bmCapabilities: D0+D1 */
|
||||
/*!*/ CDC_INTF_NUM, /* bDataInterface: 0 */
|
||||
/* Control Endpoint Descriptor*/
|
||||
0x07, /* bLength: Endpoint Descriptor size */
|
||||
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
|
||||
CDC_CMD_EP, /* bEndpointAddress */
|
||||
0x03, /* bmAttributes: Interrupt */
|
||||
LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */
|
||||
HIBYTE(CDC_CMD_PACKET_SIZE),
|
||||
0x10, /* bInterval: */
|
||||
|
||||
/*ACM Functional Descriptor*/
|
||||
0x04, /* bFunctionLength */
|
||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||
0x02, /* bDescriptorSubtype: Abstract Control Management desc */
|
||||
0x02, /* bmCapabilities */
|
||||
/* Interface descriptor */
|
||||
0x09, /* bLength */
|
||||
USB_DESC_TYPE_INTERFACE, /* bDescriptorType */
|
||||
CDC_SLAVE_INTF_NUM, /* bInterfaceNumber */
|
||||
0x00, /* bAlternateSetting */
|
||||
0x02, /* bNumEndpoints */
|
||||
0x0A, /* bInterfaceClass: Communication class data */
|
||||
0x00, /* bInterfaceSubClass */
|
||||
0x00, /* bInterfaceProtocol */
|
||||
0x00,
|
||||
|
||||
/*Union Functional Descriptor*/
|
||||
0x05, /* bFunctionLength */
|
||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||
0x06, /* bDescriptorSubtype: Union func desc */
|
||||
/*!*/ CDC_INTF_NUM, /* bMasterInterface: Communication class interface */
|
||||
/*!*/ CDC_INTF_NUM, /* bSlaveInterface0: Data Class Interface */
|
||||
/*Endpoint OUT Descriptor*/
|
||||
0x07, /* bLength: Endpoint Descriptor size */
|
||||
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
|
||||
CDC_OUT_EP, /* bEndpointAddress */
|
||||
0x02, /* bmAttributes: Bulk */
|
||||
LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
|
||||
HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
|
||||
0x00, /* bInterval: ignore for Bulk transfer */
|
||||
|
||||
/*Endpoint 2 Descriptor*/
|
||||
0x07, /* bLength: Endpoint Descriptor size */
|
||||
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
|
||||
CDC_CMD_EP, /* bEndpointAddress */
|
||||
0x03, /* bmAttributes: Interrupt */
|
||||
LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */
|
||||
HIBYTE(CDC_CMD_PACKET_SIZE),
|
||||
0x10, /* bInterval: */
|
||||
/*Endpoint IN Descriptor*/
|
||||
0x07, /* bLength: Endpoint Descriptor size */
|
||||
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
|
||||
CDC_IN_EP, /* bEndpointAddress */
|
||||
0x02, /* bmAttributes: Bulk */
|
||||
LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
|
||||
HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
|
||||
0x00, /* bInterval: ignore for Bulk transfer */
|
||||
|
||||
/*Endpoint OUT Descriptor*/
|
||||
0x07, /* bLength: Endpoint Descriptor size */
|
||||
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
|
||||
CDC_OUT_EP, /* bEndpointAddress */
|
||||
0x02, /* bmAttributes: Bulk */
|
||||
LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
|
||||
HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
|
||||
0x00, /* bInterval: ignore for Bulk transfer */
|
||||
4, /* Descriptor size */
|
||||
3, /* Descriptor type */
|
||||
0x09,
|
||||
0x04,
|
||||
#endif
|
||||
|
||||
/*Endpoint IN Descriptor*/
|
||||
0x07, /* bLength: Endpoint Descriptor size */
|
||||
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
|
||||
CDC_IN_EP, /* bEndpointAddress */
|
||||
0x02, /* bmAttributes: Bulk */
|
||||
LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
|
||||
HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
|
||||
0x00, /* bInterval: ignore for Bulk transfer */
|
||||
#ifdef ENABLE_CCID
|
||||
|
||||
/* CCID Interface Descriptor */
|
||||
9, /* bLength: Interface Descriptor size */
|
||||
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
|
||||
CCID_INTF_NUM, /* bInterfaceNumber: CCID Interface */
|
||||
0, /* Alternate setting for this interface */
|
||||
3, /* bNumEndpoints: Bulk-IN, Bulk-OUT, Intr-IN */
|
||||
0x0B, /* CCID class */
|
||||
0x00, /* CCID subclass */
|
||||
0x00, /* CCID protocol */
|
||||
0, /* string index for interface */
|
||||
|
||||
/* ICC Descriptor */
|
||||
54, /* bLength: */
|
||||
0x21, /* bDescriptorType: USBDESCR_ICC */
|
||||
0x10, 0x01, /* bcdCCID: revision 1.1 (of CCID) */
|
||||
0, /* bMaxSlotIndex: */
|
||||
1, /* bVoltageSupport: 5V-only */
|
||||
0x02, 0, 0, 0, /* dwProtocols: T=1 */
|
||||
0xa0, 0x0f, 0, 0, /* dwDefaultClock: 4000 */
|
||||
0xa0, 0x0f, 0, 0, /* dwMaximumClock: 4000 */
|
||||
0, /* bNumClockSupported: 0x00 */
|
||||
0x80, 0x25, 0, 0, /* dwDataRate: 9600 */
|
||||
0x80, 0x25, 0, 0, /* dwMaxDataRate: 9600 */
|
||||
0, /* bNumDataRateSupported: 0x00 */
|
||||
0xfe, 0, 0, 0, /* dwMaxIFSD: 254 */
|
||||
0, 0, 0, 0, /* dwSynchProtocols: 0 */
|
||||
0, 0, 0, 0, /* dwMechanical: 0 */
|
||||
0x7a, 0x04, 0x02, 0x00, /* dwFeatures:
|
||||
* Short and extended APDU level: 0x40000 ----
|
||||
* Short APDU level : 0x20000 *
|
||||
* (ICCD?) : 0x00800 ----
|
||||
* Automatic IFSD : 0x00400 *
|
||||
* NAD value other than 0x00 : 0x00200
|
||||
* Can set ICC in clock stop : 0x00100
|
||||
* Automatic PPS CUR : 0x00080
|
||||
* Automatic PPS PROP : 0x00040 *
|
||||
* Auto baud rate change : 0x00020 *
|
||||
* Auto clock change : 0x00010 *
|
||||
* Auto voltage selection : 0x00008 *
|
||||
* Auto activaction of ICC : 0x00004
|
||||
* Automatic conf. based on ATR : 0x00002 *
|
||||
*/
|
||||
0x0f, 0x01, 0, 0, /* dwMaxCCIDMessageLength: 271 */
|
||||
0xff, /* bClassGetResponse: 0xff */
|
||||
0x00, /* bClassEnvelope: 0 */
|
||||
0, 0, /* wLCDLayout: 0 */
|
||||
0, /* bPinSupport: No PIN pad */
|
||||
|
||||
1, /* bMaxCCIDBusySlots: 1 */
|
||||
/*Endpoint IN1 Descriptor*/
|
||||
7, /* bLength: Endpoint Descriptor size */
|
||||
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
|
||||
CCID_IN_EP, /* bEndpointAddress: (IN1) */
|
||||
0x02, /* bmAttributes: Bulk */
|
||||
CCID_DATA_PACKET_SIZE, 0x00, /* wMaxPacketSize: */
|
||||
0x00, /* bInterval */
|
||||
/*Endpoint OUT1 Descriptor*/
|
||||
7, /* bLength: Endpoint Descriptor size */
|
||||
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
|
||||
CCID_OUT_EP, /* bEndpointAddress: (OUT1) */
|
||||
0x02, /* bmAttributes: Bulk */
|
||||
CCID_DATA_PACKET_SIZE, 0x00, /* wMaxPacketSize: */
|
||||
0x00, /* bInterval */
|
||||
/*Endpoint IN2 Descriptor*/
|
||||
7, /* bLength: Endpoint Descriptor size */
|
||||
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
|
||||
CCID_CMD_EP, /* bEndpointAddress: (IN2) */
|
||||
0x03, /* bmAttributes: Interrupt */
|
||||
CCID_DATA_PACKET_SIZE, 0x00, /* wMaxPacketSize: 4 */
|
||||
0xFF, /* bInterval (255ms) */
|
||||
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
};
|
||||
|
||||
USBD_ClassTypeDef USBD_Composite =
|
||||
{
|
||||
USBD_Composite_Init,
|
||||
@ -189,32 +305,57 @@ USBD_ClassTypeDef USBD_Composite =
|
||||
USBD_Composite_GetDeviceQualifierDescriptor,
|
||||
};
|
||||
|
||||
static USBD_ClassTypeDef *USBD_Classes[MAX_CLASSES];
|
||||
static USBD_ClassTypeDef * USBD_Classes[MAX_CLASSES];
|
||||
|
||||
int in_endpoint_to_class[MAX_ENDPOINTS];
|
||||
|
||||
int out_endpoint_to_class[MAX_ENDPOINTS];
|
||||
|
||||
void USBD_Composite_Set_Classes(USBD_ClassTypeDef *class0, USBD_ClassTypeDef *class1) {
|
||||
USBD_Classes[0] = class0;
|
||||
USBD_Classes[1] = class1;
|
||||
void USBD_Composite_Set_Classes(USBD_ClassTypeDef *hid_class, USBD_ClassTypeDef *ccid_class, USBD_ClassTypeDef *cdc_class) {
|
||||
memset(USBD_Classes, 0 , sizeof(USBD_Classes));
|
||||
USBD_Classes[0] = hid_class;
|
||||
#ifdef ENABLE_CCID
|
||||
USBD_Classes[1] = ccid_class;
|
||||
#endif
|
||||
#if DEBUG_LEVEL > 0
|
||||
USBD_Classes[2] = cdc_class;
|
||||
#endif
|
||||
}
|
||||
|
||||
static USBD_ClassTypeDef * getClass(uint8_t index)
|
||||
{
|
||||
switch(index)
|
||||
{
|
||||
case HID_INTF_NUM:
|
||||
return USBD_Classes[0];
|
||||
#ifdef ENABLE_CCID
|
||||
case CCID_INTF_NUM:
|
||||
return USBD_Classes[1];
|
||||
#endif
|
||||
#if DEBUG_LEVEL > 0
|
||||
case CDC_MASTER_INTF_NUM:
|
||||
case CDC_SLAVE_INTF_NUM:
|
||||
return USBD_Classes[2];
|
||||
#endif
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static uint8_t USBD_Composite_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx) {
|
||||
int i;
|
||||
for(i = 0; i < NUM_INTERFACES; i++) {
|
||||
if (USBD_Classes[i]->Init(pdev, cfgidx) != USBD_OK) {
|
||||
for(i = 0; i < NUM_CLASSES; i++) {
|
||||
if (USBD_Classes[i] != NULL && USBD_Classes[i]->Init(pdev, cfgidx) != USBD_OK) {
|
||||
return USBD_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
//N
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
static uint8_t USBD_Composite_DeInit (USBD_HandleTypeDef *pdev, uint8_t cfgidx) {
|
||||
int i;
|
||||
for(i = 0; i < NUM_INTERFACES; i++) {
|
||||
if (USBD_Classes[i]->DeInit(pdev, cfgidx) != USBD_OK) {
|
||||
for(i = 0; i < NUM_CLASSES; i++) {
|
||||
if (USBD_Classes[i] != NULL && USBD_Classes[i]->DeInit(pdev, cfgidx) != USBD_OK) {
|
||||
return USBD_FAIL;
|
||||
}
|
||||
}
|
||||
@ -224,10 +365,13 @@ static uint8_t USBD_Composite_DeInit (USBD_HandleTypeDef *pdev, uint8_t cfgidx)
|
||||
|
||||
static uint8_t USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) {
|
||||
int i;
|
||||
USBD_ClassTypeDef * device_class;
|
||||
device_class = getClass(req->wIndex);
|
||||
|
||||
switch (req->bmRequest & USB_REQ_TYPE_MASK) {
|
||||
case USB_REQ_TYPE_CLASS :
|
||||
if (req->wIndex < NUM_INTERFACES)
|
||||
return USBD_Classes[req->wIndex]->Setup(pdev, req);
|
||||
if (device_class != NULL)
|
||||
return device_class->Setup(pdev, req);
|
||||
else
|
||||
return USBD_FAIL;
|
||||
|
||||
@ -236,8 +380,8 @@ static uint8_t USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqType
|
||||
switch (req->bRequest) {
|
||||
|
||||
case USB_REQ_GET_DESCRIPTOR :
|
||||
for(i = 0; i < NUM_INTERFACES; i++) {
|
||||
if (USBD_Classes[i]->Setup(pdev, req) != USBD_OK) {
|
||||
for(i = 0; i < NUM_CLASSES; i++) {
|
||||
if (USBD_Classes[i] != NULL && USBD_Classes[i]->Setup(pdev, req) != USBD_OK) {
|
||||
return USBD_FAIL;
|
||||
}
|
||||
}
|
||||
@ -246,8 +390,8 @@ static uint8_t USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqType
|
||||
|
||||
case USB_REQ_GET_INTERFACE :
|
||||
case USB_REQ_SET_INTERFACE :
|
||||
if (req->wIndex < NUM_INTERFACES)
|
||||
return USBD_Classes[req->wIndex]->Setup(pdev, req);
|
||||
if (device_class != NULL)
|
||||
return device_class->Setup(pdev, req);
|
||||
else
|
||||
return USBD_FAIL;
|
||||
}
|
||||
@ -260,6 +404,8 @@ static uint8_t USBD_Composite_DataIn (USBD_HandleTypeDef *pdev, uint8_t epnum) {
|
||||
|
||||
i = in_endpoint_to_class[epnum];
|
||||
|
||||
if (USBD_Classes[i] == NULL) return USBD_FAIL;
|
||||
|
||||
return USBD_Classes[i]->DataIn(pdev, epnum);
|
||||
}
|
||||
|
||||
@ -268,14 +414,16 @@ static uint8_t USBD_Composite_DataOut (USBD_HandleTypeDef *pdev, uint8_t epnum)
|
||||
|
||||
i = out_endpoint_to_class[epnum];
|
||||
|
||||
if (USBD_Classes[i] == NULL) return USBD_FAIL;
|
||||
|
||||
return USBD_Classes[i]->DataOut(pdev, epnum);
|
||||
|
||||
}
|
||||
|
||||
static uint8_t USBD_Composite_EP0_RxReady (USBD_HandleTypeDef *pdev) {
|
||||
int i;
|
||||
for(i = 0; i < NUM_INTERFACES; i++) {
|
||||
if (USBD_Classes[i]->EP0_RxReady != NULL) {
|
||||
for(i = 0; i < NUM_CLASSES; i++) {
|
||||
if (USBD_Classes[i] != NULL && USBD_Classes[i]->EP0_RxReady != NULL) {
|
||||
if (USBD_Classes[i]->EP0_RxReady(pdev) != USBD_OK) {
|
||||
return USBD_FAIL;
|
||||
}
|
||||
@ -285,16 +433,19 @@ static uint8_t USBD_Composite_EP0_RxReady (USBD_HandleTypeDef *pdev) {
|
||||
}
|
||||
|
||||
static uint8_t *USBD_Composite_GetFSCfgDesc (uint16_t *length) {
|
||||
//Y
|
||||
*length = COMPOSITE_CDC_HID_DESCRIPTOR_SIZE;
|
||||
return COMPOSITE_CDC_HID_DESCRIPTOR;
|
||||
}
|
||||
|
||||
static uint8_t *USBD_Composite_GetHSCfgDesc (uint16_t *length) {
|
||||
//N
|
||||
*length = COMPOSITE_CDC_HID_DESCRIPTOR_SIZE;
|
||||
return COMPOSITE_CDC_HID_DESCRIPTOR;
|
||||
}
|
||||
|
||||
static uint8_t *USBD_Composite_GetOtherSpeedCfgDesc (uint16_t *length) {
|
||||
|
||||
*length = COMPOSITE_CDC_HID_DESCRIPTOR_SIZE;
|
||||
return COMPOSITE_CDC_HID_DESCRIPTOR;
|
||||
}
|
||||
@ -315,6 +466,7 @@ __ALIGN_BEGIN static uint8_t USBD_Composite_DeviceQualifierDesc[USB_LEN_DEV_QUAL
|
||||
};
|
||||
|
||||
uint8_t *USBD_Composite_GetDeviceQualifierDescriptor (uint16_t *length) {
|
||||
*length = sizeof (USBD_Composite_DeviceQualifierDesc);
|
||||
return USBD_Composite_DeviceQualifierDesc;
|
||||
//N
|
||||
*length = sizeof (USBD_Composite_DeviceQualifierDesc);
|
||||
return USBD_Composite_DeviceQualifierDesc;
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ extern int in_endpoint_to_class[MAX_ENDPOINTS];
|
||||
|
||||
extern int out_endpoint_to_class[MAX_ENDPOINTS];
|
||||
|
||||
void USBD_Composite_Set_Classes(USBD_ClassTypeDef *class0, USBD_ClassTypeDef *class1);
|
||||
void USBD_Composite_Set_Classes(USBD_ClassTypeDef *class0, USBD_ClassTypeDef *class1, USBD_ClassTypeDef *class2);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -50,6 +50,9 @@
|
||||
#include "stm32l4xx_hal.h"
|
||||
#include "usbd_core.h"
|
||||
#include "usbd_hid.h"
|
||||
#include "usbd_cdc.h"
|
||||
#include "usbd_ccid.h"
|
||||
#include "log.h"
|
||||
|
||||
void SystemClock_Config(void);
|
||||
|
||||
@ -117,9 +120,14 @@ void HAL_PCD_DataOutStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
|
||||
USBD_LL_DataOutStage((USBD_HandleTypeDef*)hpcd->pData, epnum, hpcd->OUT_ep[epnum].xfer_buff);
|
||||
switch(epnum)
|
||||
{
|
||||
case HID_ENDPOINT:
|
||||
case HID_EPOUT_ADDR:
|
||||
usb_hid_recieve_callback(epnum);
|
||||
break;
|
||||
#ifdef ENABLE_CCID
|
||||
case CCID_OUT_EP:
|
||||
usb_ccid_recieve_callback((USBD_HandleTypeDef*)hpcd->pData, epnum);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -218,7 +226,6 @@ void HAL_PCD_DisconnectCallback(PCD_HandleTypeDef *hpcd)
|
||||
{
|
||||
USBD_LL_DevDisconnected((USBD_HandleTypeDef*)hpcd->pData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes the low level portion of the device driver.
|
||||
* @param pdev: Device handle
|
||||
@ -252,14 +259,20 @@ USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev)
|
||||
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x80 , PCD_SNG_BUF, 0x58);
|
||||
|
||||
// HID
|
||||
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x01 , PCD_SNG_BUF, 0x98);
|
||||
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x81 , PCD_SNG_BUF, 0xd8);
|
||||
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , HID_EPOUT_ADDR , PCD_SNG_BUF, 0x98);
|
||||
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , HID_EPIN_ADDR , PCD_SNG_BUF, 0xd8);
|
||||
|
||||
// CCID
|
||||
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CCID_OUT_EP , PCD_SNG_BUF, 0xd8 + 64); // data OUT
|
||||
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CCID_IN_EP , PCD_SNG_BUF, 0xd8 + 64*2); // data IN
|
||||
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CCID_CMD_EP , PCD_SNG_BUF, 0xd8 + 64*3); // commands
|
||||
|
||||
// CDC / uart
|
||||
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x02 , PCD_SNG_BUF, 0xd8 + 64); // data OUT
|
||||
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x82 , PCD_SNG_BUF, 0xd8 + 64*2); // data IN
|
||||
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x83 , PCD_SNG_BUF, 0xd8 + 64*3); // commands
|
||||
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_CMD_EP , PCD_SNG_BUF, 0xd8 + 64*4); // commands
|
||||
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_OUT_EP , PCD_SNG_BUF, 0xd8 + 64*5); // data OUT
|
||||
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_IN_EP , PCD_SNG_BUF, 0xd8 + 64*6); // data IN
|
||||
|
||||
// dump_pma_header("usbd_conf");
|
||||
|
||||
return USBD_OK;
|
||||
}
|
||||
@ -310,6 +323,7 @@ USBD_StatusTypeDef USBD_LL_OpenEP(USBD_HandleTypeDef *pdev,
|
||||
uint8_t ep_type,
|
||||
uint16_t ep_mps)
|
||||
{
|
||||
// printf1(TAG_RED,"LL_Open. ep: %x, %x\r\n", ep_addr, ep_type);
|
||||
HAL_PCD_EP_Open((PCD_HandleTypeDef*) pdev->pData,
|
||||
ep_addr,
|
||||
ep_mps,
|
||||
|
@ -342,6 +342,7 @@ static uint8_t USBD_HID_Setup (USBD_HandleTypeDef *pdev,
|
||||
uint8_t *pbuf = NULL;
|
||||
uint16_t status_info = 0U;
|
||||
USBD_StatusTypeDef ret = USBD_OK;
|
||||
req->wLength = req->wLength & 0x7f;
|
||||
|
||||
switch (req->bmRequest & USB_REQ_TYPE_MASK)
|
||||
{
|
||||
@ -386,6 +387,7 @@ static uint8_t USBD_HID_Setup (USBD_HandleTypeDef *pdev,
|
||||
break;
|
||||
|
||||
case USB_REQ_GET_DESCRIPTOR:
|
||||
req->wLength = req->wLength & 0x7f;
|
||||
if(req->wValue >> 8 == HID_REPORT_DESC)
|
||||
{
|
||||
len = MIN(HID_FIDO_REPORT_DESC_SIZE , req->wLength);
|
||||
|
@ -12,9 +12,17 @@ _estack = 0x2000c000;
|
||||
|
||||
_MIN_STACK_SIZE = 0x400;
|
||||
|
||||
/*
|
||||
flash_cfg is for storing bootloader data, like last used firmware version.
|
||||
bootloader_configuration should be equal to (APPLICATION_END_PAGE) page address, from targets/stm32l432/src/memory_layout.h:30; and equal to flash_cfg origin
|
||||
*/
|
||||
|
||||
bootloader_configuration = 0x08000000 + 216*1024+8;
|
||||
|
||||
MEMORY
|
||||
{
|
||||
flash (rx) : ORIGIN = 0x08000000, LENGTH = 20K
|
||||
flash_cfg (rx) : ORIGIN = 0x08000000 + 216*1024+8, LENGTH = 2K-8
|
||||
ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
|
||||
sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
|
||||
}
|
||||
@ -39,6 +47,11 @@ SECTIONS
|
||||
_etext = .;
|
||||
} >flash
|
||||
|
||||
.flag2 bootloader_configuration :
|
||||
{
|
||||
KEEP(*(.flag2)) ;
|
||||
} > flash_cfg
|
||||
|
||||
_sidata = LOADADDR(.data);
|
||||
|
||||
.data :
|
||||
|
@ -12,9 +12,17 @@ _estack = 0x2000c000;
|
||||
|
||||
_MIN_STACK_SIZE = 0x400;
|
||||
|
||||
/*
|
||||
flash_cfg is for storing bootloader data, like last used firmware version.
|
||||
bootloader_configuration should be equal to (APPLICATION_END_PAGE) page address, from targets/stm32l432/src/memory_layout.h:30; and equal to flash_cfg origin
|
||||
*/
|
||||
|
||||
bootloader_configuration = 0x08000000 + 216*1024+8;
|
||||
|
||||
MEMORY
|
||||
{
|
||||
flash (rx) : ORIGIN = 0x08000000, LENGTH = 32K
|
||||
flash_cfg (rx) : ORIGIN = 0x08000000 + 216*1024+8, LENGTH = 2K-8
|
||||
ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
|
||||
sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
|
||||
}
|
||||
@ -39,6 +47,11 @@ SECTIONS
|
||||
_etext = .;
|
||||
} >flash
|
||||
|
||||
.flag2 bootloader_configuration :
|
||||
{
|
||||
KEEP(*(.flag2)) ;
|
||||
} > flash_cfg
|
||||
|
||||
_sidata = LOADADDR(.data);
|
||||
|
||||
.data :
|
||||
|
@ -13,14 +13,21 @@ _estack = 0x2000c000;
|
||||
_MIN_STACK_SIZE = 0x400;
|
||||
|
||||
/*
|
||||
Memory layout of device:
|
||||
20 KB 198KB-8 38 KB
|
||||
| bootloader | application | secrets/data |
|
||||
len | 20 KB/10p| 196KB-8-8/98p | 2kB/1p | 38 KB/19p |
|
||||
pos | 0->20 KB | 20->216KB-8-8 | 216kB -> 218 kB | 218->256 KB |
|
||||
posp | 0-10 | 10-113 | 113-114 | 113-128 |
|
||||
desc | bootloader | application | bootloader data | secrets/data |
|
||||
|
||||
Last 8 bytes in application space are occupied by bootloader flags - app
|
||||
authorization and bootloader activation flag.
|
||||
*/
|
||||
|
||||
/* Current firmware version number is concatenated to the firmware code - see .flag marker */
|
||||
/* flash length is (APPLICATION_END_PAGE-20*1024), where 20K is bootloader */
|
||||
|
||||
MEMORY
|
||||
{
|
||||
flash (rx) : ORIGIN = 0x08005000, LENGTH = 198K - 8
|
||||
flash (rx) : ORIGIN = 0x08000000 + 20K, LENGTH = 216K - 20K - 8
|
||||
ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
|
||||
sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
|
||||
}
|
||||
@ -54,7 +61,13 @@ SECTIONS
|
||||
*(.data*)
|
||||
. = ALIGN(8);
|
||||
_edata = .;
|
||||
} >ram AT> flash
|
||||
} >sram2 AT> flash
|
||||
|
||||
.flag :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
KEEP(*(.flag)) ;
|
||||
} > flash
|
||||
|
||||
.bss :
|
||||
{
|
||||
|
@ -12,9 +12,22 @@ _estack = 0x2000c000;
|
||||
|
||||
_MIN_STACK_SIZE = 0x400;
|
||||
|
||||
/*
|
||||
len | 32 KB/16p| 184KB-8-8/92p | 2kB/1p | 38 KB/19p |
|
||||
pos | 0->32 KB | 32->216KB-8-8 | 216kB -> 218 kB | 218->256 KB |
|
||||
posp | 0-16 | 16-113 | 113-114 | 113-128 |
|
||||
desc | bootloader | application | bootloader data | secrets/data |
|
||||
|
||||
Last 8 bytes in application space are occupied by bootloader flags - app
|
||||
authorization and bootloader activation flag.
|
||||
*/
|
||||
|
||||
/* Current firmware version number is concatenated to the firmware code - see .flag marker */
|
||||
/* flash length is (APPLICATION_END_PAGE-20*1024), where 20K is bootloader */
|
||||
|
||||
MEMORY
|
||||
{
|
||||
flash (rx) : ORIGIN = 0x08008000, LENGTH = 186K - 8
|
||||
flash (rx) : ORIGIN = 0x08000000 + 20K + 12K, LENGTH = 216K - 20K - 12K - 8
|
||||
ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
|
||||
sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
|
||||
}
|
||||
@ -50,6 +63,12 @@ SECTIONS
|
||||
_edata = .;
|
||||
} >ram AT> flash
|
||||
|
||||
.flag :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
KEEP(*(.flag)) ;
|
||||
} > flash
|
||||
|
||||
.bss :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
|
@ -8,21 +8,25 @@
|
||||
#include "device.h"
|
||||
#include "nfc.h"
|
||||
|
||||
static void flush_rx()
|
||||
static void flush_rx(void)
|
||||
{
|
||||
while(LL_SPI_IsActiveFlag_RXNE(SPI1) != 0)
|
||||
{
|
||||
LL_SPI_ReceiveData8(SPI1);
|
||||
}
|
||||
}
|
||||
static void wait_for_tx()
|
||||
|
||||
|
||||
static void wait_for_tx(void)
|
||||
{
|
||||
// while (LL_SPI_IsActiveFlag_BSY(SPI1) == 1)
|
||||
// ;
|
||||
while(LL_SPI_GetTxFIFOLevel(SPI1) != LL_SPI_TX_FIFO_EMPTY)
|
||||
;
|
||||
}
|
||||
static void wait_for_rx()
|
||||
|
||||
|
||||
static void wait_for_rx(void)
|
||||
{
|
||||
while(LL_SPI_IsActiveFlag_RXNE(SPI1) == 0)
|
||||
;
|
||||
@ -270,7 +274,7 @@ void ams_print_int1(uint8_t int0)
|
||||
#endif
|
||||
}
|
||||
|
||||
int ams_init()
|
||||
int ams_init(void)
|
||||
{
|
||||
LL_GPIO_SetPinMode(SOLO_AMS_CS_PORT,SOLO_AMS_CS_PIN,LL_GPIO_MODE_OUTPUT);
|
||||
LL_GPIO_SetOutputPin(SOLO_AMS_CS_PORT,SOLO_AMS_CS_PIN);
|
||||
@ -292,7 +296,7 @@ int ams_init()
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ams_configure()
|
||||
void ams_configure(void)
|
||||
{
|
||||
// Should not be used during passive operation.
|
||||
uint8_t block[4];
|
||||
|
@ -39,8 +39,8 @@ typedef union
|
||||
#define SELECT() LL_GPIO_ResetOutputPin(SOLO_AMS_CS_PORT,SOLO_AMS_CS_PIN)
|
||||
#define UNSELECT() LL_GPIO_SetOutputPin(SOLO_AMS_CS_PORT,SOLO_AMS_CS_PIN)
|
||||
|
||||
int ams_init();
|
||||
void ams_configure();
|
||||
int ams_init(void);
|
||||
void ams_configure(void);
|
||||
|
||||
void ams_read_buffer(uint8_t * data, int len);
|
||||
void ams_write_buffer(uint8_t * data, int len);
|
||||
|
@ -8,13 +8,20 @@
|
||||
#define _APP_H_
|
||||
#include <stdint.h>
|
||||
#include "version.h"
|
||||
#include "solo.h"
|
||||
|
||||
#define SOLO
|
||||
|
||||
#define DEBUG_UART USART1
|
||||
|
||||
#ifndef DEBUG_LEVEL
|
||||
// Enable the CDC ACM USB interface & debug logs (DEBUG_LEVEL > 0)
|
||||
#define DEBUG_LEVEL 0
|
||||
#endif
|
||||
|
||||
// Enable the CCID USB interface
|
||||
// #define ENABLE_CCID
|
||||
|
||||
#define NON_BLOCK_PRINTING 0
|
||||
|
||||
|
||||
@ -31,7 +38,7 @@
|
||||
// #define DISABLE_CTAPHID_WINK
|
||||
// #define DISABLE_CTAPHID_CBOR
|
||||
|
||||
#define ENABLE_SERIAL_PRINTING
|
||||
// #define ENABLE_SERIAL_PRINTING
|
||||
|
||||
#if defined(SOLO_HACKER)
|
||||
#define SOLO_PRODUCT_NAME "Solo Hacker " SOLO_VERSION
|
||||
@ -42,6 +49,9 @@
|
||||
void printing_init();
|
||||
void hw_init(int lf);
|
||||
|
||||
// Return 1 if Solo is secure/locked.
|
||||
int solo_is_locked();
|
||||
|
||||
//#define TEST
|
||||
//#define TEST_POWER
|
||||
|
||||
|
@ -5,12 +5,16 @@
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "crypto.h"
|
||||
#include "memory_layout.h"
|
||||
#include "device.h"
|
||||
#include "sense.h"
|
||||
#include "log.h"
|
||||
|
||||
#ifdef USE_SOLOKEYS_CERT
|
||||
|
||||
const uint8_t attestation_cert_der[] =
|
||||
"\x30\x82\x02\xe1\x30\x82\x02\x88\xa0\x03\x02\x01\x02\x02\x01\x01\x30\x0a\x06\x08"
|
||||
const uint8_t attestation_solo_cert_der[] =
|
||||
"\x30\x82\x03\x03\x30\x82\x02\xaa\xa0\x03\x02\x01\x02\x02\x01\x01\x30\x0a\x06\x08"
|
||||
"\x2a\x86\x48\xce\x3d\x04\x03\x02\x30\x81\x80\x31\x0b\x30\x09\x06\x03\x55\x04\x06"
|
||||
"\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08\x4d\x61\x72\x79\x6c"
|
||||
"\x61\x6e\x64\x31\x12\x30\x10\x06\x03\x55\x04\x0a\x0c\x09\x53\x6f\x6c\x6f\x20\x4b"
|
||||
@ -18,42 +22,40 @@ const uint8_t attestation_cert_der[] =
|
||||
"\x41\x31\x15\x30\x13\x06\x03\x55\x04\x03\x0c\x0c\x73\x6f\x6c\x6f\x6b\x65\x79\x73"
|
||||
"\x2e\x63\x6f\x6d\x31\x21\x30\x1f\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01\x16"
|
||||
"\x12\x68\x65\x6c\x6c\x6f\x40\x73\x6f\x6c\x6f\x6b\x65\x79\x73\x2e\x63\x6f\x6d\x30"
|
||||
"\x20\x17\x0d\x31\x38\x31\x31\x31\x31\x31\x32\x35\x32\x30\x30\x5a\x18\x0f\x32\x30"
|
||||
"\x36\x38\x31\x30\x32\x39\x31\x32\x35\x32\x30\x30\x5a\x30\x81\x92\x31\x0b\x30\x09"
|
||||
"\x20\x17\x0d\x31\x39\x31\x32\x30\x31\x31\x39\x32\x33\x34\x39\x5a\x18\x0f\x32\x30"
|
||||
"\x36\x39\x31\x31\x31\x38\x31\x39\x32\x33\x34\x39\x5a\x30\x81\x91\x31\x0b\x30\x09"
|
||||
"\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08"
|
||||
"\x4d\x61\x72\x79\x6c\x61\x6e\x64\x31\x12\x30\x10\x06\x03\x55\x04\x0a\x0c\x09\x53"
|
||||
"\x6f\x6c\x6f\x20\x4b\x65\x79\x73\x31\x22\x30\x20\x06\x03\x55\x04\x0b\x0c\x19\x41"
|
||||
"\x75\x74\x68\x65\x6e\x74\x69\x63\x61\x74\x6f\x72\x20\x41\x74\x74\x65\x73\x74\x61"
|
||||
"\x74\x69\x6f\x6e\x31\x15\x30\x13\x06\x03\x55\x04\x03\x0c\x0c\x73\x6f\x6c\x6f\x6b"
|
||||
"\x65\x79\x73\x2e\x63\x6f\x6d\x31\x21\x30\x1f\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01"
|
||||
"\x09\x01\x16\x12\x68\x65\x6c\x6c\x6f\x40\x73\x6f\x6c\x6f\x6b\x65\x79\x73\x2e\x63"
|
||||
"\x6f\x6d\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48"
|
||||
"\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x22\xfe\x0f\xb5\x2a\x78\xbe\xc6\x45\x37\x1a"
|
||||
"\x28\xa7\x57\x43\x49\xa4\x6f\x85\x4d\xca\x4e\x25\x1c\x9f\x75\x30\x3d\xbf\x10\xd5"
|
||||
"\xd2\xd2\x0b\xb9\x69\x2c\xdd\xb2\x5c\x14\xd8\x39\x85\x12\xf6\x23\xee\x91\xba\xc6"
|
||||
"\xac\xff\x4a\x1a\x27\xef\xe0\xc1\x54\x3f\xd4\xd9\xc5\xa3\x81\xdc\x30\x81\xd9\x30"
|
||||
"\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x3b\xe6\xd2\xc0\x6f\xf2\xe7\xb0\x7c\x9d"
|
||||
"\x9e\x28\xc0\x20\xb0\x0d\x07\xc8\x15\xc8\x30\x81\x9f\x06\x03\x55\x1d\x23\x04\x81"
|
||||
"\x97\x30\x81\x94\xa1\x81\x86\xa4\x81\x83\x30\x81\x80\x31\x0b\x30\x09\x06\x03\x55"
|
||||
"\x04\x06\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08\x4d\x61\x72"
|
||||
"\x79\x6c\x61\x6e\x64\x31\x12\x30\x10\x06\x03\x55\x04\x0a\x0c\x09\x53\x6f\x6c\x6f"
|
||||
"\x20\x4b\x65\x79\x73\x31\x10\x30\x0e\x06\x03\x55\x04\x0b\x0c\x07\x52\x6f\x6f\x74"
|
||||
"\x20\x43\x41\x31\x15\x30\x13\x06\x03\x55\x04\x03\x0c\x0c\x73\x6f\x6c\x6f\x6b\x65"
|
||||
"\x4d\x61\x72\x79\x6c\x61\x6e\x64\x31\x11\x30\x0f\x06\x03\x55\x04\x0a\x0c\x08\x53"
|
||||
"\x6f\x6c\x6f\x4b\x65\x79\x73\x31\x22\x30\x20\x06\x03\x55\x04\x0b\x0c\x19\x41\x75"
|
||||
"\x74\x68\x65\x6e\x74\x69\x63\x61\x74\x6f\x72\x20\x41\x74\x74\x65\x73\x74\x61\x74"
|
||||
"\x69\x6f\x6e\x31\x15\x30\x13\x06\x03\x55\x04\x03\x0c\x0c\x73\x6f\x6c\x6f\x6b\x65"
|
||||
"\x79\x73\x2e\x63\x6f\x6d\x31\x21\x30\x1f\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09"
|
||||
"\x01\x16\x12\x68\x65\x6c\x6c\x6f\x40\x73\x6f\x6c\x6f\x6b\x65\x79\x73\x2e\x63\x6f"
|
||||
"\x6d\x82\x09\x00\xc4\x47\x63\x92\x8f\xf4\xbe\x8c\x30\x09\x06\x03\x55\x1d\x13\x04"
|
||||
"\x02\x30\x00\x30\x0b\x06\x03\x55\x1d\x0f\x04\x04\x03\x02\x04\xf0\x30\x0a\x06\x08"
|
||||
"\x2a\x86\x48\xce\x3d\x04\x03\x02\x03\x47\x00\x30\x44\x02\x20\x71\x10\x46\x2c\xf5"
|
||||
"\x16\x18\x97\x55\xca\x64\x50\x3b\x69\xb2\xdf\x17\x71\xab\xad\x8e\xc0\xd6\xa6\x07"
|
||||
"\x3d\x66\x8a\x3b\xbb\xfe\x61\x02\x20\x1e\x82\xef\xeb\x5e\x4e\x3a\x00\x84\x64\xd2"
|
||||
"\xf8\x84\xc3\x78\x35\x93\x63\x81\x2e\xbe\xa6\x12\x32\x6e\x29\x90\xc8\x91\x4b\x71"
|
||||
"\x52"
|
||||
"\x6d\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce"
|
||||
"\x3d\x03\x01\x07\x03\x42\x00\x04\x22\xfe\x0f\xb5\x2a\x78\xbe\xc6\x45\x37\x1a\x28"
|
||||
"\xa7\x57\x43\x49\xa4\x6f\x85\x4d\xca\x4e\x25\x1c\x9f\x75\x30\x3d\xbf\x10\xd5\xd2"
|
||||
"\xd2\x0b\xb9\x69\x2c\xdd\xb2\x5c\x14\xd8\x39\x85\x12\xf6\x23\xee\x91\xba\xc6\xac"
|
||||
"\xff\x4a\x1a\x27\xef\xe0\xc1\x54\x3f\xd4\xd9\xc5\xa3\x81\xff\x30\x81\xfc\x30\x1d"
|
||||
"\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x3b\xe6\xd2\xc0\x6f\xf2\xe7\xb0\x7c\x9d\x9e"
|
||||
"\x28\xc0\x20\xb0\x0d\x07\xc8\x15\xc8\x30\x81\x9f\x06\x03\x55\x1d\x23\x04\x81\x97"
|
||||
"\x30\x81\x94\xa1\x81\x86\xa4\x81\x83\x30\x81\x80\x31\x0b\x30\x09\x06\x03\x55\x04"
|
||||
"\x06\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08\x4d\x61\x72\x79"
|
||||
"\x6c\x61\x6e\x64\x31\x12\x30\x10\x06\x03\x55\x04\x0a\x0c\x09\x53\x6f\x6c\x6f\x20"
|
||||
"\x4b\x65\x79\x73\x31\x10\x30\x0e\x06\x03\x55\x04\x0b\x0c\x07\x52\x6f\x6f\x74\x20"
|
||||
"\x43\x41\x31\x15\x30\x13\x06\x03\x55\x04\x03\x0c\x0c\x73\x6f\x6c\x6f\x6b\x65\x79"
|
||||
"\x73\x2e\x63\x6f\x6d\x31\x21\x30\x1f\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01"
|
||||
"\x16\x12\x68\x65\x6c\x6c\x6f\x40\x73\x6f\x6c\x6f\x6b\x65\x79\x73\x2e\x63\x6f\x6d"
|
||||
"\x82\x09\x00\xc4\x47\x63\x92\x8f\xf4\xbe\x8c\x30\x09\x06\x03\x55\x1d\x13\x04\x02"
|
||||
"\x30\x00\x30\x0b\x06\x03\x55\x1d\x0f\x04\x04\x03\x02\x04\xf0\x30\x21\x06\x0b\x2b"
|
||||
"\x06\x01\x04\x01\x82\xe5\x1c\x01\x01\x04\x04\x12\x04\x10\x88\x76\x63\x1b\xd4\xa0"
|
||||
"\x42\x7f\x57\x73\x0e\xc7\x1c\x9e\x02\x79\x30\x0a\x06\x08\x2a\x86\x48\xce\x3d\x04"
|
||||
"\x03\x02\x03\x47\x00\x30\x44\x02\x20\x4d\xea\x09\x15\x6c\x86\x48\x57\x2a\xa8\x8d"
|
||||
"\x87\xc3\xfa\xb6\x6b\x29\x9b\xfb\x8b\x4d\x4d\x29\x77\x5b\xa1\x04\x4c\x7f\x12\x8d"
|
||||
"\x71\x02\x20\x47\x4c\x3d\xb2\xa1\x74\xe3\x9c\xfe\xe1\x23\xbf\xec\x47\x96\xf4\xe5"
|
||||
"\x9b\x65\x76\xac\xc8\x69\x1d\xe2\x74\xff\x4e\xa4\xcf\x02\x6d"
|
||||
;
|
||||
#else
|
||||
|
||||
// For testing/development only
|
||||
|
||||
const uint8_t attestation_cert_der[] =
|
||||
const uint8_t attestation_hacker_cert_der[] =
|
||||
"\x30\x82\x02\xe9\x30\x82\x02\x8e\xa0\x03\x02\x01\x02\x02\x01\x01\x30\x0a\x06\x08"
|
||||
"\x2a\x86\x48\xce\x3d\x04\x03\x02\x30\x81\x82\x31\x0b\x30\x09\x06\x03\x55\x04\x06"
|
||||
"\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08\x4d\x61\x72\x79\x6c"
|
||||
@ -94,8 +96,36 @@ const uint8_t attestation_cert_der[] =
|
||||
"\xf3\x87\x61\x82\xd8\xcd\x48\xfc\x57"
|
||||
;
|
||||
|
||||
#endif
|
||||
|
||||
const uint16_t attestation_cert_der_size = sizeof(attestation_cert_der)-1;
|
||||
const uint16_t attestation_solo_cert_der_size = sizeof(attestation_solo_cert_der)-1;
|
||||
const uint16_t attestation_hacker_cert_der_size = sizeof(attestation_hacker_cert_der)-1;
|
||||
|
||||
const uint16_t attestation_key_size = 32;
|
||||
|
||||
uint8_t * device_get_attestation_key(){
|
||||
flash_attestation_page * page =(flash_attestation_page *)ATTESTATION_PAGE_ADDR;
|
||||
return page->attestation_key;
|
||||
}
|
||||
|
||||
uint16_t device_attestation_cert_der_get_size(){
|
||||
uint16_t sz = (uint16_t)((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->attestation_cert_size;
|
||||
return sz;
|
||||
}
|
||||
|
||||
void device_attestation_read_cert_der(uint8_t * dst){
|
||||
const uint8_t * der = ((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->attestation_cert;
|
||||
uint16_t sz = device_attestation_cert_der_get_size();
|
||||
memmove(dst, der, sz);
|
||||
|
||||
// Overwrite respective x509 fields if Tap or Somu.
|
||||
if (memcmp(dst + 0x2c6, "\xea\x09\x15\x6c\x86\x48\x57\x2a\xa8\x8d", 10) == 0){
|
||||
if (device_is_nfc()){
|
||||
dst[0x2a2] = 0x89;//tap aaguid byte
|
||||
memmove(dst + 0xac, "\x34\x33\x38\x5a\x18\x0f\x32\x30\x36\x39\x31\x31\x31\x38\x31\x39\x32\x34\x33\x38", 20);//tap-id
|
||||
memmove(dst + 0x2c5, "\x6d\x7b\x41\x2b\xff\x57\xf0\x03\xbd\x5b\x39\x4a\xf7\xa9\x2d\x6d\xcb\x9e\x2d\x88\xbf\xb3\x93\xc5\x66\x3b\xd1\xbc\x34\xfa\x5c\x4c\x02\x20\x59\x01\x49\x39\x1b\xb7\xa9\x1c\xed\x49\x78\x4f\x92\xa9\x61\x14\xa5\x6e\x96\x3f\x29\x02\x93\xe0\x5d\xe2\x75\xd0\x60\xd9\x74\xc2", 66);//tap-sig
|
||||
} else if (tsc_sensor_exists()) {
|
||||
dst[0x2a2] = 0x98;//somu aaguid byte
|
||||
memmove(dst + 0xac, "\x35\x30\x32\x5a\x18\x0f\x32\x30\x36\x39\x31\x31\x31\x38\x31\x39\x32\x35\x30\x32", 20);//somu-id
|
||||
memmove(dst + 0x2c5, "\x4d\x08\xc8\x9d\xc4\x50\x49\x70\x48\x4d\xd0\x12\xd9\x7c\x62\x5e\x6b\xd3\x84\xd5\x36\x42\xfe\x86\x8e\x7a\x23\x59\xa0\x20\xf0\xc5\x02\x20\x5f\x70\x93\x61\x5a\xe4\x20\xcf\xb9\x8a\xf5\xdd\x87\xd0\x48\x6d\x7d\x59\xef\x9e\x0e\x11\xa3\x8e\xf7\xe3\xe2\xf5\x35\x37\x99\x1a", 66);//somu-sig
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,356 +0,0 @@
|
||||
// Copyright 2019 SoloKeys Developers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
/*
|
||||
* Wrapper for crypto implementation on device
|
||||
*
|
||||
* */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
|
||||
#include "util.h"
|
||||
#include "crypto.h"
|
||||
|
||||
#ifdef USE_SOFTWARE_IMPLEMENTATION
|
||||
|
||||
#include "sha256.h"
|
||||
#include "uECC.h"
|
||||
#include "aes.h"
|
||||
#include "ctap.h"
|
||||
#include "device.h"
|
||||
// stuff for SHA512
|
||||
#include "sha2.h"
|
||||
#include "blockwise.h"
|
||||
#include APP_CONFIG
|
||||
#include "log.h"
|
||||
#include "memory_layout.h"
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MBEDTLS_ECP_DP_NONE = 0,
|
||||
MBEDTLS_ECP_DP_SECP192R1, /*!< 192-bits NIST curve */
|
||||
MBEDTLS_ECP_DP_SECP224R1, /*!< 224-bits NIST curve */
|
||||
MBEDTLS_ECP_DP_SECP256R1, /*!< 256-bits NIST curve */
|
||||
MBEDTLS_ECP_DP_SECP384R1, /*!< 384-bits NIST curve */
|
||||
MBEDTLS_ECP_DP_SECP521R1, /*!< 521-bits NIST curve */
|
||||
MBEDTLS_ECP_DP_BP256R1, /*!< 256-bits Brainpool curve */
|
||||
MBEDTLS_ECP_DP_BP384R1, /*!< 384-bits Brainpool curve */
|
||||
MBEDTLS_ECP_DP_BP512R1, /*!< 512-bits Brainpool curve */
|
||||
MBEDTLS_ECP_DP_CURVE25519, /*!< Curve25519 */
|
||||
MBEDTLS_ECP_DP_SECP192K1, /*!< 192-bits "Koblitz" curve */
|
||||
MBEDTLS_ECP_DP_SECP224K1, /*!< 224-bits "Koblitz" curve */
|
||||
MBEDTLS_ECP_DP_SECP256K1, /*!< 256-bits "Koblitz" curve */
|
||||
} mbedtls_ecp_group_id;
|
||||
|
||||
|
||||
static SHA256_CTX sha256_ctx;
|
||||
static cf_sha512_context sha512_ctx;
|
||||
static const struct uECC_Curve_t * _es256_curve = NULL;
|
||||
static const uint8_t * _signing_key = NULL;
|
||||
static int _key_len = 0;
|
||||
|
||||
// Secrets for testing only
|
||||
static uint8_t master_secret[64];
|
||||
static uint8_t transport_secret[32];
|
||||
|
||||
|
||||
void crypto_sha256_init()
|
||||
{
|
||||
sha256_init(&sha256_ctx);
|
||||
}
|
||||
|
||||
void crypto_sha512_init() {
|
||||
cf_sha512_init(&sha512_ctx);
|
||||
}
|
||||
|
||||
void crypto_load_master_secret(uint8_t * key)
|
||||
{
|
||||
#if KEY_SPACE_BYTES < 96
|
||||
#error "need more key bytes"
|
||||
#endif
|
||||
memmove(master_secret, key, 64);
|
||||
memmove(transport_secret, key+64, 32);
|
||||
}
|
||||
|
||||
void crypto_reset_master_secret()
|
||||
{
|
||||
memset(master_secret, 0, 64);
|
||||
memset(transport_secret, 0, 32);
|
||||
ctap_generate_rng(master_secret, 64);
|
||||
ctap_generate_rng(transport_secret, 32);
|
||||
}
|
||||
|
||||
|
||||
void crypto_sha256_update(uint8_t * data, size_t len)
|
||||
{
|
||||
sha256_update(&sha256_ctx, data, len);
|
||||
}
|
||||
|
||||
void crypto_sha512_update(const uint8_t * data, size_t len) {
|
||||
cf_sha512_update(&sha512_ctx, data, len);
|
||||
}
|
||||
|
||||
void crypto_sha256_update_secret()
|
||||
{
|
||||
sha256_update(&sha256_ctx, master_secret, 32);
|
||||
}
|
||||
|
||||
void crypto_sha256_final(uint8_t * hash)
|
||||
{
|
||||
sha256_final(&sha256_ctx, hash);
|
||||
}
|
||||
|
||||
void crypto_sha512_final(uint8_t * hash) {
|
||||
// NB: there is also cf_sha512_digest
|
||||
cf_sha512_digest_final(&sha512_ctx, hash);
|
||||
}
|
||||
|
||||
void crypto_sha256_hmac_init(uint8_t * key, uint32_t klen, uint8_t * hmac)
|
||||
{
|
||||
uint8_t buf[64];
|
||||
unsigned int i;
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
if (key == CRYPTO_MASTER_KEY)
|
||||
{
|
||||
key = master_secret;
|
||||
klen = sizeof(master_secret)/2;
|
||||
}
|
||||
else if (key == CRYPTO_TRANSPORT_KEY)
|
||||
{
|
||||
key = transport_secret;
|
||||
klen = 32;
|
||||
}
|
||||
|
||||
if(klen > 64)
|
||||
{
|
||||
printf2(TAG_ERR, "Error, key size must be <= 64\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memmove(buf, key, klen);
|
||||
|
||||
for (i = 0; i < sizeof(buf); i++)
|
||||
{
|
||||
buf[i] = buf[i] ^ 0x36;
|
||||
}
|
||||
|
||||
crypto_sha256_init();
|
||||
crypto_sha256_update(buf, 64);
|
||||
}
|
||||
|
||||
void crypto_sha256_hmac_final(uint8_t * key, uint32_t klen, uint8_t * hmac)
|
||||
{
|
||||
uint8_t buf[64];
|
||||
unsigned int i;
|
||||
crypto_sha256_final(hmac);
|
||||
memset(buf, 0, sizeof(buf));
|
||||
if (key == CRYPTO_MASTER_KEY)
|
||||
{
|
||||
key = master_secret;
|
||||
klen = sizeof(master_secret)/2;
|
||||
}
|
||||
|
||||
|
||||
if(klen > 64)
|
||||
{
|
||||
printf2(TAG_ERR, "Error, key size must be <= 64\n");
|
||||
exit(1);
|
||||
}
|
||||
memmove(buf, key, klen);
|
||||
|
||||
for (i = 0; i < sizeof(buf); i++)
|
||||
{
|
||||
buf[i] = buf[i] ^ 0x5c;
|
||||
}
|
||||
|
||||
crypto_sha256_init();
|
||||
crypto_sha256_update(buf, 64);
|
||||
crypto_sha256_update(hmac, 32);
|
||||
crypto_sha256_final(hmac);
|
||||
}
|
||||
|
||||
|
||||
void crypto_ecc256_init()
|
||||
{
|
||||
uECC_set_rng((uECC_RNG_Function)ctap_generate_rng);
|
||||
_es256_curve = uECC_secp256r1();
|
||||
}
|
||||
|
||||
|
||||
void crypto_ecc256_load_attestation_key()
|
||||
{
|
||||
static uint8_t _key [32];
|
||||
memmove(_key, (uint8_t*)ATTESTATION_KEY_ADDR, 32);
|
||||
_signing_key = _key;
|
||||
_key_len = 32;
|
||||
}
|
||||
|
||||
void crypto_ecc256_sign(uint8_t * data, int len, uint8_t * sig)
|
||||
{
|
||||
if ( uECC_sign(_signing_key, data, len, sig, _es256_curve) == 0)
|
||||
{
|
||||
printf2(TAG_ERR, "error, uECC failed\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void crypto_ecc256_load_key(uint8_t * data, int len, uint8_t * data2, int len2)
|
||||
{
|
||||
static uint8_t privkey[32];
|
||||
generate_private_key(data,len,data2,len2,privkey);
|
||||
_signing_key = privkey;
|
||||
_key_len = 32;
|
||||
}
|
||||
|
||||
void crypto_ecdsa_sign(uint8_t * data, int len, uint8_t * sig, int MBEDTLS_ECP_ID)
|
||||
{
|
||||
|
||||
const struct uECC_Curve_t * curve = NULL;
|
||||
|
||||
switch(MBEDTLS_ECP_ID)
|
||||
{
|
||||
case MBEDTLS_ECP_DP_SECP192R1:
|
||||
curve = uECC_secp192r1();
|
||||
if (_key_len != 24) goto fail;
|
||||
break;
|
||||
case MBEDTLS_ECP_DP_SECP224R1:
|
||||
curve = uECC_secp224r1();
|
||||
if (_key_len != 28) goto fail;
|
||||
break;
|
||||
case MBEDTLS_ECP_DP_SECP256R1:
|
||||
curve = uECC_secp256r1();
|
||||
if (_key_len != 32) goto fail;
|
||||
break;
|
||||
case MBEDTLS_ECP_DP_SECP256K1:
|
||||
curve = uECC_secp256k1();
|
||||
if (_key_len != 32) goto fail;
|
||||
break;
|
||||
default:
|
||||
printf2(TAG_ERR, "error, invalid ECDSA alg specifier\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ( uECC_sign(_signing_key, data, len, sig, curve) == 0)
|
||||
{
|
||||
printf2(TAG_ERR, "error, uECC failed\n");
|
||||
exit(1);
|
||||
}
|
||||
return;
|
||||
|
||||
fail:
|
||||
printf2(TAG_ERR, "error, invalid key length\n");
|
||||
exit(1);
|
||||
|
||||
}
|
||||
|
||||
void generate_private_key(uint8_t * data, int len, uint8_t * data2, int len2, uint8_t * privkey)
|
||||
{
|
||||
crypto_sha256_hmac_init(CRYPTO_MASTER_KEY, 0, privkey);
|
||||
crypto_sha256_update(data, len);
|
||||
crypto_sha256_update(data2, len2);
|
||||
crypto_sha256_update(master_secret, 32); // TODO AES
|
||||
crypto_sha256_hmac_final(CRYPTO_MASTER_KEY, 0, privkey);
|
||||
|
||||
crypto_aes256_init(master_secret + 32, NULL);
|
||||
crypto_aes256_encrypt(privkey, 32);
|
||||
}
|
||||
|
||||
|
||||
/*int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, uECC_Curve curve);*/
|
||||
void crypto_ecc256_derive_public_key(uint8_t * data, int len, uint8_t * x, uint8_t * y)
|
||||
{
|
||||
uint8_t privkey[32];
|
||||
uint8_t pubkey[64];
|
||||
|
||||
generate_private_key(data,len,NULL,0,privkey);
|
||||
|
||||
memset(pubkey,0,sizeof(pubkey));
|
||||
uECC_compute_public_key(privkey, pubkey, _es256_curve);
|
||||
memmove(x,pubkey,32);
|
||||
memmove(y,pubkey+32,32);
|
||||
}
|
||||
|
||||
void crypto_load_external_key(uint8_t * key, int len)
|
||||
{
|
||||
_signing_key = key;
|
||||
_key_len = len;
|
||||
}
|
||||
|
||||
|
||||
void crypto_ecc256_make_key_pair(uint8_t * pubkey, uint8_t * privkey)
|
||||
{
|
||||
if (uECC_make_key(pubkey, privkey, _es256_curve) != 1)
|
||||
{
|
||||
printf2(TAG_ERR, "Error, uECC_make_key failed\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void crypto_ecc256_shared_secret(const uint8_t * pubkey, const uint8_t * privkey, uint8_t * shared_secret)
|
||||
{
|
||||
if (uECC_shared_secret(pubkey, privkey, shared_secret, _es256_curve) != 1)
|
||||
{
|
||||
printf2(TAG_ERR, "Error, uECC_shared_secret failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct AES_ctx aes_ctx;
|
||||
void crypto_aes256_init(uint8_t * key, uint8_t * nonce)
|
||||
{
|
||||
if (key == CRYPTO_TRANSPORT_KEY)
|
||||
{
|
||||
AES_init_ctx(&aes_ctx, transport_secret);
|
||||
}
|
||||
else
|
||||
{
|
||||
AES_init_ctx(&aes_ctx, key);
|
||||
}
|
||||
if (nonce == NULL)
|
||||
{
|
||||
memset(aes_ctx.Iv, 0, 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
memmove(aes_ctx.Iv, nonce, 16);
|
||||
}
|
||||
}
|
||||
|
||||
// prevent round key recomputation
|
||||
void crypto_aes256_reset_iv(uint8_t * nonce)
|
||||
{
|
||||
if (nonce == NULL)
|
||||
{
|
||||
memset(aes_ctx.Iv, 0, 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
memmove(aes_ctx.Iv, nonce, 16);
|
||||
}
|
||||
}
|
||||
|
||||
void crypto_aes256_decrypt(uint8_t * buf, int length)
|
||||
{
|
||||
AES_CBC_decrypt_buffer(&aes_ctx, buf, length);
|
||||
}
|
||||
|
||||
void crypto_aes256_encrypt(uint8_t * buf, int length)
|
||||
{
|
||||
AES_CBC_encrypt_buffer(&aes_ctx, buf, length);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#else
|
||||
#error "No crypto implementation defined"
|
||||
#endif
|
@ -34,31 +34,73 @@
|
||||
#define LOW_FREQUENCY 1
|
||||
#define HIGH_FREQUENCY 0
|
||||
|
||||
void wait_for_usb_tether();
|
||||
#define SOLO_FLAG_LOCKED 0x2
|
||||
|
||||
void wait_for_usb_tether(void);
|
||||
|
||||
|
||||
uint32_t __90_ms = 0;
|
||||
uint32_t __last_button_press_time = 0;
|
||||
uint32_t __last_button_bounce_time = 0;
|
||||
uint32_t __device_status = 0;
|
||||
uint32_t __last_update = 0;
|
||||
extern PCD_HandleTypeDef hpcd;
|
||||
static int _NFC_status = 0;
|
||||
static bool isLowFreq = 0;
|
||||
static bool _up_disabled = false;
|
||||
|
||||
// #define IS_BUTTON_PRESSED() (0 == (LL_GPIO_ReadInputPort(SOLO_BUTTON_PORT) & SOLO_BUTTON_PIN))
|
||||
static int is_physical_button_pressed()
|
||||
static int is_physical_button_pressed(void)
|
||||
{
|
||||
return (0 == (LL_GPIO_ReadInputPort(SOLO_BUTTON_PORT) & SOLO_BUTTON_PIN));
|
||||
}
|
||||
|
||||
static int is_touch_button_pressed()
|
||||
static int is_touch_button_pressed(void)
|
||||
{
|
||||
return tsc_read_button(0) || tsc_read_button(1);
|
||||
int is_pressed = (tsc_read_button(0) || tsc_read_button(1));
|
||||
#ifndef IS_BOOTLOADER
|
||||
if (is_pressed)
|
||||
{
|
||||
// delay for debounce, and longer than polling timer period.
|
||||
delay(95);
|
||||
return (tsc_read_button(0) || tsc_read_button(1));
|
||||
}
|
||||
#endif
|
||||
return is_pressed;
|
||||
}
|
||||
|
||||
int (*IS_BUTTON_PRESSED)() = is_physical_button_pressed;
|
||||
|
||||
static void edge_detect_touch_button(void)
|
||||
{
|
||||
static uint8_t last_touch = 0;
|
||||
uint8_t current_touch = 0;
|
||||
if (is_touch_button_pressed == IS_BUTTON_PRESSED)
|
||||
{
|
||||
current_touch = (tsc_read_button(0) || tsc_read_button(1));
|
||||
|
||||
// 1 sample per 25 ms
|
||||
if ((millis() - __last_button_bounce_time) > 25)
|
||||
{
|
||||
// Detect "touch / rising edge"
|
||||
if (!last_touch && current_touch)
|
||||
{
|
||||
__last_button_press_time = millis();
|
||||
}
|
||||
__last_button_bounce_time = millis();
|
||||
last_touch = current_touch;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void device_disable_up(bool disable)
|
||||
{
|
||||
_up_disabled = disable;
|
||||
}
|
||||
|
||||
// Timer6 overflow handler. happens every ~90ms.
|
||||
void TIM6_DAC_IRQHandler()
|
||||
void TIM6_DAC_IRQHandler(void)
|
||||
{
|
||||
// timer is only 16 bits, so roll it over here
|
||||
TIM6->SR = 0;
|
||||
@ -70,6 +112,9 @@ void TIM6_DAC_IRQHandler()
|
||||
ctaphid_update_status(__device_status);
|
||||
}
|
||||
}
|
||||
|
||||
edge_detect_touch_button();
|
||||
|
||||
#ifndef IS_BOOTLOADER
|
||||
// NFC sending WTX if needs
|
||||
if (device_is_nfc() == NFC_IS_ACTIVE)
|
||||
@ -79,13 +124,28 @@ void TIM6_DAC_IRQHandler()
|
||||
#endif
|
||||
}
|
||||
|
||||
// Interrupt on rising edge of button (button released)
|
||||
void EXTI0_IRQHandler(void)
|
||||
{
|
||||
EXTI->PR1 = EXTI->PR1;
|
||||
if (is_physical_button_pressed == IS_BUTTON_PRESSED)
|
||||
{
|
||||
// Only allow 1 press per 25 ms.
|
||||
if ((millis() - __last_button_bounce_time) > 25)
|
||||
{
|
||||
__last_button_press_time = millis();
|
||||
}
|
||||
__last_button_bounce_time = millis();
|
||||
}
|
||||
}
|
||||
|
||||
// Global USB interrupt handler
|
||||
void USB_IRQHandler(void)
|
||||
{
|
||||
HAL_PCD_IRQHandler(&hpcd);
|
||||
}
|
||||
|
||||
uint32_t millis()
|
||||
uint32_t millis(void)
|
||||
{
|
||||
return (((uint32_t)TIM6->CNT) + (__90_ms * 90));
|
||||
}
|
||||
@ -103,9 +163,8 @@ void device_set_status(uint32_t status)
|
||||
__device_status = status;
|
||||
}
|
||||
|
||||
int device_is_button_pressed()
|
||||
int device_is_button_pressed(void)
|
||||
{
|
||||
|
||||
return IS_BUTTON_PRESSED();
|
||||
}
|
||||
|
||||
@ -115,12 +174,13 @@ void delay(uint32_t ms)
|
||||
while ((millis() - time) < ms)
|
||||
;
|
||||
}
|
||||
void device_reboot()
|
||||
|
||||
void device_reboot(void)
|
||||
{
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
void device_init_button()
|
||||
void device_init_button(void)
|
||||
{
|
||||
if (tsc_sensor_exists())
|
||||
{
|
||||
@ -133,7 +193,113 @@ void device_init_button()
|
||||
}
|
||||
}
|
||||
|
||||
void device_init(int argc, char *argv[])
|
||||
int solo_is_locked(){
|
||||
uint64_t device_settings = ((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->device_settings;
|
||||
uint32_t tag = (uint32_t)(device_settings >> 32ull);
|
||||
return tag == ATTESTATION_CONFIGURED_TAG && (device_settings & SOLO_FLAG_LOCKED) != 0;
|
||||
}
|
||||
|
||||
// Locks solo flash from debugging. Locks on next reboot.
|
||||
// This should be removed in next Solo release.
|
||||
void solo_lock_if_not_already() {
|
||||
uint8_t buf[2048];
|
||||
|
||||
memmove(buf, (uint8_t*)ATTESTATION_PAGE_ADDR, 2048);
|
||||
|
||||
((flash_attestation_page *)buf)->device_settings |= SOLO_FLAG_LOCKED;
|
||||
|
||||
flash_erase_page(ATTESTATION_PAGE);
|
||||
|
||||
flash_write(ATTESTATION_PAGE_ADDR, buf, 2048);
|
||||
}
|
||||
|
||||
/** device_migrate
|
||||
* Depending on version of device, migrates:
|
||||
* * Moves attestation certificate to data segment.
|
||||
* * Creates locked variable and stores in data segment.
|
||||
*
|
||||
* Once in place, this allows all devices to accept same firmware,
|
||||
* rather than using "hacker" and "secure" builds.
|
||||
*/
|
||||
static void device_migrate(){
|
||||
extern const uint16_t attestation_solo_cert_der_size;
|
||||
extern const uint16_t attestation_hacker_cert_der_size;
|
||||
|
||||
extern uint8_t attestation_solo_cert_der[];
|
||||
extern uint8_t attestation_hacker_cert_der[];
|
||||
|
||||
uint64_t device_settings = ((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->device_settings;
|
||||
uint32_t configure_tag = (uint32_t)(device_settings >> 32);
|
||||
|
||||
if (configure_tag != ATTESTATION_CONFIGURED_TAG)
|
||||
{
|
||||
printf1(TAG_RED,"Migrating certificate and lock information to data segment.\r\n");
|
||||
|
||||
device_settings = ATTESTATION_CONFIGURED_TAG;
|
||||
device_settings <<= 32;
|
||||
|
||||
// Read current device lock level.
|
||||
uint32_t optr = FLASH->OPTR;
|
||||
if ((optr & 0xff) != 0xAA){
|
||||
device_settings |= SOLO_FLAG_LOCKED;
|
||||
}
|
||||
|
||||
uint8_t tmp_attestation_key[32];
|
||||
|
||||
memmove(tmp_attestation_key,
|
||||
((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->attestation_key,
|
||||
32);
|
||||
|
||||
flash_erase_page(ATTESTATION_PAGE);
|
||||
flash_write(
|
||||
(uint32_t)((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->attestation_key,
|
||||
tmp_attestation_key,
|
||||
32
|
||||
);
|
||||
|
||||
// Check if this is Solo Hacker attestation (not confidential)
|
||||
// then write solo or hacker attestation cert to flash page.
|
||||
uint8_t solo_hacker_attestation_key[32] = "\x1b\x26\x26\xec\xc8\xf6\x9b\x0f\x69\xe3\x4f"
|
||||
"\xb2\x36\xd7\x64\x66\xba\x12\xac\x16\xc3\xab"
|
||||
"\x57\x50\xba\x06\x4e\x8b\x90\xe0\x24\x48";
|
||||
|
||||
if (memcmp(solo_hacker_attestation_key,
|
||||
tmp_attestation_key,
|
||||
32) == 0)
|
||||
{
|
||||
printf1(TAG_GREEN,"Updating solo hacker cert\r\n");
|
||||
flash_write_dword(
|
||||
(uint32_t)&((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->attestation_cert_size,
|
||||
(uint64_t)attestation_hacker_cert_der_size
|
||||
);
|
||||
flash_write(
|
||||
(uint32_t)((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->attestation_cert,
|
||||
attestation_hacker_cert_der,
|
||||
attestation_hacker_cert_der_size
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf1(TAG_GREEN,"Updating solo secure cert\r\n");
|
||||
flash_write_dword(
|
||||
(uint32_t)&((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->attestation_cert_size,
|
||||
(uint64_t)attestation_solo_cert_der_size
|
||||
);
|
||||
flash_write(
|
||||
(uint32_t)((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->attestation_cert,
|
||||
attestation_solo_cert_der,
|
||||
attestation_solo_cert_der_size
|
||||
);
|
||||
}
|
||||
|
||||
// Save / done.
|
||||
flash_write_dword(
|
||||
(uint32_t) & ((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->device_settings,
|
||||
(uint64_t)device_settings);
|
||||
}
|
||||
}
|
||||
|
||||
void device_init()
|
||||
{
|
||||
|
||||
hw_init(LOW_FREQUENCY);
|
||||
@ -161,6 +327,8 @@ void device_init(int argc, char *argv[])
|
||||
ctaphid_init();
|
||||
ctap_init();
|
||||
|
||||
device_migrate();
|
||||
|
||||
#if BOOT_TO_DFU
|
||||
flash_option_bytes_init(1);
|
||||
#else
|
||||
@ -170,12 +338,12 @@ void device_init(int argc, char *argv[])
|
||||
|
||||
}
|
||||
|
||||
int device_is_nfc()
|
||||
int device_is_nfc(void)
|
||||
{
|
||||
return _NFC_status;
|
||||
}
|
||||
|
||||
void wait_for_usb_tether()
|
||||
void wait_for_usb_tether(void)
|
||||
{
|
||||
while (USBD_OK != CDC_Transmit_FS((uint8_t*)"tethered\r\n", 10) )
|
||||
;
|
||||
@ -186,7 +354,7 @@ void wait_for_usb_tether()
|
||||
;
|
||||
}
|
||||
|
||||
void usbhid_init()
|
||||
void usbhid_init(void)
|
||||
{
|
||||
if (!isLowFreq)
|
||||
{
|
||||
@ -236,12 +404,12 @@ void ctaphid_write_block(uint8_t * data)
|
||||
}
|
||||
|
||||
|
||||
void usbhid_close()
|
||||
void usbhid_close(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void main_loop_delay()
|
||||
void main_loop_delay(void)
|
||||
{
|
||||
|
||||
}
|
||||
@ -251,13 +419,14 @@ static uint32_t winkt1 = 0;
|
||||
#ifdef LED_WINK_VALUE
|
||||
static uint32_t winkt2 = 0;
|
||||
#endif
|
||||
void device_wink()
|
||||
|
||||
void device_wink(void)
|
||||
{
|
||||
wink_time = 10;
|
||||
winkt1 = 0;
|
||||
}
|
||||
|
||||
void heartbeat()
|
||||
void heartbeat(void)
|
||||
{
|
||||
static int state = 0;
|
||||
static uint32_t val = (LED_MAX_SCALER - LED_MIN_SCALER)/2;
|
||||
@ -313,20 +482,8 @@ void heartbeat()
|
||||
|
||||
}
|
||||
|
||||
void authenticator_read_state(AuthenticatorState * a)
|
||||
{
|
||||
uint32_t * ptr = (uint32_t *)flash_addr(STATE1_PAGE);
|
||||
memmove(a,ptr,sizeof(AuthenticatorState));
|
||||
}
|
||||
|
||||
void authenticator_read_backup_state(AuthenticatorState * a)
|
||||
{
|
||||
uint32_t * ptr = (uint32_t *)flash_addr(STATE2_PAGE);
|
||||
memmove(a,ptr,sizeof(AuthenticatorState));
|
||||
}
|
||||
|
||||
// Return 1 yes backup is init'd, else 0
|
||||
int authenticator_is_backup_initialized()
|
||||
static int authenticator_is_backup_initialized(void)
|
||||
{
|
||||
uint8_t header[16];
|
||||
uint32_t * ptr = (uint32_t *)flash_addr(STATE2_PAGE);
|
||||
@ -335,23 +492,39 @@ int authenticator_is_backup_initialized()
|
||||
return state->is_initialized == INITIALIZED_MARKER;
|
||||
}
|
||||
|
||||
void authenticator_write_state(AuthenticatorState * a, int backup)
|
||||
int authenticator_read_state(AuthenticatorState * a)
|
||||
{
|
||||
if (! backup)
|
||||
{
|
||||
flash_erase_page(STATE1_PAGE);
|
||||
uint32_t * ptr = (uint32_t *) flash_addr(STATE1_PAGE);
|
||||
memmove(a, ptr, sizeof(AuthenticatorState));
|
||||
|
||||
flash_write(flash_addr(STATE1_PAGE), (uint8_t*)a, sizeof(AuthenticatorState));
|
||||
}
|
||||
else
|
||||
{
|
||||
flash_erase_page(STATE2_PAGE);
|
||||
if (a->is_initialized != INITIALIZED_MARKER){
|
||||
|
||||
flash_write(flash_addr(STATE2_PAGE), (uint8_t*)a, sizeof(AuthenticatorState));
|
||||
if (authenticator_is_backup_initialized()){
|
||||
printf1(TAG_ERR,"Warning: memory corruption detected. restoring from backup..\n");
|
||||
ptr = (uint32_t *) flash_addr(STATE2_PAGE);
|
||||
memmove(a, ptr, sizeof(AuthenticatorState));
|
||||
authenticator_write_state(a);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32_t ctap_atomic_count(int sel)
|
||||
|
||||
void authenticator_write_state(AuthenticatorState * a)
|
||||
{
|
||||
flash_erase_page(STATE1_PAGE);
|
||||
flash_write(flash_addr(STATE1_PAGE), (uint8_t*)a, sizeof(AuthenticatorState));
|
||||
|
||||
flash_erase_page(STATE2_PAGE);
|
||||
flash_write(flash_addr(STATE2_PAGE), (uint8_t*)a, sizeof(AuthenticatorState));
|
||||
}
|
||||
|
||||
#if !defined(IS_BOOTLOADER)
|
||||
uint32_t ctap_atomic_count(uint32_t amount)
|
||||
{
|
||||
int offset = 0;
|
||||
uint32_t * ptr = (uint32_t *)flash_addr(COUNTER1_PAGE);
|
||||
@ -366,10 +539,12 @@ uint32_t ctap_atomic_count(int sel)
|
||||
|
||||
uint32_t lastc = 0;
|
||||
|
||||
if (sel != 0)
|
||||
if (amount == 0)
|
||||
{
|
||||
printf2(TAG_ERR,"counter2 not imple\n");
|
||||
exit(1);
|
||||
// Use a random count [1-16].
|
||||
uint8_t rng[1];
|
||||
ctap_generate_rng(rng, 1);
|
||||
amount = (rng[0] & 0x0f) + 1;
|
||||
}
|
||||
|
||||
for (offset = 0; offset < PAGE_SIZE/4; offset += 2) // wear-level the flash
|
||||
@ -402,7 +577,11 @@ uint32_t ctap_atomic_count(int sel)
|
||||
return lastc;
|
||||
}
|
||||
|
||||
lastc++;
|
||||
if (amount > 256){
|
||||
lastc = amount;
|
||||
} else {
|
||||
lastc += amount;
|
||||
}
|
||||
|
||||
if (lastc/256 > erases)
|
||||
{
|
||||
@ -440,10 +619,10 @@ uint32_t ctap_atomic_count(int sel)
|
||||
|
||||
return lastc;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
void device_manage()
|
||||
void device_manage(void)
|
||||
{
|
||||
#if NON_BLOCK_PRINTING
|
||||
int i = 10;
|
||||
@ -469,7 +648,7 @@ void device_manage()
|
||||
#endif
|
||||
}
|
||||
|
||||
static int handle_packets()
|
||||
static int handle_packets(void)
|
||||
{
|
||||
static uint8_t hidmsg[HID_PACKET_SIZE];
|
||||
memset(hidmsg,0, sizeof(hidmsg));
|
||||
@ -488,13 +667,56 @@ static int handle_packets()
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wait_for_button_activate(uint32_t wait)
|
||||
{
|
||||
int ret;
|
||||
uint32_t start = millis();
|
||||
do
|
||||
{
|
||||
if ((start + wait) < millis())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
delay(1);
|
||||
ret = handle_packets();
|
||||
if (ret)
|
||||
return ret;
|
||||
} while (!IS_BUTTON_PRESSED());
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wait_for_button_release(uint32_t wait)
|
||||
{
|
||||
int ret;
|
||||
uint32_t start = millis();
|
||||
do
|
||||
{
|
||||
if ((start + wait) < millis())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
delay(1);
|
||||
ret = handle_packets();
|
||||
if (ret)
|
||||
return ret;
|
||||
} while (IS_BUTTON_PRESSED());
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ctap_user_presence_test(uint32_t up_delay)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (device_is_nfc() == NFC_IS_ACTIVE)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (_up_disabled)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
#if SKIP_BUTTON_CHECK_WITH_DELAY
|
||||
int i=500;
|
||||
while(i--)
|
||||
@ -507,53 +729,41 @@ int ctap_user_presence_test(uint32_t up_delay)
|
||||
#elif SKIP_BUTTON_CHECK_FAST
|
||||
delay(2);
|
||||
ret = handle_packets();
|
||||
if (ret) return ret;
|
||||
if (ret)
|
||||
return ret;
|
||||
goto done;
|
||||
#endif
|
||||
uint32_t t1 = millis();
|
||||
|
||||
// If button was pressed within last [2] seconds, succeed.
|
||||
if (__last_button_press_time && (millis() - __last_button_press_time < 2000))
|
||||
{
|
||||
goto done;
|
||||
}
|
||||
|
||||
// Set LED status and wait.
|
||||
led_rgb(0xff3520);
|
||||
|
||||
if (IS_BUTTON_PRESSED == is_touch_button_pressed)
|
||||
{
|
||||
// Wait for user to release touch button if it's already pressed
|
||||
while (IS_BUTTON_PRESSED())
|
||||
{
|
||||
if (t1 + up_delay < millis())
|
||||
{
|
||||
printf1(TAG_GEN,"Button not pressed\n");
|
||||
goto fail;
|
||||
}
|
||||
ret = handle_packets();
|
||||
if (ret) return ret;
|
||||
}
|
||||
}
|
||||
|
||||
t1 = millis();
|
||||
|
||||
do
|
||||
{
|
||||
if (t1 + up_delay < millis())
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
delay(1);
|
||||
ret = handle_packets();
|
||||
// Block and wait for some time.
|
||||
ret = wait_for_button_activate(up_delay);
|
||||
if (ret) return ret;
|
||||
ret = wait_for_button_release(up_delay);
|
||||
if (ret) return ret;
|
||||
}
|
||||
while (! IS_BUTTON_PRESSED());
|
||||
|
||||
led_rgb(0x001040);
|
||||
|
||||
delay(50);
|
||||
// If button was pressed within last [2] seconds, succeed.
|
||||
if (__last_button_press_time && (millis() - __last_button_press_time < 2000))
|
||||
{
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
|
||||
|
||||
#if SKIP_BUTTON_CHECK_WITH_DELAY || SKIP_BUTTON_CHECK_FAST
|
||||
done:
|
||||
#endif
|
||||
return 1;
|
||||
ret = wait_for_button_release(up_delay);
|
||||
__last_button_press_time = 0;
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ctap_generate_rng(uint8_t * dst, size_t num)
|
||||
@ -563,12 +773,7 @@ int ctap_generate_rng(uint8_t * dst, size_t num)
|
||||
}
|
||||
|
||||
|
||||
int ctap_user_verification(uint8_t arg)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ctap_reset_rk()
|
||||
void ctap_reset_rk(void)
|
||||
{
|
||||
int i;
|
||||
printf1(TAG_GREEN, "resetting RK \r\n");
|
||||
@ -578,40 +783,35 @@ void ctap_reset_rk()
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ctap_rk_size()
|
||||
uint32_t ctap_rk_size(void)
|
||||
{
|
||||
return RK_NUM_PAGES * (PAGE_SIZE / sizeof(CTAP_residentKey));
|
||||
}
|
||||
|
||||
void ctap_store_rk(int index,CTAP_residentKey * rk)
|
||||
{
|
||||
int page_offset = (sizeof(CTAP_residentKey) * index) / PAGE_SIZE;
|
||||
uint32_t addr = flash_addr(page_offset + RK_START_PAGE) + ((sizeof(CTAP_residentKey)*index) % PAGE_SIZE);
|
||||
ctap_overwrite_rk(index, rk);
|
||||
}
|
||||
|
||||
printf1(TAG_GREEN, "storing RK %d @ %04x\r\n", index,addr);
|
||||
|
||||
if (page_offset < RK_NUM_PAGES)
|
||||
{
|
||||
flash_write(addr, (uint8_t*)rk, sizeof(CTAP_residentKey));
|
||||
//dump_hex1(TAG_GREEN,rk,sizeof(CTAP_residentKey));
|
||||
}
|
||||
else
|
||||
{
|
||||
printf2(TAG_ERR,"Out of bounds reading index %d for rk\n", index);
|
||||
}
|
||||
void ctap_delete_rk(int index)
|
||||
{
|
||||
CTAP_residentKey rk;
|
||||
memset(&rk, 0xff, sizeof(CTAP_residentKey));
|
||||
ctap_overwrite_rk(index, &rk);
|
||||
}
|
||||
|
||||
void ctap_load_rk(int index,CTAP_residentKey * rk)
|
||||
{
|
||||
int page_offset = (sizeof(CTAP_residentKey) * index) / PAGE_SIZE;
|
||||
uint32_t addr = flash_addr(page_offset + RK_START_PAGE) + ((sizeof(CTAP_residentKey)*index) % PAGE_SIZE);
|
||||
int byte_offset_into_page = (sizeof(CTAP_residentKey) * (index % (PAGE_SIZE/sizeof(CTAP_residentKey))));
|
||||
int page_offset = (index)/(PAGE_SIZE/sizeof(CTAP_residentKey));
|
||||
|
||||
uint32_t addr = flash_addr(page_offset + RK_START_PAGE) + byte_offset_into_page;
|
||||
|
||||
printf1(TAG_GREEN, "reading RK %d @ %04x\r\n", index, addr);
|
||||
if (page_offset < RK_NUM_PAGES)
|
||||
{
|
||||
uint32_t * ptr = (uint32_t *)addr;
|
||||
memmove((uint8_t*)rk,ptr,sizeof(CTAP_residentKey));
|
||||
//dump_hex1(TAG_GREEN,rk,sizeof(CTAP_residentKey));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -622,25 +822,31 @@ void ctap_load_rk(int index,CTAP_residentKey * rk)
|
||||
void ctap_overwrite_rk(int index,CTAP_residentKey * rk)
|
||||
{
|
||||
uint8_t tmppage[PAGE_SIZE];
|
||||
int page_offset = (sizeof(CTAP_residentKey) * index) / PAGE_SIZE;
|
||||
int page = page_offset + RK_START_PAGE;
|
||||
|
||||
printf1(TAG_GREEN, "overwriting RK %d\r\n", index);
|
||||
int byte_offset_into_page = (sizeof(CTAP_residentKey) * (index % (PAGE_SIZE/sizeof(CTAP_residentKey))));
|
||||
int page_offset = (index)/(PAGE_SIZE/sizeof(CTAP_residentKey));
|
||||
|
||||
printf1(TAG_GREEN, "overwriting RK %d @ page %d @ addr 0x%08x-0x%08x\r\n",
|
||||
index, RK_START_PAGE + page_offset,
|
||||
flash_addr(RK_START_PAGE + page_offset) + byte_offset_into_page,
|
||||
flash_addr(RK_START_PAGE + page_offset) + byte_offset_into_page + sizeof(CTAP_residentKey)
|
||||
);
|
||||
if (page_offset < RK_NUM_PAGES)
|
||||
{
|
||||
memmove(tmppage, (uint8_t*)flash_addr(page), PAGE_SIZE);
|
||||
memmove(tmppage, (uint8_t*)flash_addr(RK_START_PAGE + page_offset), PAGE_SIZE);
|
||||
|
||||
memmove(tmppage + (sizeof(CTAP_residentKey) * index) % PAGE_SIZE, rk, sizeof(CTAP_residentKey));
|
||||
flash_erase_page(page);
|
||||
flash_write(flash_addr(page), tmppage, PAGE_SIZE);
|
||||
memmove(tmppage + byte_offset_into_page, rk, sizeof(CTAP_residentKey));
|
||||
flash_erase_page(RK_START_PAGE + page_offset);
|
||||
flash_write(flash_addr(RK_START_PAGE + page_offset), tmppage, PAGE_SIZE);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf2(TAG_ERR,"Out of bounds reading index %d for rk\n", index);
|
||||
}
|
||||
printf1(TAG_GREEN, "4\r\n");
|
||||
}
|
||||
|
||||
void boot_st_bootloader()
|
||||
void boot_st_bootloader(void)
|
||||
{
|
||||
__disable_irq();
|
||||
|
||||
@ -652,7 +858,7 @@ void boot_st_bootloader()
|
||||
;
|
||||
}
|
||||
|
||||
void boot_solo_bootloader()
|
||||
void boot_solo_bootloader(void)
|
||||
{
|
||||
LL_IWDG_Enable(IWDG);
|
||||
|
||||
@ -672,6 +878,17 @@ void boot_solo_bootloader()
|
||||
|
||||
}
|
||||
|
||||
void device_read_aaguid(uint8_t * dst){
|
||||
uint8_t * aaguid = (uint8_t *)"\x88\x76\x63\x1b\xd4\xa0\x42\x7f\x57\x73\x0e\xc7\x1c\x9e\x02\x79";
|
||||
memmove(dst, aaguid, 16);
|
||||
if (device_is_nfc()){
|
||||
dst[0] = 0x89;
|
||||
}
|
||||
else if (tsc_sensor_exists()){
|
||||
dst[0] = 0x98;
|
||||
}
|
||||
dump_hex1(TAG_GREEN,dst, 16);
|
||||
}
|
||||
|
||||
|
||||
void _Error_Handler(char *file, int line)
|
||||
|
@ -14,12 +14,12 @@
|
||||
#include "log.h"
|
||||
#include "device.h"
|
||||
|
||||
static void flash_lock()
|
||||
static void flash_lock(void)
|
||||
{
|
||||
FLASH->CR |= (1U<<31);
|
||||
}
|
||||
|
||||
static void flash_unlock()
|
||||
static void flash_unlock(void)
|
||||
{
|
||||
if (FLASH->CR & FLASH_CR_LOCK)
|
||||
{
|
||||
@ -31,21 +31,18 @@ static void flash_unlock()
|
||||
// Locks flash and turns off DFU
|
||||
void flash_option_bytes_init(int boot_from_dfu)
|
||||
{
|
||||
#ifndef FLASH_ROP
|
||||
#define FLASH_ROP 0
|
||||
#endif
|
||||
#if FLASH_ROP == 0
|
||||
uint32_t val = 0xfffff8aa;
|
||||
#elif FLASH_ROP == 2
|
||||
uint32_t val = 0xfffff8cc;
|
||||
#else
|
||||
uint32_t val = 0xfffff8b9;
|
||||
#endif
|
||||
|
||||
if (boot_from_dfu)
|
||||
{
|
||||
if (boot_from_dfu){
|
||||
val &= ~(1<<27); // nBOOT0 = 0 (boot from system rom)
|
||||
}
|
||||
else {
|
||||
if (solo_is_locked())
|
||||
{
|
||||
val = 0xfffff8cc;
|
||||
}
|
||||
}
|
||||
|
||||
val &= ~(1<<26); // nSWBOOT0 = 0 (boot from nBoot0)
|
||||
val &= ~(1<<25); // SRAM2_RST = 1 (erase sram on reset)
|
||||
val &= ~(1<<24); // SRAM2_PE = 1 (parity check en)
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "stm32l4xx_ll_rng.h"
|
||||
#include "stm32l4xx_ll_spi.h"
|
||||
#include "stm32l4xx_ll_usb.h"
|
||||
#include "stm32l4xx_ll_exti.h"
|
||||
#include "stm32l4xx_hal_pcd.h"
|
||||
#include "stm32l4xx_hal.h"
|
||||
|
||||
@ -27,6 +28,7 @@
|
||||
#include "usbd_desc.h"
|
||||
#include "usbd_hid.h"
|
||||
#include "usbd_cdc.h"
|
||||
#include "usbd_ccid.h"
|
||||
#include "usbd_composite.h"
|
||||
#include "usbd_cdc_if.h"
|
||||
#include "device.h"
|
||||
@ -144,12 +146,14 @@ void device_set_clock_rate(DEVICE_CLOCK_RATE param)
|
||||
case DEVICE_LOW_POWER_IDLE:
|
||||
SET_CLOCK_RATE0();
|
||||
break;
|
||||
#if !defined(IS_BOOTLOADER)
|
||||
case DEVICE_LOW_POWER_FAST:
|
||||
SET_CLOCK_RATE1();
|
||||
break;
|
||||
case DEVICE_FAST:
|
||||
SET_CLOCK_RATE2();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -697,33 +701,33 @@ void SystemClock_Config_LF20(void)
|
||||
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_PWREN);
|
||||
}
|
||||
|
||||
void init_usb()
|
||||
void init_usb(void)
|
||||
{
|
||||
// enable USB power
|
||||
SET_BIT(PWR->CR2, PWR_CR2_USV);
|
||||
|
||||
// Enable USB Clock
|
||||
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_USBFSEN);
|
||||
|
||||
#if DEBUG_LEVEL > 0
|
||||
USBD_Composite_Set_Classes(&USBD_HID, &USBD_CDC);
|
||||
#ifndef IS_BOOTLOADER
|
||||
USBD_Composite_Set_Classes(&USBD_HID, &USBD_CCID, &USBD_CDC);
|
||||
in_endpoint_to_class[HID_EPIN_ADDR & 0x7F] = 0;
|
||||
out_endpoint_to_class[HID_EPOUT_ADDR & 0x7F] = 0;
|
||||
|
||||
in_endpoint_to_class[CDC_IN_EP & 0x7F] = 1;
|
||||
out_endpoint_to_class[CDC_OUT_EP & 0x7F] = 1;
|
||||
in_endpoint_to_class[CCID_IN_EP & 0x7F] = 1;
|
||||
out_endpoint_to_class[CCID_OUT_EP & 0x7F] = 1;
|
||||
|
||||
in_endpoint_to_class[CDC_IN_EP & 0x7F] = 2;
|
||||
out_endpoint_to_class[CDC_OUT_EP & 0x7F] = 2;
|
||||
|
||||
USBD_Init(&Solo_USBD_Device, &Solo_Desc, 0);
|
||||
USBD_RegisterClass(&Solo_USBD_Device, &USBD_Composite);
|
||||
// USBD_RegisterClass(&Solo_USBD_Device, &USBD_HID);
|
||||
//
|
||||
// USBD_RegisterClass(&Solo_USBD_Device, &USBD_CDC);
|
||||
#if DEBUG_LEVEL > 0
|
||||
USBD_CDC_RegisterInterface(&Solo_USBD_Device, &USBD_Interface_fops_FS);
|
||||
#endif
|
||||
#else
|
||||
USBD_Init(&Solo_USBD_Device, &Solo_Desc, 0);
|
||||
USBD_RegisterClass(&Solo_USBD_Device, &USBD_HID);
|
||||
#endif
|
||||
|
||||
USBD_Start(&Solo_USBD_Device);
|
||||
}
|
||||
|
||||
@ -849,19 +853,17 @@ void init_gpio(void)
|
||||
LL_GPIO_SetPinMode(SOLO_BUTTON_PORT,SOLO_BUTTON_PIN,LL_GPIO_MODE_INPUT);
|
||||
LL_GPIO_SetPinPull(SOLO_BUTTON_PORT,SOLO_BUTTON_PIN,LL_GPIO_PULL_UP);
|
||||
|
||||
#ifdef SOLO_AMS_IRQ_PORT
|
||||
// SAVE POWER
|
||||
// LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOC);
|
||||
// /**/
|
||||
// LL_GPIO_InitTypeDef GPIO_InitStruct;
|
||||
// GPIO_InitStruct.Pin = SOLO_AMS_IRQ_PIN;
|
||||
// GPIO_InitStruct.Mode = LL_GPIO_MODE_INPUT;
|
||||
// GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
|
||||
// LL_GPIO_Init(SOLO_AMS_IRQ_PORT, &GPIO_InitStruct);
|
||||
//
|
||||
//
|
||||
// LL_GPIO_SetPinMode(SOLO_AMS_IRQ_PORT,SOLO_AMS_IRQ_PIN,LL_GPIO_MODE_INPUT);
|
||||
// LL_GPIO_SetPinPull(SOLO_AMS_IRQ_PORT,SOLO_AMS_IRQ_PIN,LL_GPIO_PULL_UP);
|
||||
#ifndef IS_BOOTLOADER
|
||||
LL_SYSCFG_SetEXTISource(LL_SYSCFG_EXTI_PORTA, LL_SYSCFG_EXTI_LINE0);
|
||||
LL_EXTI_InitTypeDef EXTI_InitStruct;
|
||||
EXTI_InitStruct.Line_0_31 = LL_EXTI_LINE_0; // GPIOA_0
|
||||
EXTI_InitStruct.Line_32_63 = LL_EXTI_LINE_NONE;
|
||||
EXTI_InitStruct.LineCommand = ENABLE;
|
||||
EXTI_InitStruct.Mode = LL_EXTI_MODE_IT;
|
||||
EXTI_InitStruct.Trigger = LL_EXTI_TRIGGER_RISING;
|
||||
LL_EXTI_Init(&EXTI_InitStruct);
|
||||
|
||||
NVIC_EnableIRQ(EXTI0_IRQn);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
#ifndef _INIT_H_
|
||||
#define _INIT_H_
|
||||
|
||||
void init_usb();
|
||||
void init_usb(void);
|
||||
void init_gpio(void);
|
||||
void init_debug_uart(void);
|
||||
void init_pwm(void);
|
||||
|
@ -4,104 +4,83 @@
|
||||
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "stm32l4xx.h"
|
||||
#include "stm32l4xx_ll_gpio.h"
|
||||
#include "stm32l4xx_ll_rcc.h"
|
||||
#include "stm32l4xx_ll_system.h"
|
||||
#include "stm32l4xx_ll_pwr.h"
|
||||
#include "stm32l4xx_ll_utils.h"
|
||||
#include "stm32l4xx_ll_cortex.h"
|
||||
#include "stm32l4xx_ll_gpio.h"
|
||||
#include "stm32l4xx_ll_usart.h"
|
||||
#include "stm32l4xx_ll_bus.h"
|
||||
#include "stm32l4xx_ll_usb.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "stm32l4xx_hal_pcd.h"
|
||||
|
||||
#include "usbd_core.h"
|
||||
#include "usbd_desc.h"
|
||||
#include "usbd_hid.h"
|
||||
/*#include "usbd_hid.h"*/
|
||||
|
||||
#include APP_CONFIG
|
||||
#include "flash.h"
|
||||
#include "rng.h"
|
||||
#include "led.h"
|
||||
#include "cbor.h"
|
||||
#include "device.h"
|
||||
#include "ctaphid.h"
|
||||
//#include "bsp.h"
|
||||
#include "util.h"
|
||||
#include "fifo.h"
|
||||
#include "log.h"
|
||||
#include "ctap.h"
|
||||
#include APP_CONFIG
|
||||
|
||||
#ifdef TEST_SOLO_STM32
|
||||
#define Error_Handler() _Error_Handler(__FILE__,__LINE__)
|
||||
#define PAGE_SIZE 2048
|
||||
#define PAGES 128
|
||||
// Pages 119-127 are data
|
||||
#define COUNTER2_PAGE (PAGES - 4)
|
||||
#define COUNTER1_PAGE (PAGES - 3)
|
||||
#define STATE2_PAGE (PAGES - 2)
|
||||
#define STATE1_PAGE (PAGES - 1)
|
||||
#if !defined(TEST)
|
||||
|
||||
|
||||
uint32_t __90_ms = 0;
|
||||
#define IS_BUTTON_PRESSED() (0 == (LL_GPIO_ReadInputPort(SOLO_BUTTON_PORT) & SOLO_BUTTON_PIN))
|
||||
|
||||
// Timer6 overflow handler. happens every ~90ms.
|
||||
void TIM6_DAC_IRQHandler()
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// timer is only 16 bits, so roll it over here
|
||||
TIM6->SR = 0;
|
||||
__90_ms += 1;
|
||||
}
|
||||
uint8_t hidmsg[64];
|
||||
uint32_t t1 = 0;
|
||||
|
||||
set_logging_mask(
|
||||
/*0*/
|
||||
//TAG_GEN|
|
||||
// TAG_MC |
|
||||
// TAG_GA |
|
||||
TAG_WALLET |
|
||||
TAG_STOR |
|
||||
//TAG_NFC_APDU |
|
||||
TAG_NFC |
|
||||
//TAG_CP |
|
||||
// TAG_CTAP|
|
||||
//TAG_HID|
|
||||
TAG_U2F|
|
||||
//TAG_PARSE |
|
||||
//TAG_TIME|
|
||||
// TAG_DUMP|
|
||||
TAG_GREEN|
|
||||
TAG_RED|
|
||||
TAG_EXT|
|
||||
TAG_CCID|
|
||||
TAG_ERR
|
||||
);
|
||||
|
||||
device_init(argc, argv);
|
||||
|
||||
memset(hidmsg,0,sizeof(hidmsg));
|
||||
|
||||
|
||||
uint32_t millis()
|
||||
{
|
||||
return (((uint32_t)TIM6->CNT) + (__90_ms * 90));
|
||||
}
|
||||
void _Error_Handler(char *file, int line)
|
||||
{
|
||||
while(1)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
uint32_t i = 5;
|
||||
|
||||
hw_init();
|
||||
|
||||
LL_GPIO_SetPinMode(SOLO_BUTTON_PORT,SOLO_BUTTON_PIN,LL_GPIO_MODE_INPUT);
|
||||
LL_GPIO_SetPinPull(SOLO_BUTTON_PORT,SOLO_BUTTON_PIN,LL_GPIO_PULL_UP);
|
||||
flash_option_bytes_init(1);
|
||||
|
||||
while (1)
|
||||
{
|
||||
uint32_t t0 = millis() % 750;
|
||||
if (! IS_BUTTON_PRESSED())
|
||||
if (millis() - t1 > HEARTBEAT_PERIOD)
|
||||
{
|
||||
if (t0 < 750*1/3)
|
||||
{
|
||||
led_rgb(0 | (0 << 8) | (i << 17));
|
||||
}
|
||||
else if (t0 < 750*2/3)
|
||||
{
|
||||
led_rgb(0 | (i << 8) | (0 << 16));
|
||||
}
|
||||
else
|
||||
{
|
||||
led_rgb(i | (0 << 8) | (0 << 16));
|
||||
}
|
||||
heartbeat();
|
||||
t1 = millis();
|
||||
}
|
||||
|
||||
device_manage();
|
||||
|
||||
if (usbhid_recv(hidmsg) > 0)
|
||||
{
|
||||
ctaphid_handle_packet(hidmsg);
|
||||
memset(hidmsg, 0, sizeof(hidmsg));
|
||||
}
|
||||
else
|
||||
{
|
||||
led_rgb(0x151515);
|
||||
}
|
||||
ctaphid_check_timeouts();
|
||||
|
||||
}
|
||||
|
||||
// Should never get here
|
||||
usbhid_close();
|
||||
printf1(TAG_GREEN, "done\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
@ -17,8 +17,11 @@
|
||||
#define COUNTER1_PAGE (PAGES - 3)
|
||||
|
||||
// State of FIDO2 application
|
||||
#define STATE2_PAGE (PAGES - 2)
|
||||
#define STATE1_PAGE (PAGES - 1)
|
||||
#define STATE2_PAGE (PAGES - 2)
|
||||
#define STATE1_PAGE (PAGES - 1)
|
||||
|
||||
#define STATE1_PAGE_ADDR (0x08000000 + ((STATE1_PAGE)*PAGE_SIZE))
|
||||
#define STATE2_PAGE_ADDR (0x08000000 + ((STATE2_PAGE)*PAGE_SIZE))
|
||||
|
||||
// Storage of FIDO2 resident keys
|
||||
#define RK_NUM_PAGES 10
|
||||
@ -32,15 +35,51 @@
|
||||
#define APPLICATION_START_ADDR (0x08000000 + ((APPLICATION_START_PAGE)*PAGE_SIZE))
|
||||
|
||||
// where attestation key is located
|
||||
#define ATTESTATION_KEY_PAGE (PAGES - 15)
|
||||
#define ATTESTATION_KEY_ADDR (0x08000000 + ATTESTATION_KEY_PAGE*PAGE_SIZE)
|
||||
#define ATTESTATION_PAGE (PAGES - 15)
|
||||
#define ATTESTATION_PAGE_ADDR (0x08000000 + ATTESTATION_PAGE*PAGE_SIZE)
|
||||
|
||||
// End of application code. Leave some extra room for future data storage.
|
||||
// NOT included in application
|
||||
#define APPLICATION_END_PAGE ((PAGES - 19))
|
||||
#define APPLICATION_END_PAGE ((PAGES - 20))
|
||||
#define APPLICATION_END_ADDR ((0x08000000 + ((APPLICATION_END_PAGE)*PAGE_SIZE))-8)
|
||||
|
||||
// Bootloader state.
|
||||
#define AUTH_WORD_ADDR (APPLICATION_END_ADDR)
|
||||
|
||||
#define LAST_ADDR (APPLICATION_END_ADDR-2048 + 8)
|
||||
#define BOOT_VERSION_PAGE (APPLICATION_END_PAGE)
|
||||
#define BOOT_VERSION_ADDR (0x08000000 + BOOT_VERSION_PAGE*FLASH_PAGE_SIZE + 8)
|
||||
#define LAST_PAGE (APPLICATION_END_PAGE-1)
|
||||
|
||||
struct flash_memory_st{
|
||||
uint8_t bootloader[APPLICATION_START_PAGE*2*1024];
|
||||
uint8_t application[(APPLICATION_END_PAGE-APPLICATION_START_PAGE)*2*1024-8];
|
||||
uint8_t auth_word[4];
|
||||
uint8_t bootloader_disabled[4];
|
||||
// place for more user data
|
||||
uint8_t _reserved_application_end_mark[8];
|
||||
uint8_t bootloader_data[2*1024-8];
|
||||
uint8_t user_data[38*1024];
|
||||
} __attribute__((packed));
|
||||
|
||||
typedef struct flash_memory_st flash_memory_st;
|
||||
|
||||
#include <assert.h>
|
||||
static_assert(sizeof(flash_memory_st) == 256*1024, "Data structure doesn't match flash size");
|
||||
|
||||
#define ATTESTATION_CONFIGURED_TAG 0xaa551e79
|
||||
|
||||
struct flash_attestation_page{
|
||||
uint8_t attestation_key[32];
|
||||
// DWORD padded.
|
||||
uint64_t device_settings;
|
||||
uint64_t attestation_cert_size;
|
||||
uint8_t attestation_cert[2048 - 32 - 8 - 8];
|
||||
} __attribute__((packed));
|
||||
|
||||
typedef struct flash_attestation_page flash_attestation_page;
|
||||
|
||||
static_assert(sizeof(flash_attestation_page) == 2048, "Data structure doesn't match flash size");
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -14,6 +14,33 @@
|
||||
|
||||
#define IS_IRQ_ACTIVE() (1 == (LL_GPIO_ReadInputPort(SOLO_AMS_IRQ_PORT) & SOLO_AMS_IRQ_PIN))
|
||||
|
||||
// chain buffer for 61XX responses
|
||||
static uint8_t chain_buffer[2048] = {0};
|
||||
static size_t chain_buffer_len = 0;
|
||||
static bool chain_buffer_tx = false;
|
||||
static uint8_t current_cid = 0;
|
||||
|
||||
// forward declarations
|
||||
void rblock_acknowledge(uint8_t req0, bool ack);
|
||||
|
||||
uint8_t p14443_have_cid(uint8_t pcb) {
|
||||
// CID
|
||||
if (pcb & 0x08)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t p14443_block_offset(uint8_t pcb) {
|
||||
uint8_t offset = 1;
|
||||
// NAD following
|
||||
if (pcb & 0x04) offset++;
|
||||
// CID following
|
||||
if (pcb & 0x08) offset++;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
// Capability container
|
||||
const CAPABILITY_CONTAINER NFC_CC = {
|
||||
.cclen_hi = 0x00, .cclen_lo = 0x0f,
|
||||
@ -82,19 +109,27 @@ int nfc_init()
|
||||
return NFC_IS_NA;
|
||||
}
|
||||
|
||||
static uint8_t gl_int0 = 0;
|
||||
void process_int0(uint8_t int0)
|
||||
{
|
||||
|
||||
gl_int0 = int0;
|
||||
}
|
||||
|
||||
bool ams_wait_for_tx(uint32_t timeout_ms)
|
||||
{
|
||||
if (gl_int0 & AMS_INT_TXE) {
|
||||
uint8_t int0 = ams_read_reg(AMS_REG_INT0);
|
||||
process_int0(int0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t tstart = millis();
|
||||
while (tstart + timeout_ms > millis())
|
||||
{
|
||||
uint8_t int0 = ams_read_reg(AMS_REG_INT0);
|
||||
if (int0) process_int0(int0);
|
||||
if (int0 & AMS_INT_TXE)
|
||||
process_int0(int0);
|
||||
if (int0 & AMS_INT_TXE || int0 & AMS_INT_RXE)
|
||||
return true;
|
||||
|
||||
delay(1);
|
||||
@ -111,7 +146,13 @@ bool ams_receive_with_timeout(uint32_t timeout_ms, uint8_t * data, int maxlen, i
|
||||
uint32_t tstart = millis();
|
||||
while (tstart + timeout_ms > millis())
|
||||
{
|
||||
uint8_t int0 = ams_read_reg(AMS_REG_INT0);
|
||||
uint8_t int0 = 0;
|
||||
if (gl_int0 & AMS_INT_RXE) {
|
||||
int0 = gl_int0;
|
||||
} else {
|
||||
int0 = ams_read_reg(AMS_REG_INT0);
|
||||
process_int0(int0);
|
||||
}
|
||||
uint8_t buffer_status2 = ams_read_reg(AMS_REG_BUF2);
|
||||
|
||||
if (buffer_status2 && (int0 & AMS_INT_RXE))
|
||||
@ -161,14 +202,25 @@ bool nfc_write_response_ex(uint8_t req0, uint8_t * data, uint8_t len, uint16_t r
|
||||
if (len > 32 - 3)
|
||||
return false;
|
||||
|
||||
res[0] = NFC_CMD_IBLOCK | (req0 & 3);
|
||||
res[0] = NFC_CMD_IBLOCK | (req0 & 0x0f);
|
||||
res[1] = current_cid;
|
||||
res[2] = 0;
|
||||
|
||||
uint8_t block_offset = p14443_block_offset(req0);
|
||||
|
||||
if (len && data)
|
||||
memcpy(&res[1], data, len);
|
||||
memcpy(&res[block_offset], data, len);
|
||||
|
||||
res[len + 1] = resp >> 8;
|
||||
res[len + 2] = resp & 0xff;
|
||||
nfc_write_frame(res, 3 + len);
|
||||
res[len + block_offset + 0] = resp >> 8;
|
||||
res[len + block_offset + 1] = resp & 0xff;
|
||||
|
||||
nfc_write_frame(res, block_offset + len + 2);
|
||||
|
||||
if (!ams_wait_for_tx(1))
|
||||
{
|
||||
printf1(TAG_NFC, "TX resp timeout. len: %d \r\n", len);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -178,25 +230,30 @@ bool nfc_write_response(uint8_t req0, uint16_t resp)
|
||||
return nfc_write_response_ex(req0, NULL, 0, resp);
|
||||
}
|
||||
|
||||
void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len)
|
||||
void nfc_write_response_chaining_plain(uint8_t req0, uint8_t * data, int len)
|
||||
{
|
||||
uint8_t res[32 + 2];
|
||||
int sendlen = 0;
|
||||
uint8_t iBlock = NFC_CMD_IBLOCK | (req0 & 3);
|
||||
uint8_t iBlock = NFC_CMD_IBLOCK | (req0 & 0x0f);
|
||||
uint8_t block_offset = p14443_block_offset(req0);
|
||||
|
||||
if (len <= 31)
|
||||
{
|
||||
uint8_t res[32] = {0};
|
||||
res[0] = iBlock;
|
||||
res[0] = iBlock;
|
||||
res[1] = current_cid;
|
||||
res[2] = 0;
|
||||
if (len && data)
|
||||
memcpy(&res[1], data, len);
|
||||
nfc_write_frame(res, len + 1);
|
||||
memcpy(&res[block_offset], data, len);
|
||||
nfc_write_frame(res, len + block_offset);
|
||||
} else {
|
||||
int sendlen = 0;
|
||||
do {
|
||||
// transmit I block
|
||||
int vlen = MIN(31, len - sendlen);
|
||||
res[0] = iBlock;
|
||||
memcpy(&res[1], &data[sendlen], vlen);
|
||||
int vlen = MIN(32 - block_offset, len - sendlen);
|
||||
res[0] = iBlock;
|
||||
res[1] = current_cid;
|
||||
res[2] = 0;
|
||||
memcpy(&res[block_offset], &data[sendlen], vlen);
|
||||
|
||||
// if not a last block
|
||||
if (vlen + sendlen < len)
|
||||
@ -205,15 +262,15 @@ void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len)
|
||||
}
|
||||
|
||||
// send data
|
||||
nfc_write_frame(res, vlen + 1);
|
||||
nfc_write_frame(res, vlen + block_offset);
|
||||
sendlen += vlen;
|
||||
|
||||
// wait for transmit (32 bytes aprox 2,5ms)
|
||||
// if (!ams_wait_for_tx(10))
|
||||
// {
|
||||
// printf1(TAG_NFC, "TX timeout. slen: %d \r\n", sendlen);
|
||||
// break;
|
||||
// }
|
||||
if (!ams_wait_for_tx(5))
|
||||
{
|
||||
printf1(TAG_NFC, "TX timeout. slen: %d \r\n", sendlen);
|
||||
break;
|
||||
}
|
||||
|
||||
// if needs to receive R block (not a last block)
|
||||
if (res[0] & 0x10)
|
||||
@ -225,10 +282,25 @@ void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len)
|
||||
printf1(TAG_NFC, "R block RX timeout %d/%d.\r\n",sendlen,len);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!IS_RBLOCK(recbuf[0]))
|
||||
{
|
||||
printf1(TAG_NFC, "R block RX error. Not a R block(0x%02x) %d/%d.\r\n", recbuf[0], sendlen, len);
|
||||
break;
|
||||
}
|
||||
|
||||
// NAK check
|
||||
if (recbuf[0] & NFC_CMD_RBLOCK_ACK)
|
||||
{
|
||||
rblock_acknowledge(recbuf[0], true);
|
||||
printf1(TAG_NFC, "R block RX error. NAK received. %d/%d.\r\n", recbuf[0], sendlen, len);
|
||||
break;
|
||||
}
|
||||
|
||||
if (reclen != 1)
|
||||
uint8_t rblock_offset = p14443_block_offset(recbuf[0]);
|
||||
if (reclen != rblock_offset)
|
||||
{
|
||||
printf1(TAG_NFC, "R block length error. len: %d. %d/%d \r\n", reclen,sendlen,len);
|
||||
printf1(TAG_NFC, "R block length error. len: %d. %d/%d \r\n", reclen, sendlen, len);
|
||||
dump_hex1(TAG_NFC, recbuf, reclen);
|
||||
break;
|
||||
}
|
||||
@ -245,6 +317,38 @@ void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len)
|
||||
}
|
||||
}
|
||||
|
||||
void append_get_response(uint8_t *data, size_t rest_len)
|
||||
{
|
||||
data[0] = 0x61;
|
||||
data[1] = 0x00;
|
||||
if (rest_len <= 0xff)
|
||||
data[1] = rest_len & 0xff;
|
||||
}
|
||||
|
||||
void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len, bool extapdu)
|
||||
{
|
||||
chain_buffer_len = 0;
|
||||
chain_buffer_tx = true;
|
||||
|
||||
// if we dont need to break data to parts that need to exchange via GET RESPONSE command (ISO 7816-4 7.1.3)
|
||||
if (len <= 255 || extapdu)
|
||||
{
|
||||
nfc_write_response_chaining_plain(req0, data, len);
|
||||
} else {
|
||||
size_t pcklen = MIN(253, len);
|
||||
chain_buffer_len = len - pcklen;
|
||||
printf1(TAG_NFC, "61XX chaining %d/%d.\r\n", pcklen, chain_buffer_len);
|
||||
|
||||
memmove(chain_buffer, data, pcklen);
|
||||
append_get_response(&chain_buffer[pcklen], chain_buffer_len);
|
||||
|
||||
nfc_write_response_chaining_plain(req0, chain_buffer, pcklen + 2); // 2 for 61XX
|
||||
|
||||
// put the rest data into chain buffer
|
||||
memmove(chain_buffer, &data[pcklen], chain_buffer_len);
|
||||
}
|
||||
}
|
||||
|
||||
// WTX on/off:
|
||||
// sends/receives WTX frame to reader every `WTX_time` time in ms
|
||||
// works via timer interrupts
|
||||
@ -255,7 +359,7 @@ static uint32_t WTX_timer;
|
||||
|
||||
bool WTX_process(int read_timeout);
|
||||
|
||||
void WTX_clear()
|
||||
void WTX_clear(void)
|
||||
{
|
||||
WTX_sent = false;
|
||||
WTX_fail = false;
|
||||
@ -270,7 +374,7 @@ bool WTX_on(int WTX_time)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WTX_off()
|
||||
bool WTX_off(void)
|
||||
{
|
||||
WTX_timer = 0;
|
||||
|
||||
@ -294,10 +398,10 @@ bool WTX_off()
|
||||
return true;
|
||||
}
|
||||
|
||||
void WTX_timer_exec()
|
||||
void WTX_timer_exec(void)
|
||||
{
|
||||
// condition: (timer on) or (not expired[300ms])
|
||||
if ((WTX_timer <= 0) || WTX_timer + 300 > millis())
|
||||
if ((WTX_timer == 0) || WTX_timer + 300 > millis())
|
||||
return;
|
||||
|
||||
WTX_process(10);
|
||||
@ -308,12 +412,12 @@ void WTX_timer_exec()
|
||||
// read timeout must be 10 ms to call from interrupt
|
||||
bool WTX_process(int read_timeout)
|
||||
{
|
||||
uint8_t wtx[] = {0xf2, 0x01};
|
||||
if (WTX_fail)
|
||||
return false;
|
||||
|
||||
if (!WTX_sent)
|
||||
{
|
||||
uint8_t wtx[] = {0xf2, 0x01};
|
||||
nfc_write_frame(wtx, sizeof(wtx));
|
||||
WTX_sent = true;
|
||||
return true;
|
||||
@ -371,39 +475,74 @@ int answer_rats(uint8_t parameter)
|
||||
|
||||
|
||||
nfc_write_frame(res, sizeof(res));
|
||||
ams_wait_for_tx(10);
|
||||
if (!ams_wait_for_tx(10))
|
||||
{
|
||||
printf1(TAG_NFC, "RATS TX timeout.\r\n");
|
||||
ams_write_command(AMS_CMD_DEFAULT);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rblock_acknowledge()
|
||||
void rblock_acknowledge(uint8_t req0, bool ack)
|
||||
{
|
||||
uint8_t buf[32];
|
||||
uint8_t buf[32] = {0};
|
||||
|
||||
uint8_t block_offset = p14443_block_offset(req0);
|
||||
NFC_STATE.block_num = !NFC_STATE.block_num;
|
||||
buf[0] = NFC_CMD_RBLOCK | NFC_STATE.block_num;
|
||||
nfc_write_frame(buf,1);
|
||||
|
||||
buf[0] = NFC_CMD_RBLOCK | (req0 & 0x0f);
|
||||
buf[1] = current_cid;
|
||||
// iso14443-4:2001 page 16. ACK, if bit is set to 0, NAK, if bit is set to 1
|
||||
if (!ack)
|
||||
buf[0] |= NFC_CMD_RBLOCK_ACK;
|
||||
|
||||
nfc_write_frame(buf, block_offset);
|
||||
}
|
||||
|
||||
// international AID = RID:PIX
|
||||
// RID length == 5 bytes
|
||||
// usually aid length must be between 5 and 16 bytes
|
||||
int applet_cmp(uint8_t * aid, int len, uint8_t * const_aid, int const_len)
|
||||
{
|
||||
if (len > const_len)
|
||||
return 10;
|
||||
|
||||
// if international AID
|
||||
if ((const_aid[0] & 0xf0) == 0xa0)
|
||||
{
|
||||
if (len < 5)
|
||||
return 11;
|
||||
return memcmp(aid, const_aid, MIN(len, const_len));
|
||||
} else {
|
||||
if (len != const_len)
|
||||
return 11;
|
||||
|
||||
return memcmp(aid, const_aid, const_len);
|
||||
}
|
||||
}
|
||||
|
||||
// Selects application. Returns 1 if success, 0 otherwise
|
||||
int select_applet(uint8_t * aid, int len)
|
||||
{
|
||||
if (memcmp(aid,AID_FIDO,sizeof(AID_FIDO)) == 0)
|
||||
if (applet_cmp(aid, len, (uint8_t *)AID_FIDO, sizeof(AID_FIDO) - 1) == 0)
|
||||
{
|
||||
NFC_STATE.selected_applet = APP_FIDO;
|
||||
return APP_FIDO;
|
||||
}
|
||||
else if (memcmp(aid,AID_NDEF_TYPE_4,sizeof(AID_NDEF_TYPE_4)) == 0)
|
||||
else if (applet_cmp(aid, len, (uint8_t *)AID_NDEF_TYPE_4, sizeof(AID_NDEF_TYPE_4) - 1) == 0)
|
||||
{
|
||||
NFC_STATE.selected_applet = APP_NDEF_TYPE_4;
|
||||
return APP_NDEF_TYPE_4;
|
||||
}
|
||||
else if (memcmp(aid,AID_CAPABILITY_CONTAINER,sizeof(AID_CAPABILITY_CONTAINER)) == 0)
|
||||
else if (applet_cmp(aid, len, (uint8_t *)AID_CAPABILITY_CONTAINER, sizeof(AID_CAPABILITY_CONTAINER) - 1) == 0)
|
||||
{
|
||||
NFC_STATE.selected_applet = APP_CAPABILITY_CONTAINER;
|
||||
return APP_CAPABILITY_CONTAINER;
|
||||
}
|
||||
else if (memcmp(aid,AID_NDEF_TAG,sizeof(AID_NDEF_TAG)) == 0)
|
||||
else if (applet_cmp(aid, len, (uint8_t *)AID_NDEF_TAG, sizeof(AID_NDEF_TAG) - 1) == 0)
|
||||
{
|
||||
NFC_STATE.selected_applet = APP_NDEF_TAG;
|
||||
return APP_NDEF_TAG;
|
||||
@ -411,27 +550,71 @@ int select_applet(uint8_t * aid, int len)
|
||||
return APP_NOTHING;
|
||||
}
|
||||
|
||||
void nfc_process_iblock(uint8_t * buf, int len)
|
||||
void apdu_process(uint8_t buf0, uint8_t *apduptr, APDU_STRUCT *apdu)
|
||||
{
|
||||
APDU_HEADER * apdu = (APDU_HEADER *)(buf + 1);
|
||||
uint8_t * payload = buf + 1 + 5;
|
||||
uint8_t plen = apdu->lc;
|
||||
int selected;
|
||||
CTAP_RESPONSE ctap_resp;
|
||||
int status;
|
||||
uint16_t reslen;
|
||||
|
||||
printf1(TAG_NFC,"Iblock: ");
|
||||
dump_hex1(TAG_NFC, buf, len);
|
||||
|
||||
// check CLA
|
||||
if (apdu->cla != 0x00 && apdu->cla != 0x80) {
|
||||
printf1(TAG_NFC, "Unknown CLA %02x\r\n", apdu->cla);
|
||||
nfc_write_response(buf0, SW_CLA_INVALID);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO this needs to be organized better
|
||||
switch(apdu->ins)
|
||||
{
|
||||
case APDU_INS_SELECT:
|
||||
if (plen > len - 6)
|
||||
// ISO 7816. 7.1 GET RESPONSE command
|
||||
case APDU_GET_RESPONSE:
|
||||
if (apdu->p1 != 0x00 || apdu->p2 != 0x00)
|
||||
{
|
||||
printf1(TAG_ERR, "Truncating APDU length %d\r\n", apdu->lc);
|
||||
plen = len-6;
|
||||
nfc_write_response(buf0, SW_INCORRECT_P1P2);
|
||||
printf1(TAG_NFC, "P1 or P2 error\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// too many bytes needs. 0x00 and 0x100 - any length
|
||||
if (apdu->le != 0 && apdu->le != 0x100 && apdu->le > chain_buffer_len)
|
||||
{
|
||||
uint16_t wlresp = SW_WRONG_LENGTH; // here can be 6700, 6C00, 6FXX. but the most standard way - 67XX or 6700
|
||||
if (chain_buffer_len <= 0xff)
|
||||
wlresp += chain_buffer_len & 0xff;
|
||||
nfc_write_response(buf0, wlresp);
|
||||
printf1(TAG_NFC, "buffer length less than requesteds\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// create temporary packet
|
||||
uint8_t pck[255] = {0};
|
||||
size_t pcklen = 253;
|
||||
if (apdu->le)
|
||||
pcklen = apdu->le;
|
||||
if (pcklen > chain_buffer_len)
|
||||
pcklen = chain_buffer_len;
|
||||
|
||||
printf1(TAG_NFC, "GET RESPONSE. pck len: %d buffer len: %d\r\n", pcklen, chain_buffer_len);
|
||||
|
||||
// create packet and add 61XX there if we have another portion(s) of data
|
||||
memmove(pck, chain_buffer, pcklen);
|
||||
size_t dlen = 0;
|
||||
if (chain_buffer_len - pcklen)
|
||||
{
|
||||
append_get_response(&pck[pcklen], chain_buffer_len - pcklen);
|
||||
dlen = 2;
|
||||
}
|
||||
|
||||
// send
|
||||
nfc_write_response_chaining_plain(buf0, pck, pcklen + dlen); // dlen for 61XX
|
||||
|
||||
// shift the buffer
|
||||
chain_buffer_len -= pcklen;
|
||||
memmove(chain_buffer, &chain_buffer[pcklen], chain_buffer_len);
|
||||
break;
|
||||
|
||||
case APDU_INS_SELECT:
|
||||
// if (apdu->p1 == 0 && apdu->p2 == 0x0c)
|
||||
// {
|
||||
// printf1(TAG_NFC,"Select NDEF\r\n");
|
||||
@ -446,53 +629,49 @@ void nfc_process_iblock(uint8_t * buf, int len)
|
||||
// }
|
||||
// else
|
||||
{
|
||||
selected = select_applet(payload, plen);
|
||||
selected = select_applet(apdu->data, apdu->lc);
|
||||
if (selected == APP_FIDO)
|
||||
{
|
||||
// block = buf[0] & 1;
|
||||
// block = NFC_STATE.block_num;
|
||||
// block = !block;
|
||||
// NFC_STATE.block_num = block;
|
||||
// NFC_STATE.block_num = block;
|
||||
nfc_write_response_ex(buf[0], (uint8_t *)"U2F_V2", 6, SW_SUCCESS);
|
||||
nfc_write_response_ex(buf0, (uint8_t *)"U2F_V2", 6, SW_SUCCESS);
|
||||
printf1(TAG_NFC, "FIDO applet selected.\r\n");
|
||||
}
|
||||
else if (selected != APP_NOTHING)
|
||||
{
|
||||
nfc_write_response(buf[0], SW_SUCCESS);
|
||||
nfc_write_response(buf0, SW_SUCCESS);
|
||||
printf1(TAG_NFC, "SELECTED %d\r\n", selected);
|
||||
}
|
||||
else
|
||||
{
|
||||
nfc_write_response(buf[0], SW_FILE_NOT_FOUND);
|
||||
printf1(TAG_NFC, "NOT selected\r\n"); dump_hex1(TAG_NFC,payload, plen);
|
||||
nfc_write_response(buf0, SW_FILE_NOT_FOUND);
|
||||
printf1(TAG_NFC, "NOT selected "); dump_hex1(TAG_NFC, apdu->data, apdu->lc);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case APDU_FIDO_U2F_VERSION:
|
||||
if (NFC_STATE.selected_applet != APP_FIDO) {
|
||||
nfc_write_response(buf[0], SW_INS_INVALID);
|
||||
nfc_write_response(buf0, SW_INS_INVALID);
|
||||
break;
|
||||
}
|
||||
|
||||
printf1(TAG_NFC, "U2F GetVersion command.\r\n");
|
||||
|
||||
nfc_write_response_ex(buf[0], (uint8_t *)"U2F_V2", 6, SW_SUCCESS);
|
||||
u2f_request_nfc(apduptr, apdu->data, apdu->lc, &ctap_resp);
|
||||
nfc_write_response_chaining(buf0, ctap_resp.data, ctap_resp.length, apdu->extended_apdu);
|
||||
break;
|
||||
|
||||
case APDU_FIDO_U2F_REGISTER:
|
||||
if (NFC_STATE.selected_applet != APP_FIDO) {
|
||||
nfc_write_response(buf[0], SW_INS_INVALID);
|
||||
nfc_write_response(buf0, SW_INS_INVALID);
|
||||
break;
|
||||
}
|
||||
|
||||
printf1(TAG_NFC, "U2F Register command.\r\n");
|
||||
|
||||
if (plen != 64)
|
||||
if (apdu->lc != 64)
|
||||
{
|
||||
printf1(TAG_NFC, "U2F Register request length error. len=%d.\r\n", plen);
|
||||
nfc_write_response(buf[0], SW_WRONG_LENGTH);
|
||||
printf1(TAG_NFC, "U2F Register request length error. len=%d.\r\n", apdu->lc);
|
||||
nfc_write_response(buf0, SW_WRONG_LENGTH);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -502,66 +681,64 @@ void nfc_process_iblock(uint8_t * buf, int len)
|
||||
// WTX_on(WTX_TIME_DEFAULT);
|
||||
// SystemClock_Config_LF32();
|
||||
// delay(300);
|
||||
if (device_is_nfc()) device_set_clock_rate(DEVICE_LOW_POWER_FAST);;
|
||||
u2f_request_nfc(&buf[1], len, &ctap_resp);
|
||||
if (device_is_nfc()) device_set_clock_rate(DEVICE_LOW_POWER_IDLE);;
|
||||
if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_FAST);
|
||||
u2f_request_nfc(apduptr, apdu->data, apdu->lc, &ctap_resp);
|
||||
if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_IDLE);
|
||||
// if (!WTX_off())
|
||||
// return;
|
||||
|
||||
printf1(TAG_NFC, "U2F resp len: %d\r\n", ctap_resp.length);
|
||||
printf1(TAG_NFC,"U2F Register P2 took %d\r\n", timestamp());
|
||||
nfc_write_response_chaining(buf[0], ctap_resp.data, ctap_resp.length);
|
||||
|
||||
// printf1(TAG_NFC, "U2F resp len: %d\r\n", ctap_resp.length);
|
||||
|
||||
|
||||
|
||||
nfc_write_response_chaining(buf0, ctap_resp.data, ctap_resp.length, apdu->extended_apdu);
|
||||
|
||||
printf1(TAG_NFC,"U2F Register answered %d (took %d)\r\n", millis(), timestamp());
|
||||
break;
|
||||
|
||||
case APDU_FIDO_U2F_AUTHENTICATE:
|
||||
if (NFC_STATE.selected_applet != APP_FIDO) {
|
||||
nfc_write_response(buf[0], SW_INS_INVALID);
|
||||
nfc_write_response(buf0, SW_INS_INVALID);
|
||||
break;
|
||||
}
|
||||
|
||||
printf1(TAG_NFC, "U2F Authenticate command.\r\n");
|
||||
|
||||
if (plen != 64 + 1 + buf[6 + 64])
|
||||
if (apdu->lc != 64 + 1 + apdu->data[64])
|
||||
{
|
||||
delay(5);
|
||||
printf1(TAG_NFC, "U2F Authenticate request length error. len=%d keyhlen=%d.\r\n", plen, buf[6 + 64]);
|
||||
nfc_write_response(buf[0], SW_WRONG_LENGTH);
|
||||
printf1(TAG_NFC, "U2F Authenticate request length error. len=%d keyhlen=%d.\r\n", apdu->lc, apdu->data[64]);
|
||||
nfc_write_response(buf0, SW_WRONG_LENGTH);
|
||||
return;
|
||||
}
|
||||
|
||||
timestamp();
|
||||
// WTX_on(WTX_TIME_DEFAULT);
|
||||
u2f_request_nfc(&buf[1], len, &ctap_resp);
|
||||
u2f_request_nfc(apduptr, apdu->data, apdu->lc, &ctap_resp);
|
||||
// if (!WTX_off())
|
||||
// return;
|
||||
|
||||
printf1(TAG_NFC, "U2F resp len: %d\r\n", ctap_resp.length);
|
||||
printf1(TAG_NFC,"U2F Authenticate processing %d (took %d)\r\n", millis(), timestamp());
|
||||
nfc_write_response_chaining(buf[0], ctap_resp.data, ctap_resp.length);
|
||||
nfc_write_response_chaining(buf0, ctap_resp.data, ctap_resp.length, apdu->extended_apdu);
|
||||
printf1(TAG_NFC,"U2F Authenticate answered %d (took %d)\r\n", millis(), timestamp);
|
||||
break;
|
||||
|
||||
case APDU_FIDO_NFCCTAP_MSG:
|
||||
if (NFC_STATE.selected_applet != APP_FIDO) {
|
||||
nfc_write_response(buf[0], SW_INS_INVALID);
|
||||
break;
|
||||
nfc_write_response(buf0, SW_INS_INVALID);
|
||||
return;
|
||||
}
|
||||
|
||||
printf1(TAG_NFC, "FIDO2 CTAP message. %d\r\n", timestamp());
|
||||
|
||||
WTX_on(WTX_TIME_DEFAULT);
|
||||
// WTX_on(WTX_TIME_DEFAULT);
|
||||
device_disable_up(true);
|
||||
ctap_response_init(&ctap_resp);
|
||||
status = ctap_request(payload, plen, &ctap_resp);
|
||||
if (!WTX_off())
|
||||
return;
|
||||
status = ctap_request(apdu->data, apdu->lc, &ctap_resp);
|
||||
device_disable_up(false);
|
||||
// if (!WTX_off())
|
||||
// return;
|
||||
|
||||
printf1(TAG_NFC, "CTAP resp: 0x%02<EFBFBD> len: %d\r\n", status, ctap_resp.length);
|
||||
printf1(TAG_NFC, "CTAP resp: 0x%02x len: %d\r\n", status, ctap_resp.length);
|
||||
|
||||
if (status == CTAP1_ERR_SUCCESS)
|
||||
{
|
||||
@ -575,55 +752,111 @@ void nfc_process_iblock(uint8_t * buf, int len)
|
||||
ctap_resp.data[ctap_resp.length - 1] = SW_SUCCESS & 0xff;
|
||||
|
||||
printf1(TAG_NFC,"CTAP processing %d (took %d)\r\n", millis(), timestamp());
|
||||
nfc_write_response_chaining(buf[0], ctap_resp.data, ctap_resp.length);
|
||||
nfc_write_response_chaining(buf0, ctap_resp.data, ctap_resp.length, apdu->extended_apdu);
|
||||
printf1(TAG_NFC,"CTAP answered %d (took %d)\r\n", millis(), timestamp());
|
||||
break;
|
||||
|
||||
case APDU_INS_READ_BINARY:
|
||||
|
||||
|
||||
// response length
|
||||
reslen = apdu->le & 0xffff;
|
||||
switch(NFC_STATE.selected_applet)
|
||||
{
|
||||
case APP_CAPABILITY_CONTAINER:
|
||||
printf1(TAG_NFC,"APP_CAPABILITY_CONTAINER\r\n");
|
||||
if (plen > 15)
|
||||
{
|
||||
printf1(TAG_ERR, "Truncating requested CC length %d\r\n", apdu->lc);
|
||||
plen = 15;
|
||||
}
|
||||
nfc_write_response_ex(buf[0], (uint8_t *)&NFC_CC, plen, SW_SUCCESS);
|
||||
if (reslen == 0 || reslen > sizeof(NFC_CC))
|
||||
reslen = sizeof(NFC_CC);
|
||||
nfc_write_response_ex(buf0, (uint8_t *)&NFC_CC, reslen, SW_SUCCESS);
|
||||
ams_wait_for_tx(10);
|
||||
break;
|
||||
case APP_NDEF_TAG:
|
||||
printf1(TAG_NFC,"APP_NDEF_TAG\r\n");
|
||||
if (plen > (sizeof(NDEF_SAMPLE) - 1))
|
||||
{
|
||||
printf1(TAG_ERR, "Truncating requested CC length %d\r\n", apdu->lc);
|
||||
plen = sizeof(NDEF_SAMPLE) - 1;
|
||||
}
|
||||
nfc_write_response_ex(buf[0], NDEF_SAMPLE, plen, SW_SUCCESS);
|
||||
if (reslen == 0 || reslen > sizeof(NDEF_SAMPLE) - 1)
|
||||
reslen = sizeof(NDEF_SAMPLE) - 1;
|
||||
nfc_write_response_ex(buf0, NDEF_SAMPLE, reslen, SW_SUCCESS);
|
||||
ams_wait_for_tx(10);
|
||||
break;
|
||||
default:
|
||||
nfc_write_response(buf0, SW_FILE_NOT_FOUND);
|
||||
printf1(TAG_ERR, "No binary applet selected!\r\n");
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case APDU_SOLO_RESET:
|
||||
if (apdu->lc == 4 && !memcmp(apdu->data, "\x12\x56\xab\xf0", 4)) {
|
||||
printf1(TAG_NFC, "Reset...\r\n");
|
||||
nfc_write_response(buf0, SW_SUCCESS);
|
||||
delay(20);
|
||||
device_reboot();
|
||||
while(1);
|
||||
} else {
|
||||
printf1(TAG_NFC, "Reset FAIL\r\n");
|
||||
nfc_write_response(buf0, SW_INS_INVALID);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
printf1(TAG_NFC, "Unknown INS %02x\r\n", apdu->ins);
|
||||
nfc_write_response(buf[0], SW_INS_INVALID);
|
||||
nfc_write_response(buf0, SW_INS_INVALID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_process_iblock(uint8_t * buf, int len)
|
||||
{
|
||||
uint8_t block_offset = p14443_block_offset(buf[0]);
|
||||
|
||||
// clear tx chain buffer if we have some other command than GET RESPONSE
|
||||
if (chain_buffer_tx && buf[block_offset + 1] != APDU_GET_RESPONSE) {
|
||||
chain_buffer_len = 0;
|
||||
chain_buffer_tx = false;
|
||||
}
|
||||
|
||||
APDU_STRUCT apdu;
|
||||
uint16_t ret = apdu_decode(buf + block_offset, len - block_offset, &apdu);
|
||||
if (ret != 0) {
|
||||
printf1(TAG_NFC,"apdu decode error\r\n");
|
||||
nfc_write_response(buf[0], ret);
|
||||
return;
|
||||
}
|
||||
printf1(TAG_NFC,"apdu ok. %scase=%02x cla=%02x ins=%02x p1=%02x p2=%02x lc=%d le=%d\r\n",
|
||||
apdu.extended_apdu ? "[e]":"", apdu.case_type, apdu.cla, apdu.ins, apdu.p1, apdu.p2, apdu.lc, apdu.le);
|
||||
|
||||
// APDU level chaining. ISO7816-4, 5.1.1. class byte
|
||||
if (!chain_buffer_tx && buf[block_offset] & 0x10) {
|
||||
|
||||
if (chain_buffer_len + len > sizeof(chain_buffer)) {
|
||||
nfc_write_response(buf[0], SW_WRONG_LENGTH);
|
||||
return;
|
||||
}
|
||||
|
||||
memmove(&chain_buffer[chain_buffer_len], apdu.data, apdu.lc);
|
||||
chain_buffer_len += apdu.lc;
|
||||
nfc_write_response(buf[0], SW_SUCCESS);
|
||||
printf1(TAG_NFC, "APDU chaining ok. %d/%d\r\n", apdu.lc, chain_buffer_len);
|
||||
return;
|
||||
}
|
||||
|
||||
// if we have ISO 7816 APDU chain - move there all the data
|
||||
if (!chain_buffer_tx && chain_buffer_len > 0) {
|
||||
memmove(&apdu.data[chain_buffer_len], apdu.data, apdu.lc);
|
||||
memmove(apdu.data, chain_buffer, chain_buffer_len);
|
||||
apdu.lc += chain_buffer_len; // here apdu struct does not match with memory!
|
||||
printf1(TAG_NFC, "APDU chaining merge. %d/%d\r\n", chain_buffer_len, apdu.lc);
|
||||
}
|
||||
|
||||
|
||||
apdu_process(buf[0], &buf[block_offset], &apdu);
|
||||
|
||||
printf1(TAG_NFC,"prev.Iblock: ");
|
||||
dump_hex1(TAG_NFC, buf, len);
|
||||
}
|
||||
|
||||
static uint8_t ibuf[1024];
|
||||
static int ibuflen = 0;
|
||||
|
||||
void clear_ibuf()
|
||||
void clear_ibuf(void)
|
||||
{
|
||||
ibuflen = 0;
|
||||
memset(ibuf, 0, sizeof(ibuf));
|
||||
@ -631,19 +864,29 @@ void clear_ibuf()
|
||||
|
||||
void nfc_process_block(uint8_t * buf, unsigned int len)
|
||||
{
|
||||
|
||||
printf1(TAG_NFC, "-----\r\n");
|
||||
if (!len)
|
||||
return;
|
||||
|
||||
if (IS_PPSS_CMD(buf[0]))
|
||||
{
|
||||
printf1(TAG_NFC, "NFC_CMD_PPSS\r\n");
|
||||
printf1(TAG_NFC, "NFC_CMD_PPSS [%d] 0x%02x\r\n", len, (len > 2) ? buf[2] : 0);
|
||||
|
||||
if (buf[1] == 0x11 && (buf[2] & 0x0f) == 0x00) {
|
||||
nfc_write_frame(buf, 1); // ack with correct start byte
|
||||
} else {
|
||||
printf1(TAG_NFC, "NFC_CMD_PPSS ERROR!!!\r\n");
|
||||
nfc_write_frame((uint8_t*)"\x00", 1); // this should not happend. but iso14443-4 dont have NACK here, so just 0x00
|
||||
}
|
||||
}
|
||||
else if (IS_IBLOCK(buf[0]))
|
||||
{
|
||||
uint8_t block_offset = p14443_block_offset(buf[0]);
|
||||
if (p14443_have_cid(buf[0]))
|
||||
current_cid = buf[1];
|
||||
if (buf[0] & 0x10)
|
||||
{
|
||||
printf1(TAG_NFC_APDU, "NFC_CMD_IBLOCK chaining blen=%d len=%d\r\n", ibuflen, len);
|
||||
printf1(TAG_NFC_APDU, "NFC_CMD_IBLOCK chaining blen=%d len=%d offs=%d\r\n", ibuflen, len, block_offset);
|
||||
if (ibuflen + len > sizeof(ibuf))
|
||||
{
|
||||
printf1(TAG_NFC, "I block memory error! must have %d but have only %d\r\n", ibuflen + len, sizeof(ibuf));
|
||||
@ -654,44 +897,46 @@ void nfc_process_block(uint8_t * buf, unsigned int len)
|
||||
printf1(TAG_NFC_APDU,"i> ");
|
||||
dump_hex1(TAG_NFC_APDU, buf, len);
|
||||
|
||||
if (len)
|
||||
if (len > block_offset)
|
||||
{
|
||||
memcpy(&ibuf[ibuflen], &buf[1], len - 1);
|
||||
ibuflen += len - 1;
|
||||
memcpy(&ibuf[ibuflen], &buf[block_offset], len - block_offset);
|
||||
ibuflen += len - block_offset;
|
||||
}
|
||||
|
||||
// send R block
|
||||
uint8_t rb = NFC_CMD_RBLOCK | NFC_CMD_RBLOCK_ACK | (buf[0] & 3);
|
||||
nfc_write_frame(&rb, 1);
|
||||
rblock_acknowledge(buf[0], true);
|
||||
} else {
|
||||
if (ibuflen)
|
||||
{
|
||||
if (len)
|
||||
if (len > block_offset)
|
||||
{
|
||||
memcpy(&ibuf[ibuflen], &buf[1], len - 1);
|
||||
ibuflen += len - 1;
|
||||
memcpy(&ibuf[ibuflen], &buf[block_offset], len - block_offset);
|
||||
ibuflen += len - block_offset;
|
||||
}
|
||||
|
||||
memmove(&ibuf[1], ibuf, ibuflen);
|
||||
ibuf[0] = buf[0];
|
||||
ibuflen++;
|
||||
// add last chaining to top of the block
|
||||
memmove(&ibuf[block_offset], ibuf, ibuflen);
|
||||
memmove(ibuf, buf, block_offset);
|
||||
ibuflen += block_offset;
|
||||
|
||||
printf1(TAG_NFC_APDU, "NFC_CMD_IBLOCK chaining last block. blen=%d len=%d\r\n", ibuflen, len);
|
||||
printf1(TAG_NFC_APDU, "NFC_CMD_IBLOCK chaining last block. blen=%d len=%d offset=%d\r\n", ibuflen, len, block_offset);
|
||||
|
||||
printf1(TAG_NFC_APDU,"i> ");
|
||||
dump_hex1(TAG_NFC_APDU, buf, len);
|
||||
|
||||
nfc_process_iblock(ibuf, ibuflen);
|
||||
} else {
|
||||
// printf1(TAG_NFC, "NFC_CMD_IBLOCK\r\n");
|
||||
nfc_process_iblock(buf, len);
|
||||
memcpy(ibuf, buf, len); // because buf only 32b
|
||||
nfc_process_iblock(ibuf, len);
|
||||
}
|
||||
clear_ibuf();
|
||||
}
|
||||
}
|
||||
else if (IS_RBLOCK(buf[0]))
|
||||
{
|
||||
rblock_acknowledge();
|
||||
if (p14443_have_cid(buf[0]))
|
||||
current_cid = buf[1];
|
||||
rblock_acknowledge(buf[0], true);
|
||||
printf1(TAG_NFC, "NFC_CMD_RBLOCK\r\n");
|
||||
}
|
||||
else if (IS_SBLOCK(buf[0]))
|
||||
@ -700,7 +945,10 @@ void nfc_process_block(uint8_t * buf, unsigned int len)
|
||||
if ((buf[0] & NFC_SBLOCK_DESELECT) == 0)
|
||||
{
|
||||
printf1(TAG_NFC, "NFC_CMD_SBLOCK, DESELECTED\r\n");
|
||||
nfc_write_frame(buf, 1);
|
||||
uint8_t block_offset = p14443_block_offset(buf[0]);
|
||||
if (p14443_have_cid(buf[0]))
|
||||
current_cid = buf[1];
|
||||
nfc_write_frame(buf, block_offset);
|
||||
ams_wait_for_tx(2);
|
||||
ams_write_command(AMS_CMD_SLEEP);
|
||||
nfc_state_init();
|
||||
@ -710,6 +958,7 @@ void nfc_process_block(uint8_t * buf, unsigned int len)
|
||||
else
|
||||
{
|
||||
printf1(TAG_NFC, "NFC_CMD_SBLOCK, Unknown. len[%d]\r\n", len);
|
||||
nfc_write_response(buf[0], SW_COND_USE_NOT_SATISFIED);
|
||||
}
|
||||
dump_hex1(TAG_NFC, buf, len);
|
||||
}
|
||||
@ -720,7 +969,7 @@ void nfc_process_block(uint8_t * buf, unsigned int len)
|
||||
}
|
||||
}
|
||||
|
||||
int nfc_loop()
|
||||
int nfc_loop(void)
|
||||
{
|
||||
uint8_t buf[32];
|
||||
AMS_DEVICE ams;
|
||||
@ -728,6 +977,8 @@ int nfc_loop()
|
||||
|
||||
|
||||
read_reg_block(&ams);
|
||||
uint8_t old_int0 = gl_int0;
|
||||
process_int0(ams.regs.int0);
|
||||
uint8_t state = AMS_STATE_MASK & ams.regs.rfid_status;
|
||||
|
||||
if (state != AMS_STATE_SELECTED && state != AMS_STATE_SELECTEDX)
|
||||
@ -741,7 +992,7 @@ int nfc_loop()
|
||||
// if (state != AMS_STATE_SENSE)
|
||||
// printf1(TAG_NFC," %s x%02x\r\n", ams_get_state_string(ams.regs.rfid_status), state);
|
||||
}
|
||||
if (ams.regs.int0 & AMS_INT_INIT)
|
||||
if (ams.regs.int0 & AMS_INT_INIT || old_int0 & AMS_INT_INIT)
|
||||
{
|
||||
nfc_state_init();
|
||||
}
|
||||
@ -750,7 +1001,7 @@ int nfc_loop()
|
||||
// ams_print_int1(ams.regs.int1);
|
||||
}
|
||||
|
||||
if ((ams.regs.int0 & AMS_INT_RXE))
|
||||
if (ams.regs.int0 & AMS_INT_RXE || old_int0 & AMS_INT_RXE)
|
||||
{
|
||||
if (ams.regs.buffer_status2)
|
||||
{
|
||||
@ -779,6 +1030,7 @@ int nfc_loop()
|
||||
printf1(TAG_NFC, "NFC_CMD_WUPA\r\n");
|
||||
break;
|
||||
case NFC_CMD_HLTA:
|
||||
ams_write_command(AMS_CMD_SLEEP);
|
||||
printf1(TAG_NFC, "HLTA/Halt\r\n");
|
||||
break;
|
||||
case NFC_CMD_RATS:
|
||||
|
@ -6,9 +6,9 @@
|
||||
#include "apdu.h"
|
||||
|
||||
// Return number of bytes read if any.
|
||||
int nfc_loop();
|
||||
int nfc_loop(void);
|
||||
|
||||
int nfc_init();
|
||||
int nfc_init(void);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@ -34,12 +34,14 @@ typedef struct
|
||||
#define IS_PPSS_CMD(x) (((x) & 0xf0) == NFC_CMD_PPSS)
|
||||
#define NFC_CMD_IBLOCK 0x00
|
||||
#define IS_IBLOCK(x) ( (((x) & 0xc0) == NFC_CMD_IBLOCK) && (((x) & 0x02) == 0x02) )
|
||||
#define NFC_CMD_RBLOCK 0x80
|
||||
#define NFC_CMD_RBLOCK_ACK 0x20
|
||||
#define IS_RBLOCK(x) ( (((x) & 0xc0) == NFC_CMD_RBLOCK) && (((x) & 0x02) == 0x02) )
|
||||
#define NFC_CMD_RBLOCK 0xa0
|
||||
#define NFC_CMD_RBLOCK_ACK 0x10
|
||||
#define IS_RBLOCK(x) ( (((x) & 0xe0) == NFC_CMD_RBLOCK) && (((x) & 0x02) == 0x02) )
|
||||
#define NFC_CMD_SBLOCK 0xc0
|
||||
#define IS_SBLOCK(x) ( (((x) & 0xc0) == NFC_CMD_SBLOCK) && (((x) & 0x02) == 0x02) )
|
||||
|
||||
extern uint8_t p14443_block_offset(uint8_t pcb);
|
||||
|
||||
#define NFC_SBLOCK_DESELECT 0x30
|
||||
#define NFC_SBLOCK_WTX 0x30
|
||||
|
||||
@ -59,6 +61,6 @@ typedef enum
|
||||
APP_FIDO,
|
||||
} APPLETS;
|
||||
|
||||
void WTX_timer_exec();
|
||||
void WTX_timer_exec(void);
|
||||
|
||||
#endif
|
||||
|
@ -8,7 +8,7 @@
|
||||
#define ELECTRODE_0 TSC_GROUP2_IO1
|
||||
#define ELECTRODE_1 TSC_GROUP2_IO2
|
||||
|
||||
void tsc_init()
|
||||
void tsc_init(void)
|
||||
{
|
||||
LL_GPIO_InitTypeDef GPIO_InitStruct;
|
||||
// Enable TSC clock
|
||||
@ -74,7 +74,7 @@ void tsc_set_electrode(uint32_t channel_ids)
|
||||
TSC->IOCCR = (channel_ids);
|
||||
}
|
||||
|
||||
void tsc_start_acq()
|
||||
void tsc_start_acq(void)
|
||||
{
|
||||
TSC->CR &= ~(TSC_CR_START);
|
||||
|
||||
@ -86,7 +86,7 @@ void tsc_start_acq()
|
||||
TSC->CR |= TSC_CR_START;
|
||||
}
|
||||
|
||||
void tsc_wait_on_acq()
|
||||
void tsc_wait_on_acq(void)
|
||||
{
|
||||
while ( ! (TSC->ISR & TSC_FLAG_EOA) )
|
||||
;
|
||||
@ -117,7 +117,7 @@ uint32_t tsc_read_button(uint32_t index)
|
||||
return tsc_read(1) < 45;
|
||||
}
|
||||
|
||||
int tsc_sensor_exists()
|
||||
int tsc_sensor_exists(void)
|
||||
{
|
||||
static uint8_t does = 0;
|
||||
if (does) return 1;
|
||||
|
@ -3,9 +3,9 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void tsc_init();
|
||||
void tsc_init(void);
|
||||
|
||||
int tsc_sensor_exists();
|
||||
int tsc_sensor_exists(void);
|
||||
|
||||
// Read button0 or button1
|
||||
// Returns 1 if pressed, 0 if not.
|
||||
|
25
targets/stm32l432/src/solo.h
Normal file
25
targets/stm32l432/src/solo.h
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef _SOLO_H_
|
||||
#define _SOLO_H_
|
||||
|
||||
void device_init();
|
||||
|
||||
void main_loop_delay();
|
||||
void usbhid_init();
|
||||
void usbhid_close();
|
||||
int usbhid_recv(uint8_t * msg);
|
||||
|
||||
void heartbeat();
|
||||
|
||||
// Called each main loop. Doesn't need to do anything.
|
||||
void device_manage();
|
||||
|
||||
void device_init_button();
|
||||
|
||||
// For Solo hacker
|
||||
void boot_solo_bootloader();
|
||||
void boot_st_bootloader();
|
||||
|
||||
|
||||
void delay(uint32_t ms);
|
||||
|
||||
#endif
|
@ -1,5 +1,4 @@
|
||||
ecdsa
|
||||
fido2
|
||||
intelhex
|
||||
pyserial
|
||||
solo-python
|
||||
|
@ -1,58 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2019 SoloKeys Developers
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||
# http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
# http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
# copied, modified, or distributed except according to those terms.
|
||||
#
|
||||
|
||||
# Script for testing correctness of CTAP2/CTAP1 security token
|
||||
|
||||
import sys
|
||||
|
||||
from solo.fido2 import force_udp_backend
|
||||
from tests import Tester, FIDO2Tests, U2FTests, HIDTests, SoloTests
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: %s [sim] <[u2f]|[fido2]|[rk]|[hid]|[ping]>")
|
||||
sys.exit(0)
|
||||
|
||||
t = Tester()
|
||||
t.set_user_count(3)
|
||||
|
||||
if "sim" in sys.argv:
|
||||
print("Using UDP backend.")
|
||||
force_udp_backend()
|
||||
t.set_sim(True)
|
||||
t.set_user_count(10)
|
||||
|
||||
t.find_device()
|
||||
|
||||
if "solo" in sys.argv:
|
||||
SoloTests(t).run()
|
||||
|
||||
if "u2f" in sys.argv:
|
||||
U2FTests(t).run()
|
||||
|
||||
if "fido2" in sys.argv:
|
||||
# t.test_fido2()
|
||||
FIDO2Tests(t).run()
|
||||
|
||||
# hid tests are a bit invasive and should be done last
|
||||
if "hid" in sys.argv:
|
||||
HIDTests(t).run()
|
||||
|
||||
if "bootloader" in sys.argv:
|
||||
if t.is_sim:
|
||||
raise RuntimeError("Cannot test bootloader in simulation yet.")
|
||||
# print("Put device in bootloader mode and then hit enter")
|
||||
# input()
|
||||
# t.test_bootloader()
|
||||
|
||||
# t.test_responses()
|
||||
# t.test_fido2_brute_force()
|
@ -1,11 +0,0 @@
|
||||
from . import fido2
|
||||
from . import hid
|
||||
from . import solo
|
||||
from . import u2f
|
||||
from . import tester
|
||||
|
||||
FIDO2Tests = fido2.FIDO2Tests
|
||||
HIDTests = hid.HIDTests
|
||||
U2FTests = u2f.U2FTests
|
||||
SoloTests = solo.SoloTests
|
||||
Tester = tester.Tester
|
File diff suppressed because it is too large
Load Diff
@ -1,252 +0,0 @@
|
||||
import sys, os, time
|
||||
from binascii import hexlify
|
||||
|
||||
from fido2.hid import CTAPHID
|
||||
from fido2.ctap import CtapError
|
||||
|
||||
from .tester import Tester, Test
|
||||
|
||||
|
||||
class HIDTests(Tester):
|
||||
def __init__(self, tester=None):
|
||||
super().__init__(tester)
|
||||
self.check_timeouts = False
|
||||
|
||||
def set_check_timeouts(self, en):
|
||||
self.check_timeouts = en
|
||||
|
||||
def run(self,):
|
||||
self.test_long_ping()
|
||||
self.test_hid(self.check_timeouts)
|
||||
|
||||
def test_long_ping(self):
|
||||
amt = 1000
|
||||
pingdata = os.urandom(amt)
|
||||
with Test("Send %d byte ping" % amt):
|
||||
try:
|
||||
t1 = time.time() * 1000
|
||||
r = self.send_data(CTAPHID.PING, pingdata)
|
||||
t2 = time.time() * 1000
|
||||
delt = t2 - t1
|
||||
# if (delt < 140 ):
|
||||
# raise RuntimeError('Fob is too fast (%d ms)' % delt)
|
||||
if delt > 555 * (amt / 1000):
|
||||
raise RuntimeError("Fob is too slow (%d ms)" % delt)
|
||||
if r != pingdata:
|
||||
raise ValueError("Ping data not echo'd")
|
||||
except CtapError:
|
||||
raise RuntimeError("ping failed")
|
||||
|
||||
sys.stdout.flush()
|
||||
|
||||
def test_hid(self, check_timeouts=False):
|
||||
if check_timeouts:
|
||||
with Test("idle"):
|
||||
try:
|
||||
cmd, resp = self.recv_raw()
|
||||
except socket.timeout:
|
||||
pass
|
||||
|
||||
with Test("init"):
|
||||
r = self.send_data(CTAPHID.INIT, "\x11\x11\x11\x11\x11\x11\x11\x11")
|
||||
|
||||
with Test("100 byte ping"):
|
||||
pingdata = os.urandom(100)
|
||||
try:
|
||||
r = self.send_data(CTAPHID.PING, pingdata)
|
||||
if r != pingdata:
|
||||
raise ValueError("Ping data not echo'd")
|
||||
except CtapError as e:
|
||||
print("100 byte Ping failed:", e)
|
||||
raise RuntimeError("ping failed")
|
||||
|
||||
self.test_long_ping()
|
||||
|
||||
with Test("Wink"):
|
||||
r = self.send_data(CTAPHID.WINK, "")
|
||||
|
||||
with Test("CBOR msg with no data"):
|
||||
try:
|
||||
r = self.send_data(CTAPHID.CBOR, "")
|
||||
if len(r) > 1 or r[0] == 0:
|
||||
raise RuntimeError("Cbor is supposed to have payload")
|
||||
except CtapError as e:
|
||||
assert e.code == CtapError.ERR.INVALID_LENGTH
|
||||
|
||||
with Test("No data in U2F msg"):
|
||||
try:
|
||||
r = self.send_data(CTAPHID.MSG, "")
|
||||
print(hexlify(r))
|
||||
if len(r) > 2:
|
||||
raise RuntimeError("MSG is supposed to have payload")
|
||||
except CtapError as e:
|
||||
assert e.code == CtapError.ERR.INVALID_LENGTH
|
||||
|
||||
with Test("Use init command to resync"):
|
||||
r = self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
|
||||
with Test("Invalid HID command"):
|
||||
try:
|
||||
r = self.send_data(0x66, "")
|
||||
raise RuntimeError("Invalid command did not return error")
|
||||
except CtapError as e:
|
||||
assert e.code == CtapError.ERR.INVALID_COMMAND
|
||||
|
||||
with Test("Sending packet with too large of a length."):
|
||||
self.send_raw("\x81\x1d\xba\x00")
|
||||
cmd, resp = self.recv_raw()
|
||||
Tester.check_error(resp, CtapError.ERR.INVALID_LENGTH)
|
||||
|
||||
r = self.send_data(CTAPHID.PING, "\x44" * 200)
|
||||
with Test("Sending packets that skip a sequence number."):
|
||||
self.send_raw("\x81\x04\x90")
|
||||
self.send_raw("\x00")
|
||||
self.send_raw("\x01")
|
||||
# skip 2
|
||||
self.send_raw("\x03")
|
||||
cmd, resp = self.recv_raw()
|
||||
Tester.check_error(resp, CtapError.ERR.INVALID_SEQ)
|
||||
|
||||
with Test("Resync and send ping"):
|
||||
try:
|
||||
r = self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
pingdata = os.urandom(100)
|
||||
r = self.send_data(CTAPHID.PING, pingdata)
|
||||
if r != pingdata:
|
||||
raise ValueError("Ping data not echo'd")
|
||||
except CtapError as e:
|
||||
raise RuntimeError("resync fail: ", e)
|
||||
|
||||
with Test("Send ping and abort it"):
|
||||
self.send_raw("\x81\x04\x00")
|
||||
self.send_raw("\x00")
|
||||
self.send_raw("\x01")
|
||||
try:
|
||||
r = self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
except CtapError as e:
|
||||
raise RuntimeError("resync fail: ", e)
|
||||
|
||||
with Test("Send ping and abort it with different cid, expect timeout"):
|
||||
oldcid = self.cid()
|
||||
newcid = "\x11\x22\x33\x44"
|
||||
self.send_raw("\x81\x10\x00")
|
||||
self.send_raw("\x00")
|
||||
self.send_raw("\x01")
|
||||
self.set_cid(newcid)
|
||||
self.send_raw(
|
||||
"\x86\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88"
|
||||
) # init from different cid
|
||||
print("wait for init response")
|
||||
cmd, r = self.recv_raw() # init response
|
||||
assert cmd == 0x86
|
||||
self.set_cid(oldcid)
|
||||
if check_timeouts:
|
||||
# print('wait for timeout')
|
||||
cmd, r = self.recv_raw() # timeout response
|
||||
assert cmd == 0xBF
|
||||
|
||||
with Test("Test timeout"):
|
||||
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
t1 = time.time() * 1000
|
||||
self.send_raw("\x81\x04\x00")
|
||||
self.send_raw("\x00")
|
||||
self.send_raw("\x01")
|
||||
cmd, r = self.recv_raw() # timeout response
|
||||
t2 = time.time() * 1000
|
||||
delt = t2 - t1
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.TIMEOUT
|
||||
assert delt < 1000 and delt > 400
|
||||
|
||||
with Test("Test not cont"):
|
||||
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
self.send_raw("\x81\x04\x00")
|
||||
self.send_raw("\x00")
|
||||
self.send_raw("\x01")
|
||||
self.send_raw("\x81\x10\x00") # init packet
|
||||
cmd, r = self.recv_raw() # timeout response
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.INVALID_SEQ
|
||||
|
||||
if check_timeouts:
|
||||
with Test("Check random cont ignored"):
|
||||
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
self.send_raw("\x01\x10\x00")
|
||||
try:
|
||||
cmd, r = self.recv_raw() # timeout response
|
||||
except socket.timeout:
|
||||
pass
|
||||
|
||||
with Test("Check busy"):
|
||||
t1 = time.time() * 1000
|
||||
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
oldcid = self.cid()
|
||||
newcid = "\x11\x22\x33\x44"
|
||||
self.send_raw("\x81\x04\x00")
|
||||
self.set_cid(newcid)
|
||||
self.send_raw("\x81\x04\x00")
|
||||
cmd, r = self.recv_raw() # busy response
|
||||
t2 = time.time() * 1000
|
||||
assert t2 - t1 < 100
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.CHANNEL_BUSY
|
||||
|
||||
self.set_cid(oldcid)
|
||||
cmd, r = self.recv_raw() # timeout response
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.TIMEOUT
|
||||
|
||||
with Test("Check busy interleaved"):
|
||||
cid1 = "\x11\x22\x33\x44"
|
||||
cid2 = "\x01\x22\x33\x44"
|
||||
self.set_cid(cid2)
|
||||
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
self.set_cid(cid1)
|
||||
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
self.send_raw("\x81\x00\x63") # echo 99 bytes first channel
|
||||
|
||||
self.set_cid(cid2) # send ping on 2nd channel
|
||||
self.send_raw("\x81\x00\x63")
|
||||
Tester.delay(0.1)
|
||||
self.send_raw("\x00")
|
||||
|
||||
cmd, r = self.recv_raw() # busy response
|
||||
|
||||
self.set_cid(cid1) # finish 1st channel ping
|
||||
self.send_raw("\x00")
|
||||
|
||||
self.set_cid(cid2)
|
||||
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.CHANNEL_BUSY
|
||||
|
||||
self.set_cid(cid1)
|
||||
cmd, r = self.recv_raw() # ping response
|
||||
assert cmd == 0x81
|
||||
assert len(r) == 0x63
|
||||
|
||||
if check_timeouts:
|
||||
with Test("Test idle, wait for timeout"):
|
||||
sys.stdout.flush()
|
||||
try:
|
||||
cmd, resp = self.recv_raw()
|
||||
except socket.timeout:
|
||||
pass
|
||||
|
||||
with Test("Test cid 0 is invalid"):
|
||||
self.set_cid("\x00\x00\x00\x00")
|
||||
self.send_raw(
|
||||
"\x86\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88", cid="\x00\x00\x00\x00"
|
||||
)
|
||||
cmd, r = self.recv_raw() # timeout
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.INVALID_CHANNEL
|
||||
|
||||
with Test("Test invalid broadcast cid use"):
|
||||
self.set_cid("\xff\xff\xff\xff")
|
||||
self.send_raw(
|
||||
"\x81\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88", cid="\xff\xff\xff\xff"
|
||||
)
|
||||
cmd, r = self.recv_raw() # timeout
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.INVALID_CHANNEL
|
@ -1,83 +0,0 @@
|
||||
from solo.client import SoloClient
|
||||
from solo.commands import SoloExtension
|
||||
|
||||
from fido2.ctap1 import ApduError
|
||||
from fido2.utils import sha256
|
||||
|
||||
from .util import shannon_entropy
|
||||
from .tester import Tester, Test
|
||||
|
||||
|
||||
class SoloTests(Tester):
|
||||
def __init__(self, tester=None):
|
||||
super().__init__(tester)
|
||||
|
||||
def run(self,):
|
||||
self.test_solo()
|
||||
|
||||
def test_solo(self,):
|
||||
"""
|
||||
Solo specific tests
|
||||
"""
|
||||
# RNG command
|
||||
sc = SoloClient()
|
||||
sc.find_device(self.dev)
|
||||
sc.use_u2f()
|
||||
memmap = (0x08005000, 0x08005000 + 198 * 1024 - 8)
|
||||
|
||||
total = 1024 * 16
|
||||
with Test("Gathering %d random bytes..." % total):
|
||||
entropy = b""
|
||||
while len(entropy) < total:
|
||||
entropy += sc.get_rng()
|
||||
|
||||
with Test("Test entropy is close to perfect"):
|
||||
s = shannon_entropy(entropy)
|
||||
assert s > 7.98
|
||||
print("Entropy is %.5f bits per byte." % s)
|
||||
|
||||
with Test("Test Solo version command"):
|
||||
assert len(sc.solo_version()) == 3
|
||||
|
||||
with Test("Test bootloader is not active"):
|
||||
try:
|
||||
sc.write_flash(memmap[0], b"1234")
|
||||
except ApduError:
|
||||
pass
|
||||
|
||||
sc.exchange = sc.exchange_fido2
|
||||
|
||||
req = SoloClient.format_request(SoloExtension.version, 0, b"A" * 16)
|
||||
a = sc.ctap2.get_assertion(
|
||||
sc.host, b"B" * 32, [{"id": req, "type": "public-key"}]
|
||||
)
|
||||
|
||||
with Test("Test custom command returned valid assertion"):
|
||||
assert a.auth_data.rp_id_hash == sha256(sc.host.encode("utf8"))
|
||||
assert a.credential["id"] == req
|
||||
assert (a.auth_data.flags & 0x5) == 0x5
|
||||
|
||||
with Test("Test Solo version and random commands with fido2 layer"):
|
||||
assert len(sc.solo_version()) == 3
|
||||
sc.get_rng()
|
||||
|
||||
def test_bootloader(self,):
|
||||
sc = SoloClient()
|
||||
sc.find_device(self.dev)
|
||||
sc.use_u2f()
|
||||
|
||||
memmap = (0x08005000, 0x08005000 + 198 * 1024 - 8)
|
||||
data = b"A" * 64
|
||||
|
||||
with Test("Test version command"):
|
||||
assert len(sc.bootloader_version()) == 3
|
||||
|
||||
with Test("Test write command"):
|
||||
sc.write_flash(memmap[0], data)
|
||||
|
||||
for addr in (memmap[0] - 8, memmap[0] - 4, memmap[1], memmap[1] - 8):
|
||||
with Test("Test out of bounds write command at 0x%04x" % addr):
|
||||
try:
|
||||
sc.write_flash(addr, data)
|
||||
except CtapError as e:
|
||||
assert e.code == CtapError.ERR.NOT_ALLOWED
|
@ -1,204 +0,0 @@
|
||||
import time, struct
|
||||
|
||||
from fido2.hid import CtapHidDevice
|
||||
from fido2.client import Fido2Client
|
||||
from fido2.ctap1 import CTAP1
|
||||
from fido2.utils import Timeout
|
||||
|
||||
from fido2.ctap import CtapError
|
||||
|
||||
|
||||
def ForceU2F(client, device):
|
||||
client.ctap = CTAP1(device)
|
||||
client.pin_protocol = None
|
||||
client._do_make_credential = client._ctap1_make_credential
|
||||
client._do_get_assertion = client._ctap1_get_assertion
|
||||
|
||||
|
||||
class Packet(object):
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
|
||||
def ToWireFormat(self,):
|
||||
return self.data
|
||||
|
||||
@staticmethod
|
||||
def FromWireFormat(pkt_size, data):
|
||||
return Packet(data)
|
||||
|
||||
|
||||
class Test:
|
||||
def __init__(self, msg, catch=None):
|
||||
self.msg = msg
|
||||
self.catch = catch
|
||||
|
||||
def __enter__(self,):
|
||||
print(self.msg)
|
||||
|
||||
def __exit__(self, a, b, c):
|
||||
if self.catch is None:
|
||||
print("Pass")
|
||||
elif isinstance(b, self.catch):
|
||||
print("Pass")
|
||||
return b
|
||||
else:
|
||||
raise RuntimeError(f"Expected exception {self.catch} did not occur.")
|
||||
|
||||
|
||||
class Tester:
|
||||
def __init__(self, tester=None):
|
||||
self.origin = "https://examplo.org"
|
||||
self.host = "examplo.org"
|
||||
self.user_count = 10
|
||||
self.is_sim = False
|
||||
if tester:
|
||||
self.initFromTester(tester)
|
||||
|
||||
def initFromTester(self, tester):
|
||||
self.user_count = tester.user_count
|
||||
self.is_sim = tester.is_sim
|
||||
self.dev = tester.dev
|
||||
self.ctap = tester.ctap
|
||||
self.ctap1 = tester.ctap1
|
||||
self.client = tester.client
|
||||
|
||||
def find_device(self,):
|
||||
print(list(CtapHidDevice.list_devices()))
|
||||
dev = next(CtapHidDevice.list_devices(), None)
|
||||
if not dev:
|
||||
raise RuntimeError("No FIDO device found")
|
||||
self.dev = dev
|
||||
self.client = Fido2Client(dev, self.origin)
|
||||
self.ctap = self.client.ctap2
|
||||
self.ctap1 = CTAP1(dev)
|
||||
|
||||
# consume timeout error
|
||||
# cmd,resp = self.recv_raw()
|
||||
|
||||
def set_user_count(self, count):
|
||||
self.user_count = count
|
||||
|
||||
def set_sim(self, b):
|
||||
self.is_sim = b
|
||||
|
||||
def reboot(self,):
|
||||
if self.is_sim:
|
||||
print("Sending restart command...")
|
||||
self.send_magic_reboot()
|
||||
Tester.delay(0.25)
|
||||
else:
|
||||
print("Please reboot authentictor and hit enter")
|
||||
input()
|
||||
self.find_device()
|
||||
|
||||
def send_data(self, cmd, data):
|
||||
if not isinstance(data, bytes):
|
||||
data = struct.pack("%dB" % len(data), *[ord(x) for x in data])
|
||||
with Timeout(1.0) as event:
|
||||
return self.dev.call(cmd, data, event)
|
||||
|
||||
def send_raw(self, data, cid=None):
|
||||
if cid is None:
|
||||
cid = self.dev._dev.cid
|
||||
elif not isinstance(cid, bytes):
|
||||
cid = struct.pack("%dB" % len(cid), *[ord(x) for x in cid])
|
||||
if not isinstance(data, bytes):
|
||||
data = struct.pack("%dB" % len(data), *[ord(x) for x in data])
|
||||
data = cid + data
|
||||
l = len(data)
|
||||
if l != 64:
|
||||
pad = "\x00" * (64 - l)
|
||||
pad = struct.pack("%dB" % len(pad), *[ord(x) for x in pad])
|
||||
data = data + pad
|
||||
data = list(data)
|
||||
assert len(data) == 64
|
||||
self.dev._dev.InternalSendPacket(Packet(data))
|
||||
|
||||
def send_magic_reboot(self,):
|
||||
"""
|
||||
For use in simulation and testing. Random bytes that authentictor should detect
|
||||
and then restart itself.
|
||||
"""
|
||||
magic_cmd = (
|
||||
b"\xac\x10\x52\xca\x95\xe5\x69\xde\x69\xe0\x2e\xbf"
|
||||
+ b"\xf3\x33\x48\x5f\x13\xf9\xb2\xda\x34\xc5\xa8\xa3"
|
||||
+ b"\x40\x52\x66\x97\xa9\xab\x2e\x0b\x39\x4d\x8d\x04"
|
||||
+ b"\x97\x3c\x13\x40\x05\xbe\x1a\x01\x40\xbf\xf6\x04"
|
||||
+ b"\x5b\xb2\x6e\xb7\x7a\x73\xea\xa4\x78\x13\xf6\xb4"
|
||||
+ b"\x9a\x72\x50\xdc"
|
||||
)
|
||||
self.dev._dev.InternalSendPacket(Packet(magic_cmd))
|
||||
|
||||
def cid(self,):
|
||||
return self.dev._dev.cid
|
||||
|
||||
def set_cid(self, cid):
|
||||
if not isinstance(cid, (bytes, bytearray)):
|
||||
cid = struct.pack("%dB" % len(cid), *[ord(x) for x in cid])
|
||||
self.dev._dev.cid = cid
|
||||
|
||||
def recv_raw(self,):
|
||||
with Timeout(1.0):
|
||||
cmd, payload = self.dev._dev.InternalRecv()
|
||||
return cmd, payload
|
||||
|
||||
def check_error(data, err=None):
|
||||
assert len(data) == 1
|
||||
if err is None:
|
||||
if data[0] != 0:
|
||||
raise CtapError(data[0])
|
||||
elif data[0] != err:
|
||||
raise ValueError("Unexpected error: %02x" % data[0])
|
||||
|
||||
def testFunc(self, func, test, *args, **kwargs):
|
||||
with Test(test):
|
||||
res = None
|
||||
expectedError = kwargs.get("expectedError", None)
|
||||
otherArgs = kwargs.get("other", {})
|
||||
try:
|
||||
res = func(*args, **otherArgs)
|
||||
if expectedError != CtapError.ERR.SUCCESS:
|
||||
raise RuntimeError("Expected error to occur for test: %s" % test)
|
||||
except CtapError as e:
|
||||
if expectedError is not None:
|
||||
cond = e.code != expectedError
|
||||
if isinstance(expectedError, list):
|
||||
cond = e.code not in expectedError
|
||||
else:
|
||||
expectedError = [expectedError]
|
||||
if cond:
|
||||
raise RuntimeError(
|
||||
f"Got error code {hex(e.code)}, expected {[hex(x) for x in expectedError]}"
|
||||
)
|
||||
else:
|
||||
print(e)
|
||||
return res
|
||||
|
||||
def testReset(self,):
|
||||
print("Resetting Authenticator...")
|
||||
try:
|
||||
self.ctap.reset()
|
||||
except CtapError:
|
||||
# Some authenticators need a power cycle
|
||||
print("You must power cycle authentictor. Hit enter when done.")
|
||||
input()
|
||||
time.sleep(0.2)
|
||||
self.find_device()
|
||||
self.ctap.reset()
|
||||
|
||||
def testMC(self, test, *args, **kwargs):
|
||||
return self.testFunc(self.ctap.make_credential, test, *args, **kwargs)
|
||||
|
||||
def testGA(self, test, *args, **kwargs):
|
||||
return self.testFunc(self.ctap.get_assertion, test, *args, **kwargs)
|
||||
|
||||
def testCP(self, test, *args, **kwargs):
|
||||
return self.testFunc(self.ctap.client_pin, test, *args, **kwargs)
|
||||
|
||||
def testPP(self, test, *args, **kwargs):
|
||||
return self.testFunc(
|
||||
self.client.pin_protocol.get_pin_token, test, *args, **kwargs
|
||||
)
|
||||
|
||||
def delay(secs):
|
||||
time.sleep(secs)
|
@ -1,121 +0,0 @@
|
||||
from fido2.ctap1 import CTAP1, ApduError, APDU
|
||||
from fido2.utils import sha256
|
||||
from fido2.client import _call_polling
|
||||
|
||||
from .tester import Tester, Test
|
||||
|
||||
|
||||
class U2FTests(Tester):
|
||||
def __init__(self, tester=None):
|
||||
super().__init__(tester)
|
||||
|
||||
def run(self,):
|
||||
self.test_u2f()
|
||||
|
||||
def register(self, chal, appid):
|
||||
reg_data = _call_polling(0.25, None, None, self.ctap1.register, chal, appid)
|
||||
return reg_data
|
||||
|
||||
def authenticate(self, chal, appid, key_handle, check_only=False):
|
||||
auth_data = _call_polling(
|
||||
0.25,
|
||||
None,
|
||||
None,
|
||||
self.ctap1.authenticate,
|
||||
chal,
|
||||
appid,
|
||||
key_handle,
|
||||
check_only=check_only,
|
||||
)
|
||||
return auth_data
|
||||
|
||||
def test_u2f(self,):
|
||||
chal = sha256(b"AAA")
|
||||
appid = sha256(b"BBB")
|
||||
lastc = 0
|
||||
|
||||
regs = []
|
||||
|
||||
with Test("Check version"):
|
||||
assert self.ctap1.get_version() == "U2F_V2"
|
||||
|
||||
with Test("Check bad INS"):
|
||||
try:
|
||||
self.ctap1.send_apdu(0, 0, 0, 0, b"")
|
||||
except ApduError as e:
|
||||
assert e.code == 0x6D00
|
||||
|
||||
with Test("Check bad CLA"):
|
||||
try:
|
||||
self.ctap1.send_apdu(1, CTAP1.INS.VERSION, 0, 0, b"abc")
|
||||
except ApduError as e:
|
||||
assert e.code == 0x6E00
|
||||
|
||||
for i in range(0, self.user_count):
|
||||
with Test(
|
||||
"U2F reg + auth %d/%d (count: %02x)" % (i + 1, self.user_count, lastc)
|
||||
):
|
||||
reg = self.register(chal, appid)
|
||||
reg.verify(appid, chal)
|
||||
auth = self.authenticate(chal, appid, reg.key_handle)
|
||||
auth.verify(appid, chal, reg.public_key)
|
||||
|
||||
regs.append(reg)
|
||||
# check endianness
|
||||
if lastc:
|
||||
assert (auth.counter - lastc) < 10
|
||||
lastc = auth.counter
|
||||
if lastc > 0x80000000:
|
||||
print("WARNING: counter is unusually high: %04x" % lastc)
|
||||
assert 0
|
||||
|
||||
for i in range(0, self.user_count):
|
||||
with Test(
|
||||
"Checking previous registration %d/%d" % (i + 1, self.user_count)
|
||||
):
|
||||
auth = self.authenticate(chal, appid, regs[i].key_handle)
|
||||
auth.verify(appid, chal, regs[i].public_key)
|
||||
|
||||
print("Check that all previous credentials are registered...")
|
||||
for i in range(0, self.user_count):
|
||||
with Test("Check that previous credential %d is registered" % i):
|
||||
try:
|
||||
auth = self.ctap1.authenticate(
|
||||
chal, appid, regs[i].key_handle, check_only=True
|
||||
)
|
||||
except ApduError as e:
|
||||
# Indicates that key handle is registered
|
||||
assert e.code == APDU.USE_NOT_SATISFIED
|
||||
|
||||
with Test("Check an incorrect key handle is not registered"):
|
||||
kh = bytearray(regs[0].key_handle)
|
||||
kh[0] = kh[0] ^ (0x40)
|
||||
try:
|
||||
self.ctap1.authenticate(chal, appid, kh, check_only=True)
|
||||
assert 0
|
||||
except ApduError as e:
|
||||
assert e.code == APDU.WRONG_DATA
|
||||
|
||||
with Test("Try to sign with incorrect key handle"):
|
||||
try:
|
||||
self.ctap1.authenticate(chal, appid, kh)
|
||||
assert 0
|
||||
except ApduError as e:
|
||||
assert e.code == APDU.WRONG_DATA
|
||||
|
||||
with Test("Try to sign using an incorrect keyhandle length"):
|
||||
try:
|
||||
kh = regs[0].key_handle
|
||||
self.ctap1.authenticate(chal, appid, kh[: len(kh) // 2])
|
||||
assert 0
|
||||
except ApduError as e:
|
||||
assert e.code == APDU.WRONG_DATA
|
||||
|
||||
with Test("Try to sign using an incorrect appid"):
|
||||
badid = bytearray(appid)
|
||||
badid[0] = badid[0] ^ (0x40)
|
||||
try:
|
||||
auth = self.ctap1.authenticate(chal, badid, regs[0].key_handle)
|
||||
assert 0
|
||||
except ApduError as e:
|
||||
assert e.code == APDU.WRONG_DATA
|
@ -1,12 +0,0 @@
|
||||
import math
|
||||
|
||||
|
||||
def shannon_entropy(data):
|
||||
s = 0.0
|
||||
total = len(data)
|
||||
for x in range(0, 256):
|
||||
freq = data.count(x)
|
||||
p = freq / total
|
||||
if p > 0:
|
||||
s -= p * math.log2(p)
|
||||
return s
|
Reference in New Issue
Block a user