Compare commits
115 Commits
fault_tole
...
documentat
Author | SHA1 | Date | |
---|---|---|---|
a7cda74741 | |||
a8baba422f | |||
abd2eca3ca | |||
038c594e37 | |||
f09900a391 | |||
bd822dd95f | |||
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 |
178
.all-contributorsrc
Normal file
178
.all-contributorsrc
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
{
|
||||||
|
"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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"contributorsPerLine": 7,
|
||||||
|
"projectName": "solo",
|
||||||
|
"projectOwner": "solokeys",
|
||||||
|
"repoType": "github",
|
||||||
|
"repoHost": "https://github.com"
|
||||||
|
}
|
47
README.md
47
README.md
@ -1,4 +1,5 @@
|
|||||||
[](https://github.com/solokeys/solo/blob/master/LICENSE)
|
[](https://github.com/solokeys/solo/blob/master/LICENSE)
|
||||||
|
[](#contributors)
|
||||||
[](https://travis-ci.com/solokeys/solo)
|
[](https://travis-ci.com/solokeys/solo)
|
||||||
[](https://discourse.solokeys.com)
|
[](https://discourse.solokeys.com)
|
||||||
[](https://keybase.io/team/solokeys.public)
|
[](https://keybase.io/team/solokeys.public)
|
||||||
@ -94,10 +95,7 @@ Run the Solo application:
|
|||||||
./main
|
./main
|
||||||
```
|
```
|
||||||
|
|
||||||
In another shell, you can run client software, for example our tests:
|
In another shell, you can run our [test suite](https://github.com/solokeys/fido2-tests).
|
||||||
```bash
|
|
||||||
python tools/ctap_test.py sim fido2
|
|
||||||
```
|
|
||||||
|
|
||||||
You can find more details in our [documentation](https://docs.solokeys.io/solo/), including how to build on the the NUCLEO-L432KC development board.
|
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 +105,46 @@ You can find more details in our [documentation](https://docs.solokeys.io/solo/)
|
|||||||
Check out our [official 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.
|
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.
|
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>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||||
|
|
||||||
|
|
||||||
# License
|
# License
|
||||||
|
|
||||||
@ -123,6 +153,8 @@ Solo is fully open source.
|
|||||||
All software, unless otherwise noted, is dual licensed under Apache 2.0 and MIT.
|
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.
|
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.
|
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 2.1 license or CC-BY-SA 4.0 license.
|
||||||
|
|
||||||
@ -135,3 +167,4 @@ You may use Solo documentation under the terms of the CC-BY-SA 4.0 license
|
|||||||
# Where To Buy Solo
|
# Where To Buy Solo
|
||||||
|
|
||||||
You can buy Solo, Solo Tap, and Solo for Hackers at [solokeys.com](https://solokeys.com).
|
You can buy Solo, Solo Tap, and Solo for Hackers at [solokeys.com](https://solokeys.com).
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
2.3.0
|
2.4.2
|
||||||
|
51
docs/solo/bootloader-mode.md
Normal file
51
docs/solo/bootloader-mode.md
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# Booting into bootloader mode
|
||||||
|
|
||||||
|
You can put Solo into bootloader mode by holding down the button, and 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.
|
||||||
|
|
||||||
|
If Solo is running a hacker build, it can be put into bootloader mode on command. This makes it easier for development.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
solo program aux enter-bootloader
|
||||||
|
```
|
||||||
|
|
||||||
|
# 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 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 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.
|
@ -14,12 +14,6 @@ but be warned they might be out of date. Typically it will be called `gcc-arm-n
|
|||||||
|
|
||||||
Install `solo-python` usually with `pip3 install solo-python`. The `solo` python application may also be used for [programming](#programming).
|
Install `solo-python` usually with `pip3 install solo-python`. The `solo` python application may also be used for [programming](#programming).
|
||||||
|
|
||||||
To program your build, you'll need one of the following programs.
|
|
||||||
|
|
||||||
- [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
|
## Obtain source code and solo tool
|
||||||
|
|
||||||
Source code can be downloaded from:
|
Source code can be downloaded from:
|
||||||
@ -32,7 +26,7 @@ Source code can be downloaded from:
|
|||||||
- from python programs [repository](https://pypi.org/project/solo-python/) `pip install solo-python`
|
- from python programs [repository](https://pypi.org/project/solo-python/) `pip install solo-python`
|
||||||
- from installing prerequisites `pip3 install -r tools/requirements.txt`
|
- from installing prerequisites `pip3 install -r tools/requirements.txt`
|
||||||
- github repository: [repository](https://github.com/solokeys/solo-python)
|
- github repository: [repository](https://github.com/solokeys/solo-python)
|
||||||
- installation python enviroment witn command `make venv` from root directory of source code
|
- installation python enviroment with command `make venv` from root directory of source code
|
||||||
|
|
||||||
## Compilation
|
## Compilation
|
||||||
|
|
||||||
@ -54,7 +48,7 @@ enabled, like being able to jump to the bootloader on command. It then merges b
|
|||||||
and solo builds into the same binary. I.e. it combines `bootloader.hex` and `solo.hex`
|
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`.
|
as this can be risky if done often. Just use `solo.hex`.
|
||||||
|
|
||||||
### Building with debug messages
|
### Building with debug messages
|
||||||
@ -86,6 +80,8 @@ solo monitor <serial-port>
|
|||||||
|
|
||||||
### Building a Solo release
|
### Building a Solo release
|
||||||
|
|
||||||
|
To build Solo
|
||||||
|
|
||||||
If you want to build a release of Solo, we recommend trying a Hacker build first
|
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
|
just to make sure that it's working. Otherwise it may not be as easy or possible to
|
||||||
fix any mistakes.
|
fix any mistakes.
|
||||||
@ -96,105 +92,13 @@ If you're ready to program a full release, run this recipe to build.
|
|||||||
make build-release-locked
|
make build-release-locked
|
||||||
```
|
```
|
||||||
|
|
||||||
Programming `all.hex` will cause the device to permanently lock itself.
|
This outputs bootloader.hex, solo.hex, and the combined all.hex.
|
||||||
|
|
||||||
## Programming
|
Programming `all.hex` will cause the device to permanently lock itself. This means debuggers cannot be used and signature checking
|
||||||
|
will be enforced on all future updates.
|
||||||
|
|
||||||
It's recommended to test a debug/hacker build first to make sure Solo is working as expected.
|
Note if you program a secured `solo.hex` file onto a Solo Hacker, it will lock the flash, but the bootloader
|
||||||
Then you can switch to a locked down build, which cannot be reprogrammed as easily (or not at all!).
|
will still accept unsigned firmware updates. So you can switch it back to being a hacker, but you will
|
||||||
|
not be able to replace the unlocked bootloader anymore, since the permanently locked flash also disables the DFU.
|
||||||
|
[Read more on Solo's boot stages](/solo/bootloader-mode).
|
||||||
|
|
||||||
We recommend using our `solo` tool to manage programming. It is cross platform. First you must
|
|
||||||
install the prerequisites:
|
|
||||||
|
|
||||||
```
|
|
||||||
pip3 install -r tools/requirements.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
```
|
|
||||||
solo program aux enter-bootloader
|
|
||||||
solo program bootloader solo.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.
|
|
||||||
|
|
||||||
```
|
|
||||||
solo program aux enter-bootloader
|
|
||||||
solo program aux enter-dfu
|
|
||||||
# powercycle key
|
|
||||||
solo program dfu all.hex
|
|
||||||
```
|
|
||||||
|
|
||||||
Make sure to program `all.hex`, as this contains both the bootloader and the Solo application.
|
|
||||||
|
|
||||||
If all goes well, you should see a slow-flashing green light.
|
|
||||||
|
|
||||||
### 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
|
|
||||||
```
|
|
||||||
|
141
docs/solo/customization.md
Normal file
141
docs/solo/customization.md
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
# 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=example.com/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"
|
||||||
|
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=example.com/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
|
||||||
|
|
||||||
|
Convert the DER format of the device attestation certificate to "C" bytes using our utility script. You may first need to
|
||||||
|
first install prerequisite python modules (pip install -r tools/requirements.txt).
|
||||||
|
|
||||||
|
```
|
||||||
|
python tools/gencert/cbytes.py device_cert.der
|
||||||
|
```
|
||||||
|
|
||||||
|
Copy the byte string portion into the [`attestation.c` source file of Solo](https://github.com/solokeys/solo/blob/master/targets/stm32l432/src/attestation.c). Overwrite the development or "default" certificate that is already there.
|
||||||
|
|
||||||
|
Now [build the Solo firmware](/solo/building), either a secure or hacker build. You will need to produce a bootloader.hex file and a solo.hex file.
|
||||||
|
|
||||||
|
Print your attestation key in a hex string format.
|
||||||
|
|
||||||
|
```
|
||||||
|
python tools/print_x_y.py device_key.pem
|
||||||
|
```
|
||||||
|
|
||||||
|
Merge the bootloader.hex, solo.hex, and attestion key into one firmware file.
|
||||||
|
|
||||||
|
```
|
||||||
|
solo mergehex --attestation-key <attestation-key-hex-string> bootloader.hex solo.hex all.hex
|
||||||
|
```
|
||||||
|
|
||||||
|
Now you have a newly create `all.hex` file with a custom attestation key. You can [program this all.hex file
|
||||||
|
with Solo in DFU mode](/solo/programming#procedure).
|
258
docs/solo/nucleo32-board.md
Normal file
258
docs/solo/nucleo32-board.md
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
# 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 OsX 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
|
113
docs/solo/programming.md
Normal file
113
docs/solo/programming.md
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
# 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
|
||||||
|
# If it's a hacker, it will automatically boot into bootloader mode.
|
||||||
|
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]().
|
||||||
|
|
||||||
|
## 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 and application (or at the very least just the bootloader).
|
||||||
|
This means using the `bundle-*.hex` file or the `all.hex` from your build. If you overwrite the Solo flash with a missing bootloader,
|
||||||
|
it will be bricked.
|
||||||
|
|
||||||
|
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 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 | all.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 is 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
|
||||||
|
```
|
@ -38,6 +38,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_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);
|
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_TRANSPORT_KEY ((uint8_t*)1)
|
||||||
#define CRYPTO_MASTER_KEY ((uint8_t*)0)
|
#define CRYPTO_MASTER_KEY ((uint8_t*)0)
|
||||||
|
|
||||||
|
30
fido2/ctap.c
30
fido2/ctap.c
@ -355,9 +355,9 @@ static int ctap_make_extensions(CTAP_extensions * ext, uint8_t * ext_encoder_buf
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate credRandom
|
// Generate credRandom
|
||||||
crypto_sha256_hmac_init(CRYPTO_TRANSPORT_KEY, 0, credRandom);
|
crypto_sha256_hmac_init(CRYPTO_TRANSPORT_KEY2, 0, credRandom);
|
||||||
crypto_sha256_update((uint8_t*)&ext->hmac_secret.credential->id, sizeof(CredentialId));
|
crypto_sha256_update((uint8_t*)&ext->hmac_secret.credential->id, sizeof(CredentialId));
|
||||||
crypto_sha256_hmac_final(CRYPTO_TRANSPORT_KEY, 0, credRandom);
|
crypto_sha256_hmac_final(CRYPTO_TRANSPORT_KEY2, 0, credRandom);
|
||||||
|
|
||||||
// Decrypt saltEnc
|
// Decrypt saltEnc
|
||||||
crypto_aes256_init(shared_secret, NULL);
|
crypto_aes256_init(shared_secret, NULL);
|
||||||
@ -432,6 +432,12 @@ static unsigned int get_credential_id_size(CTAP_credentialDescriptor * cred)
|
|||||||
return sizeof(CredentialId);
|
return sizeof(CredentialId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ctap2_user_presence_test()
|
||||||
|
{
|
||||||
|
device_set_status(CTAPHID_STATUS_UPNEEDED);
|
||||||
|
return ctap_user_presence_test(CTAP2_UP_DELAY_MS);
|
||||||
|
}
|
||||||
|
|
||||||
static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * auth_data_buf, uint32_t * len, CTAP_credInfo * credInfo)
|
static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * auth_data_buf, uint32_t * len, CTAP_credInfo * credInfo)
|
||||||
{
|
{
|
||||||
CborEncoder cose_key;
|
CborEncoder cose_key;
|
||||||
@ -459,11 +465,9 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
|
|||||||
|
|
||||||
count = auth_data_update_count(&authData->head);
|
count = auth_data_update_count(&authData->head);
|
||||||
|
|
||||||
device_set_status(CTAPHID_STATUS_UPNEEDED);
|
|
||||||
|
|
||||||
int but;
|
int but;
|
||||||
|
|
||||||
but = ctap_user_presence_test(CTAP2_UP_DELAY_MS);
|
but = ctap2_user_presence_test(CTAP2_UP_DELAY_MS);
|
||||||
|
|
||||||
if (!but)
|
if (!but)
|
||||||
{
|
{
|
||||||
@ -473,6 +477,7 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
|
|||||||
{
|
{
|
||||||
return CTAP2_ERR_KEEPALIVE_CANCEL;
|
return CTAP2_ERR_KEEPALIVE_CANCEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
device_set_status(CTAPHID_STATUS_PROCESSING);
|
device_set_status(CTAPHID_STATUS_PROCESSING);
|
||||||
|
|
||||||
authData->head.flags = (but << 0);
|
authData->head.flags = (but << 0);
|
||||||
@ -605,7 +610,6 @@ int ctap_calculate_signature(uint8_t * data, int datalen, uint8_t * clientDataHa
|
|||||||
crypto_sha256_final(hashbuf);
|
crypto_sha256_final(hashbuf);
|
||||||
|
|
||||||
crypto_ecc256_sign(hashbuf, 32, sigbuf);
|
crypto_ecc256_sign(hashbuf, 32, sigbuf);
|
||||||
|
|
||||||
return ctap_encode_der_sig(sigbuf,sigder);
|
return ctap_encode_der_sig(sigbuf,sigder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -701,11 +705,11 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
|
|||||||
}
|
}
|
||||||
if (MC.pinAuthEmpty)
|
if (MC.pinAuthEmpty)
|
||||||
{
|
{
|
||||||
if (!ctap_user_presence_test(CTAP2_UP_DELAY_MS))
|
if (!ctap2_user_presence_test(CTAP2_UP_DELAY_MS))
|
||||||
{
|
{
|
||||||
return CTAP2_ERR_OPERATION_DENIED;
|
return CTAP2_ERR_OPERATION_DENIED;
|
||||||
}
|
}
|
||||||
return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_INVALID : CTAP2_ERR_PIN_NOT_SET;
|
return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_AUTH_INVALID : CTAP2_ERR_PIN_NOT_SET;
|
||||||
}
|
}
|
||||||
if ((MC.paramsParsed & MC_requiredMask) != MC_requiredMask)
|
if ((MC.paramsParsed & MC_requiredMask) != MC_requiredMask)
|
||||||
{
|
{
|
||||||
@ -1056,7 +1060,7 @@ uint8_t ctap_end_get_assertion(CborEncoder * map, CTAP_credentialDescriptor * cr
|
|||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
sigder_sz = ctap_calculate_signature(auth_data_buf, sizeof(CTAP_authDataHeader), clientDataHash, auth_data_buf, sigbuf, sigder);
|
sigder_sz = ctap_calculate_signature(auth_data_buf, auth_data_buf_sz, clientDataHash, auth_data_buf, sigbuf, sigder);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -1137,11 +1141,11 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
|
|||||||
|
|
||||||
if (GA.pinAuthEmpty)
|
if (GA.pinAuthEmpty)
|
||||||
{
|
{
|
||||||
if (!ctap_user_presence_test(CTAP2_UP_DELAY_MS))
|
if (!ctap2_user_presence_test(CTAP2_UP_DELAY_MS))
|
||||||
{
|
{
|
||||||
return CTAP2_ERR_OPERATION_DENIED;
|
return CTAP2_ERR_OPERATION_DENIED;
|
||||||
}
|
}
|
||||||
return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_INVALID : CTAP2_ERR_PIN_NOT_SET;
|
return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_AUTH_INVALID : CTAP2_ERR_PIN_NOT_SET;
|
||||||
}
|
}
|
||||||
if (GA.pinAuthPresent)
|
if (GA.pinAuthPresent)
|
||||||
{
|
{
|
||||||
@ -1604,7 +1608,6 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
|
|||||||
switch(cmd)
|
switch(cmd)
|
||||||
{
|
{
|
||||||
case CTAP_MAKE_CREDENTIAL:
|
case CTAP_MAKE_CREDENTIAL:
|
||||||
device_set_status(CTAPHID_STATUS_PROCESSING);
|
|
||||||
printf1(TAG_CTAP,"CTAP_MAKE_CREDENTIAL\n");
|
printf1(TAG_CTAP,"CTAP_MAKE_CREDENTIAL\n");
|
||||||
timestamp();
|
timestamp();
|
||||||
status = ctap_make_credential(&encoder, pkt_raw, length);
|
status = ctap_make_credential(&encoder, pkt_raw, length);
|
||||||
@ -1615,7 +1618,6 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
case CTAP_GET_ASSERTION:
|
case CTAP_GET_ASSERTION:
|
||||||
device_set_status(CTAPHID_STATUS_PROCESSING);
|
|
||||||
printf1(TAG_CTAP,"CTAP_GET_ASSERTION\n");
|
printf1(TAG_CTAP,"CTAP_GET_ASSERTION\n");
|
||||||
timestamp();
|
timestamp();
|
||||||
status = ctap_get_assertion(&encoder, pkt_raw, length);
|
status = ctap_get_assertion(&encoder, pkt_raw, length);
|
||||||
@ -1647,7 +1649,7 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
|
|||||||
break;
|
break;
|
||||||
case CTAP_RESET:
|
case CTAP_RESET:
|
||||||
printf1(TAG_CTAP,"CTAP_RESET\n");
|
printf1(TAG_CTAP,"CTAP_RESET\n");
|
||||||
if (ctap_user_presence_test(CTAP2_UP_DELAY_MS))
|
if (ctap2_user_presence_test(CTAP2_UP_DELAY_MS))
|
||||||
{
|
{
|
||||||
ctap_reset();
|
ctap_reset();
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ build firmware hacker solo
|
|||||||
build firmware hacker-debug-1 solo
|
build firmware hacker-debug-1 solo
|
||||||
build firmware hacker-debug-2 solo
|
build firmware hacker-debug-2 solo
|
||||||
build firmware secure solo
|
build firmware secure solo
|
||||||
|
build firmware secure-non-solokeys solo
|
||||||
|
|
||||||
pip install -U pip
|
pip install -U pip
|
||||||
pip install -U solo-python
|
pip install -U solo-python
|
||||||
@ -49,3 +50,6 @@ 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-hacker-debug-1-${version}.hex ${bundle}.hex
|
||||||
bundle="bundle-hacker-debug-2-${version}"
|
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-hacker-debug-2-${version}.hex ${bundle}.hex
|
||||||
|
bundle="bundle-secure-non-solokeys-${version}"
|
||||||
|
/opt/conda/bin/solo mergehex bootloader-verifying-${version}.hex firmware-secure-non-solokeys-${version}.hex ${bundle}.hex
|
||||||
|
sha256sum ${bundle}.hex > ${bundle}.sha2
|
||||||
|
@ -11,6 +11,11 @@ nav:
|
|||||||
- FIDO2 Implementation: solo/fido2-impl.md
|
- FIDO2 Implementation: solo/fido2-impl.md
|
||||||
- Metadata Statements: solo/metadata-statements.md
|
- Metadata Statements: solo/metadata-statements.md
|
||||||
- Build instructions: solo/building.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
|
||||||
|
- Running on Nucleo32 board: solo/nucleo32-board.md
|
||||||
- Signed update process: solo/signed-updates.md
|
- Signed update process: solo/signed-updates.md
|
||||||
- Code documentation: solo/code-overview.md
|
- Code documentation: solo/code-overview.md
|
||||||
- Contributing Code: solo/contributing.md
|
- Contributing Code: solo/contributing.md
|
||||||
|
@ -21,6 +21,9 @@ firmware-hacker-debug-1:
|
|||||||
firmware-hacker-debug-2:
|
firmware-hacker-debug-2:
|
||||||
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=2 EXTRA_DEFINES='-DSOLO_HACKER -DFLASH_ROP=0'
|
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=2 EXTRA_DEFINES='-DSOLO_HACKER -DFLASH_ROP=0'
|
||||||
|
|
||||||
|
firmware-secure-non-solokeys:
|
||||||
|
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=0 EXTRA_DEFINES='-DFLASH_ROP=2'
|
||||||
|
|
||||||
firmware-secure:
|
firmware-secure:
|
||||||
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=0 EXTRA_DEFINES='-DUSE_SOLOKEYS_CERT -DFLASH_ROP=2'
|
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=0 EXTRA_DEFINES='-DUSE_SOLOKEYS_CERT -DFLASH_ROP=2'
|
||||||
|
|
||||||
@ -59,7 +62,6 @@ boot-no-sig:
|
|||||||
|
|
||||||
build-release-locked: cbor clean2 boot-sig-checking clean all-locked
|
build-release-locked: cbor clean2 boot-sig-checking clean all-locked
|
||||||
$(VENV) $(merge_hex) solo.hex bootloader.hex all.hex
|
$(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
|
build-release: cbor clean2 boot-sig-checking clean all
|
||||||
$(VENV) $(merge_hex) solo.hex bootloader.hex all.hex
|
$(VENV) $(merge_hex) solo.hex bootloader.hex all.hex
|
||||||
|
@ -6,7 +6,7 @@ AR=$(PREFIX)arm-none-eabi-ar
|
|||||||
DRIVER_LIBS := lib/stm32l4xx_hal_pcd.c lib/stm32l4xx_hal_pcd_ex.c lib/stm32l4xx_ll_gpio.c \
|
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_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_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 \
|
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_conf.c lib/usbd/usbd_core.c lib/usbd/usbd_ioreq.c \
|
||||||
|
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
@ -26,151 +26,173 @@ static uint8_t *USBD_Composite_GetOtherSpeedCfgDesc (uint16_t *length);
|
|||||||
|
|
||||||
static uint8_t *USBD_Composite_GetDeviceQualifierDescriptor (uint16_t *length);
|
static uint8_t *USBD_Composite_GetDeviceQualifierDescriptor (uint16_t *length);
|
||||||
|
|
||||||
#define NUM_INTERFACES 2
|
#define NUM_CLASSES 2
|
||||||
|
#define NUM_INTERFACES 3
|
||||||
|
|
||||||
#if NUM_INTERFACES>1
|
#if NUM_INTERFACES>1
|
||||||
#define COMPOSITE_CDC_HID_DESCRIPTOR_SIZE (90)
|
#define COMPOSITE_CDC_HID_DESCRIPTOR_SIZE (90 + 8+9 + 4)
|
||||||
#else
|
#else
|
||||||
#define COMPOSITE_CDC_HID_DESCRIPTOR_SIZE (41)
|
#define COMPOSITE_CDC_HID_DESCRIPTOR_SIZE (41)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define HID_INTF_NUM 0
|
#define HID_INTF_NUM 0
|
||||||
#define CDC_INTF_NUM 1
|
#define CDC_MASTER_INTF_NUM 1
|
||||||
|
#define CDC_SLAVE_INTF_NUM 2
|
||||||
__ALIGN_BEGIN uint8_t COMPOSITE_CDC_HID_DESCRIPTOR[COMPOSITE_CDC_HID_DESCRIPTOR_SIZE] __ALIGN_END =
|
__ALIGN_BEGIN uint8_t COMPOSITE_CDC_HID_DESCRIPTOR[COMPOSITE_CDC_HID_DESCRIPTOR_SIZE] __ALIGN_END =
|
||||||
{
|
{
|
||||||
/*Configuration Descriptor*/
|
/*Configuration Descriptor*/
|
||||||
0x09, /* bLength: Configuration Descriptor size */
|
0x09, /* bLength: Configuration Descriptor size */
|
||||||
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
|
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
|
||||||
COMPOSITE_CDC_HID_DESCRIPTOR_SIZE, /* wTotalLength:no of returned bytes */
|
COMPOSITE_CDC_HID_DESCRIPTOR_SIZE, /* wTotalLength:no of returned bytes */
|
||||||
0x00,
|
0x00,
|
||||||
NUM_INTERFACES, /* bNumInterfaces: 1 interface */
|
NUM_INTERFACES, /* bNumInterfaces */
|
||||||
0x01, /* bConfigurationValue: Configuration value */
|
0x01, /* bConfigurationValue: Configuration value */
|
||||||
0x00, /* iConfiguration: Index of string descriptor describing the configuration */
|
0x00, /* iConfiguration: Index of string descriptor describing the configuration */
|
||||||
0x80, /* bmAttributes: self powered */
|
0x80, /* bmAttributes: self powered */
|
||||||
0x32, /* MaxPower 100 mA */
|
0x32, /* MaxPower 100 mA */
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
/* */
|
/* */
|
||||||
/* HID */
|
/* HID */
|
||||||
/* */
|
/* */
|
||||||
|
|
||||||
/************** Descriptor of Joystick Mouse interface ****************/
|
/************** Descriptor of Joystick Mouse interface ****************/
|
||||||
0x09, /*bLength: Interface Descriptor size*/
|
0x09, /*bLength: Interface Descriptor size*/
|
||||||
USB_DESC_TYPE_INTERFACE,/*bDescriptorType: Interface descriptor type*/
|
USB_DESC_TYPE_INTERFACE, /*bDescriptorType: Interface descriptor type*/
|
||||||
HID_INTF_NUM, /*bInterfaceNumber: Number of Interface*/
|
HID_INTF_NUM, /*bInterfaceNumber: Number of Interface*/
|
||||||
0x00, /*bAlternateSetting: Alternate setting*/
|
0x00, /*bAlternateSetting: Alternate setting*/
|
||||||
0x02, /*bNumEndpoints*/
|
0x02, /*bNumEndpoints*/
|
||||||
0x03, /*bInterfaceClass: HID*/
|
0x03, /*bInterfaceClass: HID*/
|
||||||
0x00, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
|
0x00, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
|
||||||
0x00, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
|
0x00, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
|
||||||
2, /*iInterface: Index of string descriptor*/
|
2, /*iInterface: Index of string descriptor*/
|
||||||
/******************** Descriptor of Joystick Mouse HID ********************/
|
/******************** Descriptor of Joystick Mouse HID ********************/
|
||||||
0x09, /*bLength: HID Descriptor size*/
|
0x09, /*bLength: HID Descriptor size*/
|
||||||
HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/
|
HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/
|
||||||
0x11, /*bcdHID: HID Class Spec release number*/
|
0x11, /*bcdHID: HID Class Spec release number*/
|
||||||
0x01,
|
0x01,
|
||||||
0x00, /*bCountryCode: Hardware target country*/
|
0x00, /*bCountryCode: Hardware target country*/
|
||||||
0x01, /*bNumDescriptors: Number of HID class descriptors to follow*/
|
0x01, /*bNumDescriptors: Number of HID class descriptors to follow*/
|
||||||
0x22, /*bDescriptorType*/
|
0x22, /*bDescriptorType*/
|
||||||
HID_FIDO_REPORT_DESC_SIZE,/*wItemLength: Total length of Report descriptor*/
|
HID_FIDO_REPORT_DESC_SIZE, /*wItemLength: Total length of Report descriptor*/
|
||||||
0,
|
0,
|
||||||
/******************** Descriptor of Mouse endpoint ********************/
|
/******************** Descriptor of Mouse endpoint ********************/
|
||||||
0x07, /*bLength: Endpoint Descriptor size*/
|
0x07, /*bLength: Endpoint Descriptor size*/
|
||||||
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
|
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
|
||||||
HID_EPIN_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
|
HID_EPIN_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
|
||||||
0x03, /*bmAttributes: Interrupt endpoint*/
|
0x03, /*bmAttributes: Interrupt endpoint*/
|
||||||
HID_EPIN_SIZE, /*wMaxPacketSize: 4 Byte max */
|
HID_EPIN_SIZE, /*wMaxPacketSize: 4 Byte max */
|
||||||
0x00,
|
0x00,
|
||||||
HID_BINTERVAL, /*bInterval: Polling Interval */
|
HID_BINTERVAL, /*bInterval: Polling Interval */
|
||||||
|
|
||||||
0x07, /*bLength: Endpoint Descriptor size*/
|
0x07, /*bLength: Endpoint Descriptor size*/
|
||||||
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
|
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
|
||||||
HID_EPOUT_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
|
HID_EPOUT_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
|
||||||
0x03, /*bmAttributes: Interrupt endpoint*/
|
0x03, /*bmAttributes: Interrupt endpoint*/
|
||||||
HID_EPOUT_SIZE, /*wMaxPacketSize: 4 Byte max */
|
HID_EPOUT_SIZE, /*wMaxPacketSize: 4 Byte max */
|
||||||
0x00,
|
0x00,
|
||||||
HID_BINTERVAL, /*bInterval: Polling Interval */
|
HID_BINTERVAL, /*bInterval: Polling Interval */
|
||||||
|
|
||||||
|
#if NUM_INTERFACES > 1
|
||||||
|
|
||||||
|
/* */
|
||||||
|
/* 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: */
|
||||||
|
|
||||||
/* */
|
/*Header Functional Descriptor*/
|
||||||
/* CDC */
|
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 */
|
/*ACM Functional Descriptor*/
|
||||||
0x09, /* bLength: Interface Descriptor size */
|
0x04, /* bFunctionLength */
|
||||||
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
|
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||||
/* Interface descriptor type */
|
0x02, /* bDescriptorSubtype: Abstract Control Management desc */
|
||||||
/*!*/ CDC_INTF_NUM, /* bInterfaceNumber: Number of Interface */
|
0x02, /* bmCapabilities */
|
||||||
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: */
|
|
||||||
|
|
||||||
/*Header Functional Descriptor*/
|
/*Union Functional Descriptor*/
|
||||||
0x05, /* bLength: Endpoint Descriptor size */
|
0x05, /* bFunctionLength */
|
||||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||||
0x00, /* bDescriptorSubtype: Header Func Desc */
|
0x06, /* bDescriptorSubtype: Union func desc */
|
||||||
0x10, /* bcdCDC: spec release number */
|
/*!*/ CDC_MASTER_INTF_NUM, /* bMasterInterface: Communication class interface */
|
||||||
0x01,
|
/*!*/ CDC_SLAVE_INTF_NUM, /* bSlaveInterface0: Data Class Interface */
|
||||||
|
|
||||||
/*Call Management Functional Descriptor*/
|
/* Control Endpoint Descriptor*/
|
||||||
0x05, /* bFunctionLength */
|
0x07, /* bLength: Endpoint Descriptor size */
|
||||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
|
||||||
0x01, /* bDescriptorSubtype: Call Management Func Desc */
|
CDC_CMD_EP, /* bEndpointAddress */
|
||||||
0x00, /* bmCapabilities: D0+D1 */
|
0x03, /* bmAttributes: Interrupt */
|
||||||
/*!*/ CDC_INTF_NUM, /* bDataInterface: 0 */
|
LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */
|
||||||
|
HIBYTE(CDC_CMD_PACKET_SIZE),
|
||||||
|
0x10, /* bInterval: */
|
||||||
|
|
||||||
/*ACM Functional Descriptor*/
|
/* Interface descriptor */
|
||||||
0x04, /* bFunctionLength */
|
0x09, /* bLength */
|
||||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
USB_DESC_TYPE_INTERFACE, /* bDescriptorType */
|
||||||
0x02, /* bDescriptorSubtype: Abstract Control Management desc */
|
CDC_SLAVE_INTF_NUM, /* bInterfaceNumber */
|
||||||
0x02, /* bmCapabilities */
|
0x00, /* bAlternateSetting */
|
||||||
|
0x02, /* bNumEndpoints */
|
||||||
|
0x0A, /* bInterfaceClass: Communication class data */
|
||||||
|
0x00, /* bInterfaceSubClass */
|
||||||
|
0x00, /* bInterfaceProtocol */
|
||||||
|
0x00,
|
||||||
|
|
||||||
/*Union Functional Descriptor*/
|
/*Endpoint OUT Descriptor*/
|
||||||
0x05, /* bFunctionLength */
|
0x07, /* bLength: Endpoint Descriptor size */
|
||||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
|
||||||
0x06, /* bDescriptorSubtype: Union func desc */
|
CDC_OUT_EP, /* bEndpointAddress */
|
||||||
/*!*/ CDC_INTF_NUM, /* bMasterInterface: Communication class interface */
|
0x02, /* bmAttributes: Bulk */
|
||||||
/*!*/ CDC_INTF_NUM, /* bSlaveInterface0: Data Class Interface */
|
LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
|
||||||
|
HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
|
||||||
|
0x00, /* bInterval: ignore for Bulk transfer */
|
||||||
|
|
||||||
/*Endpoint 2 Descriptor*/
|
/*Endpoint IN Descriptor*/
|
||||||
0x07, /* bLength: Endpoint Descriptor size */
|
0x07, /* bLength: Endpoint Descriptor size */
|
||||||
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
|
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
|
||||||
CDC_CMD_EP, /* bEndpointAddress */
|
CDC_IN_EP, /* bEndpointAddress */
|
||||||
0x03, /* bmAttributes: Interrupt */
|
0x02, /* bmAttributes: Bulk */
|
||||||
LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */
|
LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
|
||||||
HIBYTE(CDC_CMD_PACKET_SIZE),
|
HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
|
||||||
0x10, /* bInterval: */
|
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 */
|
|
||||||
|
|
||||||
/*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 */
|
|
||||||
|
|
||||||
|
4, /* Descriptor size */
|
||||||
|
3, /* Descriptor type */
|
||||||
|
0x09,
|
||||||
|
0x04,
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
USBD_ClassTypeDef USBD_Composite =
|
USBD_ClassTypeDef USBD_Composite =
|
||||||
{
|
{
|
||||||
USBD_Composite_Init,
|
USBD_Composite_Init,
|
||||||
@ -195,14 +217,27 @@ int in_endpoint_to_class[MAX_ENDPOINTS];
|
|||||||
|
|
||||||
int out_endpoint_to_class[MAX_ENDPOINTS];
|
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 *hid_class, USBD_ClassTypeDef *cdc_class) {
|
||||||
USBD_Classes[0] = class0;
|
USBD_Classes[0] = hid_class;
|
||||||
USBD_Classes[1] = class1;
|
USBD_Classes[1] = cdc_class;
|
||||||
|
}
|
||||||
|
|
||||||
|
static USBD_ClassTypeDef * getClass(uint8_t index)
|
||||||
|
{
|
||||||
|
switch(index)
|
||||||
|
{
|
||||||
|
case HID_INTF_NUM:
|
||||||
|
return USBD_Classes[0];
|
||||||
|
case CDC_MASTER_INTF_NUM:
|
||||||
|
case CDC_SLAVE_INTF_NUM:
|
||||||
|
return USBD_Classes[1];
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t USBD_Composite_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx) {
|
static uint8_t USBD_Composite_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx) {
|
||||||
int i;
|
int i;
|
||||||
for(i = 0; i < NUM_INTERFACES; i++) {
|
for(i = 0; i < NUM_CLASSES; i++) {
|
||||||
if (USBD_Classes[i]->Init(pdev, cfgidx) != USBD_OK) {
|
if (USBD_Classes[i]->Init(pdev, cfgidx) != USBD_OK) {
|
||||||
return USBD_FAIL;
|
return USBD_FAIL;
|
||||||
}
|
}
|
||||||
@ -213,7 +248,7 @@ static uint8_t USBD_Composite_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx) {
|
|||||||
|
|
||||||
static uint8_t USBD_Composite_DeInit (USBD_HandleTypeDef *pdev, uint8_t cfgidx) {
|
static uint8_t USBD_Composite_DeInit (USBD_HandleTypeDef *pdev, uint8_t cfgidx) {
|
||||||
int i;
|
int i;
|
||||||
for(i = 0; i < NUM_INTERFACES; i++) {
|
for(i = 0; i < NUM_CLASSES; i++) {
|
||||||
if (USBD_Classes[i]->DeInit(pdev, cfgidx) != USBD_OK) {
|
if (USBD_Classes[i]->DeInit(pdev, cfgidx) != USBD_OK) {
|
||||||
return USBD_FAIL;
|
return USBD_FAIL;
|
||||||
}
|
}
|
||||||
@ -224,10 +259,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) {
|
static uint8_t USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) {
|
||||||
int i;
|
int i;
|
||||||
|
USBD_ClassTypeDef * device_class;
|
||||||
|
device_class = getClass(req->wIndex);
|
||||||
|
|
||||||
switch (req->bmRequest & USB_REQ_TYPE_MASK) {
|
switch (req->bmRequest & USB_REQ_TYPE_MASK) {
|
||||||
case USB_REQ_TYPE_CLASS :
|
case USB_REQ_TYPE_CLASS :
|
||||||
if (req->wIndex < NUM_INTERFACES)
|
if (device_class != NULL)
|
||||||
return USBD_Classes[req->wIndex]->Setup(pdev, req);
|
return device_class->Setup(pdev, req);
|
||||||
else
|
else
|
||||||
return USBD_FAIL;
|
return USBD_FAIL;
|
||||||
|
|
||||||
@ -236,7 +274,7 @@ static uint8_t USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqType
|
|||||||
switch (req->bRequest) {
|
switch (req->bRequest) {
|
||||||
|
|
||||||
case USB_REQ_GET_DESCRIPTOR :
|
case USB_REQ_GET_DESCRIPTOR :
|
||||||
for(i = 0; i < NUM_INTERFACES; i++) {
|
for(i = 0; i < NUM_CLASSES; i++) {
|
||||||
if (USBD_Classes[i]->Setup(pdev, req) != USBD_OK) {
|
if (USBD_Classes[i]->Setup(pdev, req) != USBD_OK) {
|
||||||
return USBD_FAIL;
|
return USBD_FAIL;
|
||||||
}
|
}
|
||||||
@ -246,8 +284,8 @@ static uint8_t USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqType
|
|||||||
|
|
||||||
case USB_REQ_GET_INTERFACE :
|
case USB_REQ_GET_INTERFACE :
|
||||||
case USB_REQ_SET_INTERFACE :
|
case USB_REQ_SET_INTERFACE :
|
||||||
if (req->wIndex < NUM_INTERFACES)
|
if (device_class != NULL)
|
||||||
return USBD_Classes[req->wIndex]->Setup(pdev, req);
|
return device_class->Setup(pdev, req);
|
||||||
else
|
else
|
||||||
return USBD_FAIL;
|
return USBD_FAIL;
|
||||||
}
|
}
|
||||||
@ -274,7 +312,7 @@ static uint8_t USBD_Composite_DataOut (USBD_HandleTypeDef *pdev, uint8_t epnum)
|
|||||||
|
|
||||||
static uint8_t USBD_Composite_EP0_RxReady (USBD_HandleTypeDef *pdev) {
|
static uint8_t USBD_Composite_EP0_RxReady (USBD_HandleTypeDef *pdev) {
|
||||||
int i;
|
int i;
|
||||||
for(i = 0; i < NUM_INTERFACES; i++) {
|
for(i = 0; i < NUM_CLASSES; i++) {
|
||||||
if (USBD_Classes[i]->EP0_RxReady != NULL) {
|
if (USBD_Classes[i]->EP0_RxReady != NULL) {
|
||||||
if (USBD_Classes[i]->EP0_RxReady(pdev) != USBD_OK) {
|
if (USBD_Classes[i]->EP0_RxReady(pdev) != USBD_OK) {
|
||||||
return USBD_FAIL;
|
return USBD_FAIL;
|
||||||
|
@ -157,6 +157,11 @@ void crypto_sha256_hmac_final(uint8_t * key, uint32_t klen, uint8_t * hmac)
|
|||||||
key = master_secret;
|
key = master_secret;
|
||||||
klen = sizeof(master_secret)/2;
|
klen = sizeof(master_secret)/2;
|
||||||
}
|
}
|
||||||
|
else if (key == CRYPTO_TRANSPORT_KEY2)
|
||||||
|
{
|
||||||
|
key = transport_secret;
|
||||||
|
klen = 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if(klen > 64)
|
if(klen > 64)
|
||||||
|
@ -38,6 +38,8 @@ void wait_for_usb_tether();
|
|||||||
|
|
||||||
|
|
||||||
uint32_t __90_ms = 0;
|
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 __device_status = 0;
|
||||||
uint32_t __last_update = 0;
|
uint32_t __last_update = 0;
|
||||||
extern PCD_HandleTypeDef hpcd;
|
extern PCD_HandleTypeDef hpcd;
|
||||||
@ -75,6 +77,21 @@ void TIM6_DAC_IRQHandler()
|
|||||||
ctaphid_update_status(__device_status);
|
ctaphid_update_status(__device_status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (is_touch_button_pressed == IS_BUTTON_PRESSED)
|
||||||
|
{
|
||||||
|
if (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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef IS_BOOTLOADER
|
#ifndef IS_BOOTLOADER
|
||||||
// NFC sending WTX if needs
|
// NFC sending WTX if needs
|
||||||
if (device_is_nfc() == NFC_IS_ACTIVE)
|
if (device_is_nfc() == NFC_IS_ACTIVE)
|
||||||
@ -84,6 +101,21 @@ void TIM6_DAC_IRQHandler()
|
|||||||
#endif
|
#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
|
// Global USB interrupt handler
|
||||||
void USB_IRQHandler(void)
|
void USB_IRQHandler(void)
|
||||||
{
|
{
|
||||||
@ -493,6 +525,41 @@ static int handle_packets()
|
|||||||
return 0;
|
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 ctap_user_presence_test(uint32_t up_delay)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
@ -500,6 +567,7 @@ int ctap_user_presence_test(uint32_t up_delay)
|
|||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if SKIP_BUTTON_CHECK_WITH_DELAY
|
#if SKIP_BUTTON_CHECK_WITH_DELAY
|
||||||
int i=500;
|
int i=500;
|
||||||
while(i--)
|
while(i--)
|
||||||
@ -512,53 +580,41 @@ int ctap_user_presence_test(uint32_t up_delay)
|
|||||||
#elif SKIP_BUTTON_CHECK_FAST
|
#elif SKIP_BUTTON_CHECK_FAST
|
||||||
delay(2);
|
delay(2);
|
||||||
ret = handle_packets();
|
ret = handle_packets();
|
||||||
if (ret) return ret;
|
if (ret)
|
||||||
|
return ret;
|
||||||
goto done;
|
goto done;
|
||||||
#endif
|
#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);
|
led_rgb(0xff3520);
|
||||||
|
|
||||||
if (IS_BUTTON_PRESSED == is_touch_button_pressed)
|
// Block and wait for some time.
|
||||||
{
|
ret = wait_for_button_activate(up_delay);
|
||||||
// Wait for user to release touch button if it's already pressed
|
if (ret) return ret;
|
||||||
while (IS_BUTTON_PRESSED())
|
ret = wait_for_button_release(up_delay);
|
||||||
{
|
|
||||||
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();
|
|
||||||
if (ret) return ret;
|
if (ret) return ret;
|
||||||
}
|
|
||||||
while (! IS_BUTTON_PRESSED());
|
|
||||||
|
|
||||||
led_rgb(0x001040);
|
// If button was pressed within last [2] seconds, succeed.
|
||||||
|
if (__last_button_press_time && (millis() - __last_button_press_time < 2000))
|
||||||
delay(50);
|
{
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
|
||||||
#if SKIP_BUTTON_CHECK_WITH_DELAY || SKIP_BUTTON_CHECK_FAST
|
|
||||||
done:
|
done:
|
||||||
#endif
|
ret = wait_for_button_release(up_delay);
|
||||||
return 1;
|
__last_button_press_time = 0;
|
||||||
|
return 1;
|
||||||
|
|
||||||
fail:
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ctap_generate_rng(uint8_t * dst, size_t num)
|
int ctap_generate_rng(uint8_t * dst, size_t num)
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "stm32l4xx_ll_rng.h"
|
#include "stm32l4xx_ll_rng.h"
|
||||||
#include "stm32l4xx_ll_spi.h"
|
#include "stm32l4xx_ll_spi.h"
|
||||||
#include "stm32l4xx_ll_usb.h"
|
#include "stm32l4xx_ll_usb.h"
|
||||||
|
#include "stm32l4xx_ll_exti.h"
|
||||||
#include "stm32l4xx_hal_pcd.h"
|
#include "stm32l4xx_hal_pcd.h"
|
||||||
#include "stm32l4xx_hal.h"
|
#include "stm32l4xx_hal.h"
|
||||||
|
|
||||||
@ -849,19 +850,17 @@ void init_gpio(void)
|
|||||||
LL_GPIO_SetPinMode(SOLO_BUTTON_PORT,SOLO_BUTTON_PIN,LL_GPIO_MODE_INPUT);
|
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);
|
LL_GPIO_SetPinPull(SOLO_BUTTON_PORT,SOLO_BUTTON_PIN,LL_GPIO_PULL_UP);
|
||||||
|
|
||||||
#ifdef SOLO_AMS_IRQ_PORT
|
#ifndef IS_BOOTLOADER
|
||||||
// SAVE POWER
|
LL_SYSCFG_SetEXTISource(LL_SYSCFG_EXTI_PORTA, LL_SYSCFG_EXTI_LINE0);
|
||||||
// LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOC);
|
LL_EXTI_InitTypeDef EXTI_InitStruct;
|
||||||
// /**/
|
EXTI_InitStruct.Line_0_31 = LL_EXTI_LINE_0; // GPIOA_0
|
||||||
// LL_GPIO_InitTypeDef GPIO_InitStruct;
|
EXTI_InitStruct.Line_32_63 = LL_EXTI_LINE_NONE;
|
||||||
// GPIO_InitStruct.Pin = SOLO_AMS_IRQ_PIN;
|
EXTI_InitStruct.LineCommand = ENABLE;
|
||||||
// GPIO_InitStruct.Mode = LL_GPIO_MODE_INPUT;
|
EXTI_InitStruct.Mode = LL_EXTI_MODE_IT;
|
||||||
// GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
|
EXTI_InitStruct.Trigger = LL_EXTI_TRIGGER_RISING;
|
||||||
// LL_GPIO_Init(SOLO_AMS_IRQ_PORT, &GPIO_InitStruct);
|
LL_EXTI_Init(&EXTI_InitStruct);
|
||||||
//
|
|
||||||
//
|
NVIC_EnableIRQ(EXTI0_IRQn);
|
||||||
// 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);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -92,19 +92,27 @@ int nfc_init()
|
|||||||
return NFC_IS_NA;
|
return NFC_IS_NA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint8_t gl_int0 = 0;
|
||||||
void process_int0(uint8_t int0)
|
void process_int0(uint8_t int0)
|
||||||
{
|
{
|
||||||
|
gl_int0 = int0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ams_wait_for_tx(uint32_t timeout_ms)
|
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();
|
uint32_t tstart = millis();
|
||||||
while (tstart + timeout_ms > millis())
|
while (tstart + timeout_ms > millis())
|
||||||
{
|
{
|
||||||
uint8_t int0 = ams_read_reg(AMS_REG_INT0);
|
uint8_t int0 = ams_read_reg(AMS_REG_INT0);
|
||||||
if (int0) process_int0(int0);
|
process_int0(int0);
|
||||||
if (int0 & AMS_INT_TXE)
|
if (int0 & AMS_INT_TXE || int0 & AMS_INT_RXE)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
delay(1);
|
delay(1);
|
||||||
@ -121,8 +129,13 @@ bool ams_receive_with_timeout(uint32_t timeout_ms, uint8_t * data, int maxlen, i
|
|||||||
uint32_t tstart = millis();
|
uint32_t tstart = millis();
|
||||||
while (tstart + timeout_ms > millis())
|
while (tstart + timeout_ms > millis())
|
||||||
{
|
{
|
||||||
uint8_t int0 = ams_read_reg(AMS_REG_INT0);
|
uint8_t int0 = 0;
|
||||||
if (int0) process_int0(int0);
|
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);
|
uint8_t buffer_status2 = ams_read_reg(AMS_REG_BUF2);
|
||||||
|
|
||||||
if (buffer_status2 && (int0 & AMS_INT_RXE))
|
if (buffer_status2 && (int0 & AMS_INT_RXE))
|
||||||
@ -196,7 +209,6 @@ bool nfc_write_response(uint8_t req0, uint16_t resp)
|
|||||||
void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len)
|
void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len)
|
||||||
{
|
{
|
||||||
uint8_t res[32 + 2];
|
uint8_t res[32 + 2];
|
||||||
int sendlen = 0;
|
|
||||||
uint8_t iBlock = NFC_CMD_IBLOCK | (req0 & 0x0f);
|
uint8_t iBlock = NFC_CMD_IBLOCK | (req0 & 0x0f);
|
||||||
uint8_t block_offset = p14443_block_offset(req0);
|
uint8_t block_offset = p14443_block_offset(req0);
|
||||||
|
|
||||||
@ -208,6 +220,7 @@ void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len)
|
|||||||
memcpy(&res[block_offset], data, len);
|
memcpy(&res[block_offset], data, len);
|
||||||
nfc_write_frame(res, len + block_offset);
|
nfc_write_frame(res, len + block_offset);
|
||||||
} else {
|
} else {
|
||||||
|
int sendlen = 0;
|
||||||
do {
|
do {
|
||||||
// transmit I block
|
// transmit I block
|
||||||
int vlen = MIN(32 - block_offset, len - sendlen);
|
int vlen = MIN(32 - block_offset, len - sendlen);
|
||||||
@ -227,11 +240,11 @@ void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len)
|
|||||||
sendlen += vlen;
|
sendlen += vlen;
|
||||||
|
|
||||||
// wait for transmit (32 bytes aprox 2,5ms)
|
// wait for transmit (32 bytes aprox 2,5ms)
|
||||||
// if (!ams_wait_for_tx(10))
|
if (!ams_wait_for_tx(5))
|
||||||
// {
|
{
|
||||||
// printf1(TAG_NFC, "TX timeout. slen: %d \r\n", sendlen);
|
printf1(TAG_NFC, "TX timeout. slen: %d \r\n", sendlen);
|
||||||
// break;
|
break;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// if needs to receive R block (not a last block)
|
// if needs to receive R block (not a last block)
|
||||||
if (res[0] & 0x10)
|
if (res[0] & 0x10)
|
||||||
@ -316,7 +329,7 @@ bool WTX_off()
|
|||||||
void WTX_timer_exec()
|
void WTX_timer_exec()
|
||||||
{
|
{
|
||||||
// condition: (timer on) or (not expired[300ms])
|
// condition: (timer on) or (not expired[300ms])
|
||||||
if ((WTX_timer <= 0) || WTX_timer + 300 > millis())
|
if ((WTX_timer == 0) || WTX_timer + 300 > millis())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
WTX_process(10);
|
WTX_process(10);
|
||||||
@ -327,12 +340,12 @@ void WTX_timer_exec()
|
|||||||
// read timeout must be 10 ms to call from interrupt
|
// read timeout must be 10 ms to call from interrupt
|
||||||
bool WTX_process(int read_timeout)
|
bool WTX_process(int read_timeout)
|
||||||
{
|
{
|
||||||
uint8_t wtx[] = {0xf2, 0x01};
|
|
||||||
if (WTX_fail)
|
if (WTX_fail)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!WTX_sent)
|
if (!WTX_sent)
|
||||||
{
|
{
|
||||||
|
uint8_t wtx[] = {0xf2, 0x01};
|
||||||
nfc_write_frame(wtx, sizeof(wtx));
|
nfc_write_frame(wtx, sizeof(wtx));
|
||||||
WTX_sent = true;
|
WTX_sent = true;
|
||||||
return true;
|
return true;
|
||||||
@ -618,7 +631,7 @@ void nfc_process_iblock(uint8_t * buf, int len)
|
|||||||
if (!WTX_off())
|
if (!WTX_off())
|
||||||
return;
|
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)
|
if (status == CTAP1_ERR_SUCCESS)
|
||||||
{
|
{
|
||||||
@ -687,7 +700,14 @@ void nfc_process_block(uint8_t * buf, unsigned int len)
|
|||||||
|
|
||||||
if (IS_PPSS_CMD(buf[0]))
|
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]))
|
else if (IS_IBLOCK(buf[0]))
|
||||||
{
|
{
|
||||||
@ -779,6 +799,8 @@ int nfc_loop()
|
|||||||
|
|
||||||
|
|
||||||
read_reg_block(&ams);
|
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;
|
uint8_t state = AMS_STATE_MASK & ams.regs.rfid_status;
|
||||||
|
|
||||||
if (state != AMS_STATE_SELECTED && state != AMS_STATE_SELECTEDX)
|
if (state != AMS_STATE_SELECTED && state != AMS_STATE_SELECTEDX)
|
||||||
@ -792,7 +814,7 @@ int nfc_loop()
|
|||||||
// if (state != AMS_STATE_SENSE)
|
// if (state != AMS_STATE_SENSE)
|
||||||
// printf1(TAG_NFC," %s x%02x\r\n", ams_get_state_string(ams.regs.rfid_status), state);
|
// 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();
|
nfc_state_init();
|
||||||
}
|
}
|
||||||
@ -801,7 +823,7 @@ int nfc_loop()
|
|||||||
// ams_print_int1(ams.regs.int1);
|
// 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)
|
if (ams.regs.buffer_status2)
|
||||||
{
|
{
|
||||||
|
@ -1,64 +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] [nfc] <[u2f]|[fido2]|[rk]|[hid]|[ping]>")
|
|
||||||
print(" sim - test via UDP simulation backend only")
|
|
||||||
print(" nfc - test via NFC interface only")
|
|
||||||
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)
|
|
||||||
|
|
||||||
nfcOnly = False
|
|
||||||
if "nfc" in sys.argv:
|
|
||||||
nfcOnly = True
|
|
||||||
|
|
||||||
t.find_device(nfcOnly)
|
|
||||||
|
|
||||||
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,218 +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
|
|
||||||
self.nfc_interface_only = 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
|
|
||||||
self.nfc_interface_only = tester.nfc_interface_only
|
|
||||||
|
|
||||||
def find_device(self, nfcInterfaceOnly=False):
|
|
||||||
dev = None
|
|
||||||
self.nfc_interface_only = nfcInterfaceOnly
|
|
||||||
if not nfcInterfaceOnly:
|
|
||||||
print("--- HID ---")
|
|
||||||
print(list(CtapHidDevice.list_devices()))
|
|
||||||
dev = next(CtapHidDevice.list_devices(), None)
|
|
||||||
|
|
||||||
if not dev:
|
|
||||||
from fido2.pcsc import CtapPcscDevice
|
|
||||||
|
|
||||||
print("--- NFC ---")
|
|
||||||
print(list(CtapPcscDevice.list_devices()))
|
|
||||||
dev = next(CtapPcscDevice.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(self.nfc_interface_only)
|
|
||||||
|
|
||||||
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.nfc_interface_only)
|
|
||||||
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,123 +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"")
|
|
||||||
assert False
|
|
||||||
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")
|
|
||||||
assert False
|
|
||||||
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