Compare commits
80 Commits
windows_he
...
cache_butt
Author | SHA1 | Date | |
---|---|---|---|
e09e8f4055 | |||
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 | |||
4e3420f19f | |||
6b8e575fac | |||
28788d1eeb | |||
1ee031ae9c | |||
6dfe42e486 | |||
29b2032dae | |||
f9f1e96c73 | |||
e2738d11d3 | |||
a8c7c43e14 | |||
db479850a6 | |||
09d450ed02 | |||
be37ed46f7 | |||
420d052ac9 | |||
3698c942a2 | |||
17a170bb90 | |||
d4e61421b6 |
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.4.0
|
2.4.2
|
||||||
|
251
docs/solo/nucleo32-board.md
Normal file
251
docs/solo/nucleo32-board.md
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
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 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 env3
|
||||||
|
. env3/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://webauthn.bin.coffee/>
|
||||||
|
2. <https://github.com/apowers313/fido2-server-demo/>
|
||||||
|
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
|
@ -11,6 +11,7 @@ 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
|
||||||
|
- 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
|
||||||
|
@ -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
@ -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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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,230 +0,0 @@
|
|||||||
import time, struct
|
|
||||||
|
|
||||||
from fido2.hid import CtapHidDevice
|
|
||||||
from fido2.client import Fido2Client
|
|
||||||
from fido2.attestation import Attestation
|
|
||||||
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):
|
|
||||||
attestation_object = self.testFunc(
|
|
||||||
self.ctap.make_credential, test, *args, **kwargs
|
|
||||||
)
|
|
||||||
if attestation_object:
|
|
||||||
verifier = Attestation.for_type(attestation_object.fmt)
|
|
||||||
client_data = args[0]
|
|
||||||
verifier().verify(
|
|
||||||
attestation_object.att_statement,
|
|
||||||
attestation_object.auth_data,
|
|
||||||
client_data,
|
|
||||||
)
|
|
||||||
return attestation_object
|
|
||||||
|
|
||||||
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,133 +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)
|
|
||||||
|
|
||||||
self.reboot()
|
|
||||||
|
|
||||||
for i in range(0, self.user_count):
|
|
||||||
with Test(
|
|
||||||
"Post reboot, 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