Compare commits
58 Commits
cache_butt
...
2.5.1
Author | SHA1 | Date | |
---|---|---|---|
5d3914bc5e | |||
abe306a649 | |||
41ceb78f6c | |||
8e192f2363 | |||
affc256ca2 | |||
b3ac739a35 | |||
3b53537077 | |||
3fad9a7a7d | |||
8973608f59 | |||
8af6505f6d | |||
d39d7978fd | |||
c972a13034 | |||
a95e62e2ea | |||
c79b7abfb6 | |||
dfb124dc8b | |||
972760eb78 | |||
0d621d13f9 | |||
728acc1671 | |||
62b4418dac | |||
8059a9765f | |||
b743d5fac5 | |||
dccfb0d1b3 | |||
a72f0ede05 | |||
adcbd3aeb8 | |||
d931954a13 | |||
b706cc30b0 | |||
57fe39704b | |||
4b6619b705 | |||
095b08e3d9 | |||
89e021003a | |||
4f3d4b09eb | |||
3f4843b03a | |||
26af0c423e | |||
19422d9daa | |||
b7a4cf001a | |||
3927aec06d | |||
f5794481ae | |||
caac9d0cc1 | |||
ffadab05a3 | |||
2423154fee | |||
cf79b7865d | |||
6f0cf99c92 | |||
7ef68fd5d3 | |||
3be8611fcf | |||
21489658a7 | |||
a07a3dee8d | |||
416da63a9a | |||
027fa791a3 | |||
3e52d7b42b | |||
301e18c6a2 | |||
44205141eb | |||
6e1110ca9b | |||
9105b988e2 | |||
14c94ea8f5 | |||
435b908c17 | |||
78280e570b | |||
36aec9f20b | |||
02a51454b7 |
@ -168,6 +168,16 @@
|
||||
"infra",
|
||||
"tool"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "kimusan",
|
||||
"name": "Kim Schulz",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/1150049?v=4",
|
||||
"profile": "http://www.schulz.dk",
|
||||
"contributions": [
|
||||
"business",
|
||||
"ideas"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
45
README.md
45
README.md
@ -1,24 +1,18 @@
|
||||
[](https://github.com/solokeys/solo/blob/master/LICENSE)
|
||||
[](#contributors)
|
||||
[](https://travis-ci.com/solokeys/solo)
|
||||
[](https://discourse.solokeys.com)
|
||||
**NEW!** We launched a new tiny security key called Somu, it's live on Crowd Supply and you can [pre-order it now](https://solokeys.com/somu)!
|
||||
|
||||
[<img src="https://miro.medium.com/max/1400/1*PnzCPLqq_5nt1gjgSEY2LQ.png" width="600">](https://solokeys.com/somu)
|
||||
|
||||
Somu is the micro version of Solo. We were inspired to make a secure Tomu, so we took its tiny form factor, we added the secure microcontroller and firmware of Solo, et voilà! Here we have Somu.
|
||||
|
||||
[](https://update.solokeys.com/)
|
||||
[](https://keybase.io/team/solokeys.public)
|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fsolokeys%2Fsolo?ref=badge_shield)
|
||||
|
||||
[](https://github.com/solokeys/solo/releases)
|
||||
[](https://github.com/solokeys/solo/commits/master)
|
||||
[](https://github.com/solokeys/solo/commits/master)
|
||||
[](https://github.com/solokeys/solo/commits/master)
|
||||
[](https://github.com/solokeys/solo/graphs/contributors)
|
||||
|
||||
|
||||
# Solo
|
||||
[](https://travis-ci.com/solokeys/solo)
|
||||
|
||||
Solo is an open source security key, and you can get one at [solokeys.com](https://solokeys.com).
|
||||
|
||||
Solo supports FIDO2 and U2F standards for strong two-factor authentication and password-less login, and it will protect you against phishing and other online attacks. With colored cases and multilingual guides we want to make secure login more personable and accessible to everyone around the globe.
|
||||
[<img src="https://static.solokeys.com/images/photos/hero-on-white-cropped.png" width="600">](https://solokeys.com)
|
||||
|
||||
<img src="https://static.solokeys.com/images/photos/hero-on-white-cropped.png" width="600">
|
||||
Solo supports FIDO2 and U2F standards for strong two-factor authentication and password-less login, and it will protect you against phishing and other online attacks. With colored cases and multilingual guides we want to make secure login more personable and accessible to everyone around the globe.
|
||||
|
||||
This repo contains the Solo firmware, including implementations of FIDO2 and U2F (CTAP2 and CTAP) over USB and NFC. The main implementation is for STM32L432, but it is easily portable.
|
||||
|
||||
@ -42,7 +36,7 @@ Solo for Hacker is a special version of Solo that let you customize its firmware
|
||||
|
||||
Check out [solokeys.com](https://solokeys.com), for options on where to buy Solo. Solo Hacker can be converted to a secure version, but normal Solo cannot be converted to a Hacker version.
|
||||
|
||||
If you have a Solo for Hacker, here's how you can load your own code on it. You can find more details, including how to permanently lock it, in our [documentation](https://docs.solokeys.io/solo/building/). We only support Python3.
|
||||
If you have a Solo for Hacker, here's how you can load your own code on it. You can find more details, including how to permanently lock it, in our [documentation](https://docs.solokeys.io/solo/building/). We support Python3.
|
||||
|
||||
```bash
|
||||
git clone --recurse-submodules https://github.com/solokeys/solo
|
||||
@ -140,6 +134,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
<td align="center"><a href="http://1bitsquared.com"><img src="https://avatars3.githubusercontent.com/u/17334?v=4" width="100px;" alt="Piotr Esden-Tempski"/><br /><sub><b>Piotr Esden-Tempski</b></sub></a><br /><a href="#business-esden" title="Business development">💼</a></td>
|
||||
<td align="center"><a href="https://github.com/m3hm00d"><img src="https://avatars1.githubusercontent.com/u/42179593?v=4" width="100px;" alt="f.m3hm00d"/><br /><sub><b>f.m3hm00d</b></sub></a><br /><a href="https://github.com/solokeys/solo/commits?author=m3hm00d" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://blogs.gnome.org/hughsie/"><img src="https://avatars0.githubusercontent.com/u/151380?v=4" width="100px;" alt="Richard Hughes"/><br /><sub><b>Richard Hughes</b></sub></a><br /><a href="#ideas-hughsie" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/solokeys/solo/commits?author=hughsie" title="Code">💻</a> <a href="#infra-hughsie" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#tool-hughsie" title="Tools">🔧</a></td>
|
||||
<td align="center"><a href="http://www.schulz.dk"><img src="https://avatars1.githubusercontent.com/u/1150049?v=4" width="100px;" alt="Kim Schulz"/><br /><sub><b>Kim Schulz</b></sub></a><br /><a href="#business-kimusan" title="Business development">💼</a> <a href="#ideas-kimusan" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@ -168,3 +163,19 @@ You may use Solo documentation under the terms of the CC-BY-SA 4.0 license
|
||||
|
||||
You can buy Solo, Solo Tap, and Solo for Hackers at [solokeys.com](https://solokeys.com).
|
||||
|
||||
<br/>
|
||||
<hr/>
|
||||
<br/>
|
||||
|
||||
[](https://github.com/solokeys/solo/blob/master/LICENSE)
|
||||
[](#contributors)
|
||||
[](https://travis-ci.com/solokeys/solo)
|
||||
[](https://discourse.solokeys.com)
|
||||
[](https://keybase.io/team/solokeys.public)
|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fsolokeys%2Fsolo?ref=badge_shield)
|
||||
|
||||
[](https://github.com/solokeys/solo/releases)
|
||||
[](https://github.com/solokeys/solo/commits/master)
|
||||
[](https://github.com/solokeys/solo/commits/master)
|
||||
[](https://github.com/solokeys/solo/commits/master)
|
||||
[](https://github.com/solokeys/solo/graphs/contributors)
|
||||
|
@ -1 +1 @@
|
||||
2.4.2
|
||||
2.4.3
|
||||
|
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).
|
||||
|
||||
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
|
||||
|
||||
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 installing prerequisites `pip3 install -r tools/requirements.txt`
|
||||
- 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
|
||||
|
||||
@ -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`
|
||||
into `all.hex`.
|
||||
|
||||
If you're just planning to do development, please don't try to reprogram the bootloader,
|
||||
If you're just planning to do development, **please don't try to reprogram the bootloader**,
|
||||
as this can be risky if done often. Just use `solo.hex`.
|
||||
|
||||
### Building with debug messages
|
||||
@ -86,6 +80,8 @@ solo monitor <serial-port>
|
||||
|
||||
### Building a Solo release
|
||||
|
||||
To build Solo
|
||||
|
||||
If you want to build a release of Solo, we recommend trying a Hacker build first
|
||||
just to make sure that it's working. Otherwise it may not be as easy or possible to
|
||||
fix any mistakes.
|
||||
@ -96,105 +92,13 @@ If you're ready to program a full release, run this recipe to build.
|
||||
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.
|
||||
Then you can switch to a locked down build, which cannot be reprogrammed as easily (or not at all!).
|
||||
Note if you program a secured `solo.hex` file onto a Solo Hacker, it will lock the flash, but the bootloader
|
||||
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).
|
@ -66,7 +66,7 @@ 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
|
||||
### Install ARM tools Linux
|
||||
|
||||
1. Download current [ARM tools] package: [gcc-arm-none-eabi-8-2018-q4-major-linux.tar.bz2].
|
||||
|
||||
@ -75,6 +75,13 @@ See <https://docs.solokeys.io/solo/building/> for the original guide. Here detai
|
||||
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:
|
||||
@ -114,8 +121,8 @@ Do not use it, if you do not plan to do so.
|
||||
```bash
|
||||
# while in the main project directory
|
||||
# create Python virtual environment with required packages, and activate
|
||||
make env3
|
||||
. env3/bin/activate
|
||||
make venv
|
||||
. venv/bin/activate
|
||||
# Run flashing
|
||||
cd ./targets/stm32l432
|
||||
make flash
|
||||
@ -178,8 +185,8 @@ make fido2-test
|
||||
|
||||
#### FIDO2 test sites
|
||||
|
||||
1. <https://webauthn.bin.coffee/>
|
||||
2. <https://github.com/apowers313/fido2-server-demo/>
|
||||
1. <https://www.passwordless.dev/overview>
|
||||
2. <https://webauthn.bin.coffee/>
|
||||
3. <https://webauthn.org/>
|
||||
|
||||
#### U2F test sites
|
||||
|
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
|
||||
```
|
@ -1,20 +1,21 @@
|
||||
# Summary
|
||||
|
||||
On Linux, by default USB dongles can't be accessed by users, for security reasons. To allow user access, so-called "udev rules" must be installed. (Under Fedora, your key may work without such a rule.)
|
||||
On Linux, by default USB dongles can't be accessed by users, for security reasons. To allow user access, so-called "udev rules" must be installed.
|
||||
|
||||
Create a file like [`70-solokeys-access.rules`](https://github.com/solokeys/solo/blob/master/udev/70-solokeys-access.rules) in your `/etc/udev/rules.d` directory, for instance the following rule should cover normal access (it has to be on one line):
|
||||
For some users, things will work automatically:
|
||||
|
||||
```
|
||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess", MODE="0660", GROUP="plugdev"
|
||||
```
|
||||
- Fedora seems to use a ["universal" udev rule for FIDO devices](https://github.com/amluto/u2f-hidraw-policy)
|
||||
- Our udev rule made it into [libu2f-host](https://github.com/Yubico/libu2f-host/) v1.1.10
|
||||
- Arch Linux [has this package](https://www.archlinux.org/packages/community/x86_64/libu2f-host/)
|
||||
- [Debian sid](https://packages.debian.org/sid/libu2f-udev) and [Ubuntu Eon](https://packages.ubuntu.com/eoan/libu2f-udev) can use the `libu2f-udev` package
|
||||
- Debian Buster and Ubuntu Disco still distribute v1.1.10, so need the manual rule
|
||||
- FreeBSD has support in [u2f-devd](https://github.com/solokeys/solo/issues/144#issuecomment-500216020)
|
||||
|
||||
Additionally, run the following command after you create this file (it is not necessary to do this again in the future):
|
||||
There is hope that `udev` itself will adopt the Fedora approach (which is to check for HID usage page `F1D0`, and avoids manually whitelisting each U2F/FIDO2 key): <https://github.com/systemd/systemd/issues/11996>.
|
||||
|
||||
```
|
||||
sudo udevadm control --reload-rules && sudo udevadm trigger
|
||||
```
|
||||
Further progress is tracked in: <https://github.com/solokeys/solo/issues/144>.
|
||||
|
||||
A simple way to setup both the udev rule and the udevadm reload is:
|
||||
If you still need to setup a rule, a simple way to do it is:
|
||||
|
||||
```
|
||||
git clone git@github.com:solokeys/solo.git
|
||||
@ -22,9 +23,11 @@ cd solo/udev
|
||||
make setup
|
||||
```
|
||||
|
||||
We are working on getting user access to Solo keys enabled automatically in common Linux distributions: <https://github.com/solokeys/solo/issues/144>.
|
||||
|
||||
|
||||
Or, manually, create a file like [`70-solokeys-access.rules`](https://github.com/solokeys/solo/blob/master/udev/70-solokeys-access.rules) in your `/etc/udev/rules.d` directory.
|
||||
Additionally, run the following command after you create this file (it is not necessary to do this again in the future):
|
||||
```
|
||||
sudo udevadm control --reload-rules && sudo udevadm trigger
|
||||
```
|
||||
|
||||
# How do udev rules work and why are they needed
|
||||
|
||||
|
@ -13,7 +13,7 @@ int apdu_decode(uint8_t *data, size_t len, APDU_STRUCT *apdu)
|
||||
{
|
||||
EXT_APDU_HEADER *hapdu = (EXT_APDU_HEADER *)data;
|
||||
|
||||
apdu->cla = hapdu->cla;
|
||||
apdu->cla = hapdu->cla & 0xef; // mask chaining bit if any
|
||||
apdu->ins = hapdu->ins;
|
||||
apdu->p1 = hapdu->p1;
|
||||
apdu->p2 = hapdu->p2;
|
||||
|
@ -42,14 +42,20 @@ extern int apdu_decode(uint8_t *data, size_t len, APDU_STRUCT *apdu);
|
||||
#define APDU_FIDO_U2F_AUTHENTICATE 0x02
|
||||
#define APDU_FIDO_U2F_VERSION 0x03
|
||||
#define APDU_FIDO_NFCCTAP_MSG 0x10
|
||||
#define APDU_FIDO_U2F_VENDOR_FIRST 0xc0 // First vendor defined command
|
||||
#define APDU_FIDO_U2F_VENDOR_LAST 0xff // Last vendor defined command
|
||||
#define APDU_SOLO_RESET 0xee
|
||||
|
||||
#define APDU_INS_SELECT 0xA4
|
||||
#define APDU_INS_READ_BINARY 0xB0
|
||||
#define APDU_GET_RESPONSE 0xC0
|
||||
|
||||
#define SW_SUCCESS 0x9000
|
||||
#define SW_GET_RESPONSE 0x6100 // Command successfully executed; 'XX' bytes of data are available and can be requested using GET RESPONSE.
|
||||
#define SW_WRONG_LENGTH 0x6700
|
||||
#define SW_COND_USE_NOT_SATISFIED 0x6985
|
||||
#define SW_FILE_NOT_FOUND 0x6a82
|
||||
#define SW_INCORRECT_P1P2 0x6a86
|
||||
#define SW_INS_INVALID 0x6d00 // Instruction code not supported or invalid
|
||||
#define SW_CLA_INVALID 0x6e00
|
||||
#define SW_INTERNAL_EXCEPTION 0x6f00
|
||||
|
@ -262,6 +262,11 @@ void crypto_ecc256_derive_public_key(uint8_t * data, int len, uint8_t * x, uint8
|
||||
memmove(y,pubkey+32,32);
|
||||
}
|
||||
|
||||
void crypto_ecc256_compute_public_key(uint8_t * privkey, uint8_t * pubkey)
|
||||
{
|
||||
uECC_compute_public_key(privkey, pubkey, _es256_curve);
|
||||
}
|
||||
|
||||
void crypto_load_external_key(uint8_t * key, int len)
|
||||
{
|
||||
_signing_key = key;
|
||||
|
@ -26,6 +26,7 @@ void crypto_sha512_final(uint8_t * hash);
|
||||
|
||||
void crypto_ecc256_init();
|
||||
void crypto_ecc256_derive_public_key(uint8_t * data, int len, uint8_t * x, uint8_t * y);
|
||||
void crypto_ecc256_compute_public_key(uint8_t * privkey, uint8_t * pubkey);
|
||||
|
||||
void crypto_ecc256_load_key(uint8_t * data, int len, uint8_t * data2, int len2);
|
||||
void crypto_ecc256_load_attestation_key();
|
||||
|
59
fido2/ctap.c
59
fido2/ctap.c
@ -256,7 +256,9 @@ static int ctap_generate_cose_key(CborEncoder * cose_key, uint8_t * hmac_input,
|
||||
switch(algtype)
|
||||
{
|
||||
case COSE_ALG_ES256:
|
||||
if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_FAST);
|
||||
crypto_ecc256_derive_public_key(hmac_input, len, x, y);
|
||||
if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_IDLE);
|
||||
break;
|
||||
default:
|
||||
printf2(TAG_ERR,"Error, COSE alg %d not supported\n", algtype);
|
||||
@ -435,7 +437,19 @@ static unsigned int get_credential_id_size(CTAP_credentialDescriptor * cred)
|
||||
static int ctap2_user_presence_test()
|
||||
{
|
||||
device_set_status(CTAPHID_STATUS_UPNEEDED);
|
||||
return ctap_user_presence_test(CTAP2_UP_DELAY_MS);
|
||||
int ret = ctap_user_presence_test(CTAP2_UP_DELAY_MS);
|
||||
if ( ret > 0 )
|
||||
{
|
||||
return CTAP1_ERR_SUCCESS;
|
||||
}
|
||||
else if (ret < 0)
|
||||
{
|
||||
return CTAP2_ERR_KEEPALIVE_CANCEL;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CTAP2_ERR_ACTION_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * auth_data_buf, uint32_t * len, CTAP_credInfo * credInfo)
|
||||
@ -468,19 +482,11 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
|
||||
int but;
|
||||
|
||||
but = ctap2_user_presence_test(CTAP2_UP_DELAY_MS);
|
||||
|
||||
if (!but)
|
||||
{
|
||||
return CTAP2_ERR_OPERATION_DENIED;
|
||||
}
|
||||
else if (but < 0) // Cancel
|
||||
{
|
||||
return CTAP2_ERR_KEEPALIVE_CANCEL;
|
||||
}
|
||||
check_retr(but);
|
||||
|
||||
device_set_status(CTAPHID_STATUS_PROCESSING);
|
||||
|
||||
authData->head.flags = (but << 0);
|
||||
authData->head.flags = (1 << 0); // User presence
|
||||
authData->head.flags |= (ctap_is_pin_set() << 2);
|
||||
|
||||
|
||||
@ -705,10 +711,7 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
|
||||
}
|
||||
if (MC.pinAuthEmpty)
|
||||
{
|
||||
if (!ctap2_user_presence_test(CTAP2_UP_DELAY_MS))
|
||||
{
|
||||
return CTAP2_ERR_OPERATION_DENIED;
|
||||
}
|
||||
check_retr( ctap2_user_presence_test(CTAP2_UP_DELAY_MS) );
|
||||
return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_AUTH_INVALID : CTAP2_ERR_PIN_NOT_SET;
|
||||
}
|
||||
if ((MC.paramsParsed & MC_requiredMask) != MC_requiredMask)
|
||||
@ -1141,10 +1144,7 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
|
||||
|
||||
if (GA.pinAuthEmpty)
|
||||
{
|
||||
if (!ctap2_user_presence_test(CTAP2_UP_DELAY_MS))
|
||||
{
|
||||
return CTAP2_ERR_OPERATION_DENIED;
|
||||
}
|
||||
check_retr( ctap2_user_presence_test(CTAP2_UP_DELAY_MS) );
|
||||
return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_AUTH_INVALID : CTAP2_ERR_PIN_NOT_SET;
|
||||
}
|
||||
if (GA.pinAuthPresent)
|
||||
@ -1479,6 +1479,11 @@ uint8_t ctap_client_pin(CborEncoder * encoder, uint8_t * request, int length)
|
||||
|
||||
ret = cbor_encode_int(&map, RESP_keyAgreement);
|
||||
check_ret(ret);
|
||||
|
||||
if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_FAST);
|
||||
crypto_ecc256_compute_public_key(KEY_AGREEMENT_PRIV, KEY_AGREEMENT_PUB);
|
||||
if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_IDLE);
|
||||
|
||||
ret = ctap_add_cose_key(&map, KEY_AGREEMENT_PUB, KEY_AGREEMENT_PUB+32, PUB_KEY_CRED_PUB_KEY, COSE_ALG_ECDH_ES_HKDF_256);
|
||||
check_retr(ret);
|
||||
|
||||
@ -1649,14 +1654,11 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
|
||||
break;
|
||||
case CTAP_RESET:
|
||||
printf1(TAG_CTAP,"CTAP_RESET\n");
|
||||
if (ctap2_user_presence_test(CTAP2_UP_DELAY_MS))
|
||||
status = ctap2_user_presence_test(CTAP2_UP_DELAY_MS);
|
||||
if (status == CTAP1_ERR_SUCCESS)
|
||||
{
|
||||
ctap_reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
status = CTAP2_ERR_OPERATION_DENIED;
|
||||
}
|
||||
break;
|
||||
case GET_NEXT_ASSERTION:
|
||||
printf1(TAG_CTAP,"CTAP_NEXT_ASSERTION\n");
|
||||
@ -1678,7 +1680,7 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
|
||||
break;
|
||||
default:
|
||||
status = CTAP1_ERR_INVALID_COMMAND;
|
||||
printf2(TAG_ERR,"error, invalid cmd\n");
|
||||
printf2(TAG_ERR,"error, invalid cmd: 0x%02x\n", cmd);
|
||||
}
|
||||
|
||||
done:
|
||||
@ -1767,10 +1769,7 @@ void ctap_init()
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (device_is_nfc() != NFC_IS_ACTIVE)
|
||||
{
|
||||
ctap_reset_key_agreement();
|
||||
}
|
||||
ctap_reset_key_agreement();
|
||||
|
||||
#ifdef BRIDGE_TO_WALLET
|
||||
wallet_init();
|
||||
@ -1969,7 +1968,7 @@ int8_t ctap_load_key(uint8_t index, uint8_t * key)
|
||||
|
||||
static void ctap_reset_key_agreement()
|
||||
{
|
||||
crypto_ecc256_make_key_pair(KEY_AGREEMENT_PUB, KEY_AGREEMENT_PRIV);
|
||||
ctap_generate_rng(KEY_AGREEMENT_PRIV, sizeof(KEY_AGREEMENT_PRIV));
|
||||
}
|
||||
|
||||
void ctap_reset()
|
||||
|
@ -131,7 +131,7 @@
|
||||
#define PIN_LOCKOUT_ATTEMPTS 8 // Number of attempts total
|
||||
#define PIN_BOOT_ATTEMPTS 3 // number of attempts per boot
|
||||
|
||||
#define CTAP2_UP_DELAY_MS 5000
|
||||
#define CTAP2_UP_DELAY_MS 29000
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
@ -49,6 +49,7 @@
|
||||
#define CTAP2_ERR_PIN_POLICY_VIOLATION 0x37
|
||||
#define CTAP2_ERR_PIN_TOKEN_EXPIRED 0x38
|
||||
#define CTAP2_ERR_REQUEST_TOO_LARGE 0x39
|
||||
#define CTAP2_ERR_ACTION_TIMEOUT 0x3A
|
||||
#define CTAP1_ERR_OTHER 0x7F
|
||||
#define CTAP2_ERR_SPEC_LAST 0xDF
|
||||
#define CTAP2_ERR_EXTENSION_FIRST 0xE0
|
||||
|
@ -30,6 +30,7 @@ void main_loop_delay();
|
||||
|
||||
void heartbeat();
|
||||
|
||||
void device_reboot();
|
||||
|
||||
void authenticator_read_state(AuthenticatorState * );
|
||||
|
||||
|
@ -38,6 +38,7 @@ build firmware hacker solo
|
||||
build firmware hacker-debug-1 solo
|
||||
build firmware hacker-debug-2 solo
|
||||
build firmware secure solo
|
||||
build firmware secure-non-solokeys solo
|
||||
|
||||
pip install -U pip
|
||||
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
|
||||
bundle="bundle-hacker-debug-2-${version}"
|
||||
/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,10 @@ nav:
|
||||
- FIDO2 Implementation: solo/fido2-impl.md
|
||||
- Metadata Statements: solo/metadata-statements.md
|
||||
- Build instructions: solo/building.md
|
||||
- Programming instructions: solo/programming.md
|
||||
- Bootloader mode: solo/bootloader-mode.md
|
||||
- Customization: solo/customization.md
|
||||
- Solo Extras: solo/solo-extras.md
|
||||
- Running on Nucleo32 board: solo/nucleo32-board.md
|
||||
- Signed update process: solo/signed-updates.md
|
||||
- Code documentation: solo/code-overview.md
|
||||
|
11
pc/device.c
11
pc/device.c
@ -43,7 +43,11 @@ void device_set_status(uint32_t status)
|
||||
__device_status = status;
|
||||
}
|
||||
|
||||
|
||||
void device_reboot()
|
||||
{
|
||||
printf1(TAG_RED, "REBOOT command recieved!\r\n");
|
||||
exit(100);
|
||||
}
|
||||
|
||||
int udp_server()
|
||||
{
|
||||
@ -628,3 +632,8 @@ int device_is_nfc()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void device_set_clock_rate(DEVICE_CLOCK_RATE param)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -21,6 +21,9 @@ firmware-hacker-debug-1:
|
||||
firmware-hacker-debug-2:
|
||||
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=2 EXTRA_DEFINES='-DSOLO_HACKER -DFLASH_ROP=0'
|
||||
|
||||
firmware-secure-non-solokeys:
|
||||
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=0 EXTRA_DEFINES='-DFLASH_ROP=2'
|
||||
|
||||
firmware-secure:
|
||||
$(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
|
||||
$(VENV) $(merge_hex) solo.hex bootloader.hex all.hex
|
||||
rm -f solo.hex bootloader.hex # don't program solo.hex ...
|
||||
|
||||
build-release: cbor clean2 boot-sig-checking clean all
|
||||
$(VENV) $(merge_hex) solo.hex bootloader.hex all.hex
|
||||
|
@ -26,151 +26,173 @@ static uint8_t *USBD_Composite_GetOtherSpeedCfgDesc (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
|
||||
#define COMPOSITE_CDC_HID_DESCRIPTOR_SIZE (90)
|
||||
#define COMPOSITE_CDC_HID_DESCRIPTOR_SIZE (90 + 8+9 + 4)
|
||||
#else
|
||||
#define COMPOSITE_CDC_HID_DESCRIPTOR_SIZE (41)
|
||||
#endif
|
||||
|
||||
#define HID_INTF_NUM 0
|
||||
#define CDC_INTF_NUM 1
|
||||
#define HID_INTF_NUM 0
|
||||
#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 =
|
||||
{
|
||||
/*Configuration Descriptor*/
|
||||
0x09, /* bLength: Configuration Descriptor size */
|
||||
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
|
||||
COMPOSITE_CDC_HID_DESCRIPTOR_SIZE, /* wTotalLength:no of returned bytes */
|
||||
0x00,
|
||||
NUM_INTERFACES, /* bNumInterfaces: 1 interface */
|
||||
0x01, /* bConfigurationValue: Configuration value */
|
||||
0x00, /* iConfiguration: Index of string descriptor describing the configuration */
|
||||
0x80, /* bmAttributes: self powered */
|
||||
0x32, /* MaxPower 100 mA */
|
||||
{
|
||||
/*Configuration Descriptor*/
|
||||
0x09, /* bLength: Configuration Descriptor size */
|
||||
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
|
||||
COMPOSITE_CDC_HID_DESCRIPTOR_SIZE, /* wTotalLength:no of returned bytes */
|
||||
0x00,
|
||||
NUM_INTERFACES, /* bNumInterfaces */
|
||||
0x01, /* bConfigurationValue: Configuration value */
|
||||
0x00, /* iConfiguration: Index of string descriptor describing the configuration */
|
||||
0x80, /* bmAttributes: self powered */
|
||||
0x32, /* MaxPower 100 mA */
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
/* */
|
||||
/* HID */
|
||||
/* */
|
||||
/* */
|
||||
/* HID */
|
||||
/* */
|
||||
|
||||
/************** Descriptor of Joystick Mouse interface ****************/
|
||||
0x09, /*bLength: Interface Descriptor size*/
|
||||
USB_DESC_TYPE_INTERFACE,/*bDescriptorType: Interface descriptor type*/
|
||||
HID_INTF_NUM, /*bInterfaceNumber: Number of Interface*/
|
||||
0x00, /*bAlternateSetting: Alternate setting*/
|
||||
0x02, /*bNumEndpoints*/
|
||||
0x03, /*bInterfaceClass: HID*/
|
||||
0x00, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
|
||||
0x00, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
|
||||
2, /*iInterface: Index of string descriptor*/
|
||||
/******************** Descriptor of Joystick Mouse HID ********************/
|
||||
0x09, /*bLength: HID Descriptor size*/
|
||||
HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/
|
||||
0x11, /*bcdHID: HID Class Spec release number*/
|
||||
0x01,
|
||||
0x00, /*bCountryCode: Hardware target country*/
|
||||
0x01, /*bNumDescriptors: Number of HID class descriptors to follow*/
|
||||
0x22, /*bDescriptorType*/
|
||||
HID_FIDO_REPORT_DESC_SIZE,/*wItemLength: Total length of Report descriptor*/
|
||||
0,
|
||||
/******************** Descriptor of Mouse endpoint ********************/
|
||||
0x07, /*bLength: Endpoint Descriptor size*/
|
||||
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
|
||||
HID_EPIN_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
|
||||
0x03, /*bmAttributes: Interrupt endpoint*/
|
||||
HID_EPIN_SIZE, /*wMaxPacketSize: 4 Byte max */
|
||||
0x00,
|
||||
HID_BINTERVAL, /*bInterval: Polling Interval */
|
||||
/************** Descriptor of Joystick Mouse interface ****************/
|
||||
0x09, /*bLength: Interface Descriptor size*/
|
||||
USB_DESC_TYPE_INTERFACE, /*bDescriptorType: Interface descriptor type*/
|
||||
HID_INTF_NUM, /*bInterfaceNumber: Number of Interface*/
|
||||
0x00, /*bAlternateSetting: Alternate setting*/
|
||||
0x02, /*bNumEndpoints*/
|
||||
0x03, /*bInterfaceClass: HID*/
|
||||
0x00, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
|
||||
0x00, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
|
||||
2, /*iInterface: Index of string descriptor*/
|
||||
/******************** Descriptor of Joystick Mouse HID ********************/
|
||||
0x09, /*bLength: HID Descriptor size*/
|
||||
HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/
|
||||
0x11, /*bcdHID: HID Class Spec release number*/
|
||||
0x01,
|
||||
0x00, /*bCountryCode: Hardware target country*/
|
||||
0x01, /*bNumDescriptors: Number of HID class descriptors to follow*/
|
||||
0x22, /*bDescriptorType*/
|
||||
HID_FIDO_REPORT_DESC_SIZE, /*wItemLength: Total length of Report descriptor*/
|
||||
0,
|
||||
/******************** Descriptor of Mouse endpoint ********************/
|
||||
0x07, /*bLength: Endpoint Descriptor size*/
|
||||
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
|
||||
HID_EPIN_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
|
||||
0x03, /*bmAttributes: Interrupt endpoint*/
|
||||
HID_EPIN_SIZE, /*wMaxPacketSize: 4 Byte max */
|
||||
0x00,
|
||||
HID_BINTERVAL, /*bInterval: Polling Interval */
|
||||
|
||||
0x07, /*bLength: Endpoint Descriptor size*/
|
||||
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
|
||||
HID_EPOUT_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
|
||||
0x03, /*bmAttributes: Interrupt endpoint*/
|
||||
HID_EPOUT_SIZE, /*wMaxPacketSize: 4 Byte max */
|
||||
0x00,
|
||||
HID_BINTERVAL, /*bInterval: Polling Interval */
|
||||
0x07, /*bLength: Endpoint Descriptor size*/
|
||||
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
|
||||
HID_EPOUT_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
|
||||
0x03, /*bmAttributes: Interrupt endpoint*/
|
||||
HID_EPOUT_SIZE, /*wMaxPacketSize: 4 Byte max */
|
||||
0x00,
|
||||
HID_BINTERVAL, /*bInterval: Polling Interval */
|
||||
|
||||
#if 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: */
|
||||
|
||||
/* */
|
||||
/* CDC */
|
||||
/* */
|
||||
/*Header Functional Descriptor*/
|
||||
0x05, /* bLength: Endpoint Descriptor size */
|
||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||
0x00, /* bDescriptorSubtype: Header Func Desc */
|
||||
0x10, /* bcdCDC: spec release number */
|
||||
0x01,
|
||||
|
||||
/*Call Management Functional Descriptor*/
|
||||
0x05, /* bFunctionLength */
|
||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||
0x01, /* bDescriptorSubtype: Call Management Func Desc */
|
||||
0x00, /* bmCapabilities: D0+D1 */
|
||||
/*!*/ CDC_SLAVE_INTF_NUM, /* bDataInterface: 0 */
|
||||
|
||||
/*Interface Descriptor */
|
||||
0x09, /* bLength: Interface Descriptor size */
|
||||
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
|
||||
/* Interface descriptor type */
|
||||
/*!*/ CDC_INTF_NUM, /* bInterfaceNumber: Number of Interface */
|
||||
0x00, /* bAlternateSetting: Alternate setting */
|
||||
0x03, /* bNumEndpoints: 3 endpoints used */
|
||||
0x02, /* bInterfaceClass: Communication Interface Class */
|
||||
0x02, /* bInterfaceSubClass: Abstract Control Model */
|
||||
0x00, /* bInterfaceProtocol: Common AT commands */
|
||||
0x00, /* iInterface: */
|
||||
/*ACM Functional Descriptor*/
|
||||
0x04, /* bFunctionLength */
|
||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||
0x02, /* bDescriptorSubtype: Abstract Control Management desc */
|
||||
0x02, /* bmCapabilities */
|
||||
|
||||
/*Header Functional Descriptor*/
|
||||
0x05, /* bLength: Endpoint Descriptor size */
|
||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||
0x00, /* bDescriptorSubtype: Header Func Desc */
|
||||
0x10, /* bcdCDC: spec release number */
|
||||
0x01,
|
||||
/*Union Functional Descriptor*/
|
||||
0x05, /* bFunctionLength */
|
||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||
0x06, /* bDescriptorSubtype: Union func desc */
|
||||
/*!*/ CDC_MASTER_INTF_NUM, /* bMasterInterface: Communication class interface */
|
||||
/*!*/ CDC_SLAVE_INTF_NUM, /* bSlaveInterface0: Data Class Interface */
|
||||
|
||||
/*Call Management Functional Descriptor*/
|
||||
0x05, /* bFunctionLength */
|
||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||
0x01, /* bDescriptorSubtype: Call Management Func Desc */
|
||||
0x00, /* bmCapabilities: D0+D1 */
|
||||
/*!*/ CDC_INTF_NUM, /* bDataInterface: 0 */
|
||||
/* Control Endpoint Descriptor*/
|
||||
0x07, /* bLength: Endpoint Descriptor size */
|
||||
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
|
||||
CDC_CMD_EP, /* bEndpointAddress */
|
||||
0x03, /* bmAttributes: Interrupt */
|
||||
LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */
|
||||
HIBYTE(CDC_CMD_PACKET_SIZE),
|
||||
0x10, /* bInterval: */
|
||||
|
||||
/*ACM Functional Descriptor*/
|
||||
0x04, /* bFunctionLength */
|
||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||
0x02, /* bDescriptorSubtype: Abstract Control Management desc */
|
||||
0x02, /* bmCapabilities */
|
||||
/* Interface descriptor */
|
||||
0x09, /* bLength */
|
||||
USB_DESC_TYPE_INTERFACE, /* bDescriptorType */
|
||||
CDC_SLAVE_INTF_NUM, /* bInterfaceNumber */
|
||||
0x00, /* bAlternateSetting */
|
||||
0x02, /* bNumEndpoints */
|
||||
0x0A, /* bInterfaceClass: Communication class data */
|
||||
0x00, /* bInterfaceSubClass */
|
||||
0x00, /* bInterfaceProtocol */
|
||||
0x00,
|
||||
|
||||
/*Union Functional Descriptor*/
|
||||
0x05, /* bFunctionLength */
|
||||
0x24, /* bDescriptorType: CS_INTERFACE */
|
||||
0x06, /* bDescriptorSubtype: Union func desc */
|
||||
/*!*/ CDC_INTF_NUM, /* bMasterInterface: Communication class interface */
|
||||
/*!*/ CDC_INTF_NUM, /* bSlaveInterface0: Data Class Interface */
|
||||
/*Endpoint OUT Descriptor*/
|
||||
0x07, /* bLength: Endpoint Descriptor size */
|
||||
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
|
||||
CDC_OUT_EP, /* bEndpointAddress */
|
||||
0x02, /* bmAttributes: Bulk */
|
||||
LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
|
||||
HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
|
||||
0x00, /* bInterval: ignore for Bulk transfer */
|
||||
|
||||
/*Endpoint 2 Descriptor*/
|
||||
0x07, /* bLength: Endpoint Descriptor size */
|
||||
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
|
||||
CDC_CMD_EP, /* bEndpointAddress */
|
||||
0x03, /* bmAttributes: Interrupt */
|
||||
LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */
|
||||
HIBYTE(CDC_CMD_PACKET_SIZE),
|
||||
0x10, /* bInterval: */
|
||||
|
||||
/*Endpoint 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 */
|
||||
/*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
|
||||
};
|
||||
|
||||
|
||||
USBD_ClassTypeDef USBD_Composite =
|
||||
{
|
||||
USBD_Composite_Init,
|
||||
@ -195,14 +217,27 @@ int in_endpoint_to_class[MAX_ENDPOINTS];
|
||||
|
||||
int out_endpoint_to_class[MAX_ENDPOINTS];
|
||||
|
||||
void USBD_Composite_Set_Classes(USBD_ClassTypeDef *class0, USBD_ClassTypeDef *class1) {
|
||||
USBD_Classes[0] = class0;
|
||||
USBD_Classes[1] = class1;
|
||||
void USBD_Composite_Set_Classes(USBD_ClassTypeDef *hid_class, USBD_ClassTypeDef *cdc_class) {
|
||||
USBD_Classes[0] = hid_class;
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
int i;
|
||||
USBD_ClassTypeDef * device_class;
|
||||
device_class = getClass(req->wIndex);
|
||||
|
||||
switch (req->bmRequest & USB_REQ_TYPE_MASK) {
|
||||
case USB_REQ_TYPE_CLASS :
|
||||
if (req->wIndex < NUM_INTERFACES)
|
||||
return USBD_Classes[req->wIndex]->Setup(pdev, req);
|
||||
if (device_class != NULL)
|
||||
return device_class->Setup(pdev, req);
|
||||
else
|
||||
return USBD_FAIL;
|
||||
|
||||
@ -236,7 +274,7 @@ static uint8_t USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqType
|
||||
switch (req->bRequest) {
|
||||
|
||||
case USB_REQ_GET_DESCRIPTOR :
|
||||
for(i = 0; i < NUM_INTERFACES; i++) {
|
||||
for(i = 0; i < NUM_CLASSES; i++) {
|
||||
if (USBD_Classes[i]->Setup(pdev, req) != USBD_OK) {
|
||||
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_SET_INTERFACE :
|
||||
if (req->wIndex < NUM_INTERFACES)
|
||||
return USBD_Classes[req->wIndex]->Setup(pdev, req);
|
||||
if (device_class != NULL)
|
||||
return device_class->Setup(pdev, req);
|
||||
else
|
||||
return USBD_FAIL;
|
||||
}
|
||||
@ -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) {
|
||||
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(pdev) != USBD_OK) {
|
||||
return USBD_FAIL;
|
||||
|
@ -282,6 +282,11 @@ void crypto_ecc256_derive_public_key(uint8_t * data, int len, uint8_t * x, uint8
|
||||
memmove(x,pubkey,32);
|
||||
memmove(y,pubkey+32,32);
|
||||
}
|
||||
void crypto_ecc256_compute_public_key(uint8_t * privkey, uint8_t * pubkey)
|
||||
{
|
||||
uECC_compute_public_key(privkey, pubkey, _es256_curve);
|
||||
}
|
||||
|
||||
|
||||
void crypto_load_external_key(uint8_t * key, int len)
|
||||
{
|
||||
|
@ -39,6 +39,7 @@ void wait_for_usb_tether();
|
||||
|
||||
uint32_t __90_ms = 0;
|
||||
uint32_t __last_button_press_time = 0;
|
||||
uint32_t __last_button_bounce_time = 0;
|
||||
uint32_t __device_status = 0;
|
||||
uint32_t __last_update = 0;
|
||||
extern PCD_HandleTypeDef hpcd;
|
||||
@ -54,11 +55,43 @@ static int is_physical_button_pressed()
|
||||
|
||||
static int is_touch_button_pressed()
|
||||
{
|
||||
return tsc_read_button(0) || tsc_read_button(1);
|
||||
int is_pressed = (tsc_read_button(0) || tsc_read_button(1));
|
||||
#ifndef IS_BOOTLOADER
|
||||
if (is_pressed)
|
||||
{
|
||||
// delay for debounce, and longer than polling timer period.
|
||||
delay(95);
|
||||
return (tsc_read_button(0) || tsc_read_button(1));
|
||||
}
|
||||
#endif
|
||||
return is_pressed;
|
||||
}
|
||||
|
||||
int (*IS_BUTTON_PRESSED)() = is_physical_button_pressed;
|
||||
|
||||
static void edge_detect_touch_button()
|
||||
{
|
||||
static uint8_t last_touch = 0;
|
||||
uint8_t current_touch = 0;
|
||||
if (is_touch_button_pressed == IS_BUTTON_PRESSED)
|
||||
{
|
||||
current_touch = (tsc_read_button(0) || tsc_read_button(1));
|
||||
|
||||
// 1 sample per 25 ms
|
||||
if ((millis() - __last_button_bounce_time) > 25)
|
||||
{
|
||||
// Detect "touch / rising edge"
|
||||
if (!last_touch && current_touch)
|
||||
{
|
||||
__last_button_press_time = millis();
|
||||
}
|
||||
__last_button_bounce_time = millis();
|
||||
last_touch = current_touch;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void request_from_nfc(bool request_active) {
|
||||
_RequestComeFromNFC = request_active;
|
||||
}
|
||||
@ -76,6 +109,9 @@ void TIM6_DAC_IRQHandler()
|
||||
ctaphid_update_status(__device_status);
|
||||
}
|
||||
}
|
||||
|
||||
edge_detect_touch_button();
|
||||
|
||||
#ifndef IS_BOOTLOADER
|
||||
// NFC sending WTX if needs
|
||||
if (device_is_nfc() == NFC_IS_ACTIVE)
|
||||
@ -84,10 +120,20 @@ void TIM6_DAC_IRQHandler()
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Interrupt on rising edge of button (button released)
|
||||
void EXTI0_IRQHandler(void)
|
||||
{
|
||||
EXTI->PR1 = EXTI->PR1;
|
||||
__last_button_press_time = millis();
|
||||
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
|
||||
@ -116,7 +162,6 @@ void device_set_status(uint32_t status)
|
||||
|
||||
int device_is_button_pressed()
|
||||
{
|
||||
|
||||
return IS_BUTTON_PRESSED();
|
||||
}
|
||||
|
||||
@ -499,6 +544,41 @@ static int handle_packets()
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wait_for_button_activate(uint32_t wait)
|
||||
{
|
||||
int ret;
|
||||
uint32_t start = millis();
|
||||
do
|
||||
{
|
||||
if ((start + wait) < millis())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
delay(1);
|
||||
ret = handle_packets();
|
||||
if (ret)
|
||||
return ret;
|
||||
} while (!IS_BUTTON_PRESSED());
|
||||
return 0;
|
||||
}
|
||||
static int wait_for_button_release(uint32_t wait)
|
||||
{
|
||||
int ret;
|
||||
uint32_t start = millis();
|
||||
do
|
||||
{
|
||||
if ((start + wait) < millis())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
delay(1);
|
||||
ret = handle_packets();
|
||||
if (ret)
|
||||
return ret;
|
||||
} while (IS_BUTTON_PRESSED());
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ctap_user_presence_test(uint32_t up_delay)
|
||||
{
|
||||
int ret;
|
||||
@ -506,12 +586,7 @@ int ctap_user_presence_test(uint32_t up_delay)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
// "cache" button presses for 2 seconds.
|
||||
if (millis() - __last_button_press_time < 2000)
|
||||
{
|
||||
__last_button_press_time = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if SKIP_BUTTON_CHECK_WITH_DELAY
|
||||
int i=500;
|
||||
while(i--)
|
||||
@ -524,53 +599,41 @@ int ctap_user_presence_test(uint32_t up_delay)
|
||||
#elif SKIP_BUTTON_CHECK_FAST
|
||||
delay(2);
|
||||
ret = handle_packets();
|
||||
if (ret) return ret;
|
||||
if (ret)
|
||||
return ret;
|
||||
goto done;
|
||||
#endif
|
||||
uint32_t t1 = millis();
|
||||
|
||||
// If button was pressed within last [2] seconds, succeed.
|
||||
if (__last_button_press_time && (millis() - __last_button_press_time < 2000))
|
||||
{
|
||||
goto done;
|
||||
}
|
||||
|
||||
// Set LED status and wait.
|
||||
led_rgb(0xff3520);
|
||||
|
||||
if (IS_BUTTON_PRESSED == is_touch_button_pressed)
|
||||
{
|
||||
// Wait for user to release touch button if it's already pressed
|
||||
while (IS_BUTTON_PRESSED())
|
||||
{
|
||||
if (t1 + up_delay < millis())
|
||||
{
|
||||
printf1(TAG_GEN,"Button not pressed\n");
|
||||
goto fail;
|
||||
}
|
||||
ret = handle_packets();
|
||||
if (ret) return ret;
|
||||
}
|
||||
}
|
||||
|
||||
t1 = millis();
|
||||
|
||||
do
|
||||
{
|
||||
if (t1 + up_delay < millis())
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
delay(1);
|
||||
ret = handle_packets();
|
||||
// Block and wait for some time.
|
||||
ret = wait_for_button_activate(up_delay);
|
||||
if (ret) return ret;
|
||||
ret = wait_for_button_release(up_delay);
|
||||
if (ret) return ret;
|
||||
}
|
||||
while (! IS_BUTTON_PRESSED());
|
||||
|
||||
led_rgb(0x001040);
|
||||
|
||||
delay(50);
|
||||
// If button was pressed within last [2] seconds, succeed.
|
||||
if (__last_button_press_time && (millis() - __last_button_press_time < 2000))
|
||||
{
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
|
||||
|
||||
#if SKIP_BUTTON_CHECK_WITH_DELAY || SKIP_BUTTON_CHECK_FAST
|
||||
done:
|
||||
#endif
|
||||
return 1;
|
||||
ret = wait_for_button_release(up_delay);
|
||||
__last_button_press_time = 0;
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ctap_generate_rng(uint8_t * dst, size_t num)
|
||||
|
@ -14,6 +14,11 @@
|
||||
|
||||
#define IS_IRQ_ACTIVE() (1 == (LL_GPIO_ReadInputPort(SOLO_AMS_IRQ_PORT) & SOLO_AMS_IRQ_PIN))
|
||||
|
||||
// chain buffer for 61XX responses
|
||||
static uint8_t chain_buffer[2048] = {0};
|
||||
static size_t chain_buffer_len = 0;
|
||||
static bool chain_buffer_tx = false;
|
||||
|
||||
uint8_t p14443_block_offset(uint8_t pcb) {
|
||||
uint8_t offset = 1;
|
||||
// NAD following
|
||||
@ -92,19 +97,27 @@ int nfc_init()
|
||||
return NFC_IS_NA;
|
||||
}
|
||||
|
||||
static uint8_t gl_int0 = 0;
|
||||
void process_int0(uint8_t int0)
|
||||
{
|
||||
|
||||
gl_int0 = int0;
|
||||
}
|
||||
|
||||
bool ams_wait_for_tx(uint32_t timeout_ms)
|
||||
{
|
||||
if (gl_int0 & AMS_INT_TXE) {
|
||||
uint8_t int0 = ams_read_reg(AMS_REG_INT0);
|
||||
process_int0(int0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t tstart = millis();
|
||||
while (tstart + timeout_ms > millis())
|
||||
{
|
||||
uint8_t int0 = ams_read_reg(AMS_REG_INT0);
|
||||
if (int0) process_int0(int0);
|
||||
if (int0 & AMS_INT_TXE)
|
||||
process_int0(int0);
|
||||
if (int0 & AMS_INT_TXE || int0 & AMS_INT_RXE)
|
||||
return true;
|
||||
|
||||
delay(1);
|
||||
@ -121,8 +134,13 @@ bool ams_receive_with_timeout(uint32_t timeout_ms, uint8_t * data, int maxlen, i
|
||||
uint32_t tstart = millis();
|
||||
while (tstart + timeout_ms > millis())
|
||||
{
|
||||
uint8_t int0 = ams_read_reg(AMS_REG_INT0);
|
||||
if (int0) process_int0(int0);
|
||||
uint8_t int0 = 0;
|
||||
if (gl_int0 & AMS_INT_RXE) {
|
||||
int0 = gl_int0;
|
||||
} else {
|
||||
int0 = ams_read_reg(AMS_REG_INT0);
|
||||
process_int0(int0);
|
||||
}
|
||||
uint8_t buffer_status2 = ams_read_reg(AMS_REG_BUF2);
|
||||
|
||||
if (buffer_status2 && (int0 & AMS_INT_RXE))
|
||||
@ -183,7 +201,14 @@ bool nfc_write_response_ex(uint8_t req0, uint8_t * data, uint8_t len, uint16_t r
|
||||
|
||||
res[len + block_offset + 0] = resp >> 8;
|
||||
res[len + block_offset + 1] = resp & 0xff;
|
||||
|
||||
nfc_write_frame(res, block_offset + len + 2);
|
||||
|
||||
if (!ams_wait_for_tx(1))
|
||||
{
|
||||
printf1(TAG_NFC, "TX resp timeout. len: %d \r\n", len);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -193,10 +218,9 @@ bool nfc_write_response(uint8_t req0, uint16_t resp)
|
||||
return nfc_write_response_ex(req0, NULL, 0, resp);
|
||||
}
|
||||
|
||||
void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len)
|
||||
void nfc_write_response_chaining_plain(uint8_t req0, uint8_t * data, int len)
|
||||
{
|
||||
uint8_t res[32 + 2];
|
||||
int sendlen = 0;
|
||||
uint8_t iBlock = NFC_CMD_IBLOCK | (req0 & 0x0f);
|
||||
uint8_t block_offset = p14443_block_offset(req0);
|
||||
|
||||
@ -208,6 +232,7 @@ void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len)
|
||||
memcpy(&res[block_offset], data, len);
|
||||
nfc_write_frame(res, len + block_offset);
|
||||
} else {
|
||||
int sendlen = 0;
|
||||
do {
|
||||
// transmit I block
|
||||
int vlen = MIN(32 - block_offset, len - sendlen);
|
||||
@ -227,11 +252,11 @@ void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len)
|
||||
sendlen += vlen;
|
||||
|
||||
// wait for transmit (32 bytes aprox 2,5ms)
|
||||
// if (!ams_wait_for_tx(10))
|
||||
// {
|
||||
// printf1(TAG_NFC, "TX timeout. slen: %d \r\n", sendlen);
|
||||
// break;
|
||||
// }
|
||||
if (!ams_wait_for_tx(5))
|
||||
{
|
||||
printf1(TAG_NFC, "TX timeout. slen: %d \r\n", sendlen);
|
||||
break;
|
||||
}
|
||||
|
||||
// if needs to receive R block (not a last block)
|
||||
if (res[0] & 0x10)
|
||||
@ -264,6 +289,38 @@ void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len)
|
||||
}
|
||||
}
|
||||
|
||||
void append_get_response(uint8_t *data, size_t rest_len)
|
||||
{
|
||||
data[0] = 0x61;
|
||||
data[1] = 0x00;
|
||||
if (rest_len <= 0xff)
|
||||
data[1] = rest_len & 0xff;
|
||||
}
|
||||
|
||||
void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len, bool extapdu)
|
||||
{
|
||||
chain_buffer_len = 0;
|
||||
chain_buffer_tx = true;
|
||||
|
||||
// if we dont need to break data to parts that need to exchange via GET RESPONSE command (ISO 7816-4 7.1.3)
|
||||
if (len <= 255 || extapdu)
|
||||
{
|
||||
nfc_write_response_chaining_plain(req0, data, len);
|
||||
} else {
|
||||
size_t pcklen = MIN(253, len);
|
||||
chain_buffer_len = len - pcklen;
|
||||
printf1(TAG_NFC, "61XX chaining %d/%d.\r\n", pcklen, chain_buffer_len);
|
||||
|
||||
memmove(chain_buffer, data, pcklen);
|
||||
append_get_response(&chain_buffer[pcklen], chain_buffer_len);
|
||||
|
||||
nfc_write_response_chaining_plain(req0, chain_buffer, pcklen + 2); // 2 for 61XX
|
||||
|
||||
// put the rest data into chain buffer
|
||||
memmove(chain_buffer, &data[pcklen], chain_buffer_len);
|
||||
}
|
||||
}
|
||||
|
||||
// WTX on/off:
|
||||
// sends/receives WTX frame to reader every `WTX_time` time in ms
|
||||
// works via timer interrupts
|
||||
@ -316,7 +373,7 @@ bool WTX_off()
|
||||
void WTX_timer_exec()
|
||||
{
|
||||
// condition: (timer on) or (not expired[300ms])
|
||||
if ((WTX_timer <= 0) || WTX_timer + 300 > millis())
|
||||
if ((WTX_timer == 0) || WTX_timer + 300 > millis())
|
||||
return;
|
||||
|
||||
WTX_process(10);
|
||||
@ -327,12 +384,12 @@ void WTX_timer_exec()
|
||||
// read timeout must be 10 ms to call from interrupt
|
||||
bool WTX_process(int read_timeout)
|
||||
{
|
||||
uint8_t wtx[] = {0xf2, 0x01};
|
||||
if (WTX_fail)
|
||||
return false;
|
||||
|
||||
if (!WTX_sent)
|
||||
{
|
||||
uint8_t wtx[] = {0xf2, 0x01};
|
||||
nfc_write_frame(wtx, sizeof(wtx));
|
||||
WTX_sent = true;
|
||||
return true;
|
||||
@ -463,37 +520,70 @@ int select_applet(uint8_t * aid, int len)
|
||||
return APP_NOTHING;
|
||||
}
|
||||
|
||||
void nfc_process_iblock(uint8_t * buf, int len)
|
||||
void apdu_process(uint8_t buf0, uint8_t *apduptr, APDU_STRUCT *apdu)
|
||||
{
|
||||
int selected;
|
||||
CTAP_RESPONSE ctap_resp;
|
||||
int status;
|
||||
uint16_t reslen;
|
||||
|
||||
printf1(TAG_NFC,"Iblock: ");
|
||||
dump_hex1(TAG_NFC, buf, len);
|
||||
|
||||
uint8_t block_offset = p14443_block_offset(buf[0]);
|
||||
|
||||
APDU_STRUCT apdu;
|
||||
if (apdu_decode(buf + block_offset, len - block_offset, &apdu)) {
|
||||
printf1(TAG_NFC,"apdu decode error\r\n");
|
||||
nfc_write_response(buf[0], SW_COND_USE_NOT_SATISFIED);
|
||||
return;
|
||||
}
|
||||
printf1(TAG_NFC,"apdu ok. %scase=%02x cla=%02x ins=%02x p1=%02x p2=%02x lc=%d le=%d\r\n",
|
||||
apdu.extended_apdu ? "[e]":"", apdu.case_type, apdu.cla, apdu.ins, apdu.p1, apdu.p2, apdu.lc, apdu.le);
|
||||
|
||||
// check CLA
|
||||
if (apdu.cla != 0x00 && apdu.cla != 0x80) {
|
||||
printf1(TAG_NFC, "Unknown CLA %02x\r\n", apdu.cla);
|
||||
nfc_write_response(buf[0], SW_CLA_INVALID);
|
||||
if (apdu->cla != 0x00 && apdu->cla != 0x80) {
|
||||
printf1(TAG_NFC, "Unknown CLA %02x\r\n", apdu->cla);
|
||||
nfc_write_response(buf0, SW_CLA_INVALID);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO this needs to be organized better
|
||||
switch(apdu.ins)
|
||||
switch(apdu->ins)
|
||||
{
|
||||
// ISO 7816. 7.1 GET RESPONSE command
|
||||
case APDU_GET_RESPONSE:
|
||||
if (apdu->p1 != 0x00 || apdu->p2 != 0x00)
|
||||
{
|
||||
nfc_write_response(buf0, SW_INCORRECT_P1P2);
|
||||
printf1(TAG_NFC, "P1 or P2 error\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// too many bytes needs. 0x00 and 0x100 - any length
|
||||
if (apdu->le != 0 && apdu->le != 0x100 && apdu->le > chain_buffer_len)
|
||||
{
|
||||
uint16_t wlresp = SW_WRONG_LENGTH; // here can be 6700, 6C00, 6FXX. but the most standard way - 67XX or 6700
|
||||
if (chain_buffer_len <= 0xff)
|
||||
wlresp += chain_buffer_len & 0xff;
|
||||
nfc_write_response(buf0, wlresp);
|
||||
printf1(TAG_NFC, "buffer length less than requesteds\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// create temporary packet
|
||||
uint8_t pck[255] = {0};
|
||||
size_t pcklen = 253;
|
||||
if (apdu->le)
|
||||
pcklen = apdu->le;
|
||||
if (pcklen > chain_buffer_len)
|
||||
pcklen = chain_buffer_len;
|
||||
|
||||
printf1(TAG_NFC, "GET RESPONSE. pck len: %d buffer len: %d\r\n", pcklen, chain_buffer_len);
|
||||
|
||||
// create packet and add 61XX there if we have another portion(s) of data
|
||||
memmove(pck, chain_buffer, pcklen);
|
||||
size_t dlen = 0;
|
||||
if (chain_buffer_len - pcklen)
|
||||
{
|
||||
append_get_response(&pck[pcklen], chain_buffer_len - pcklen);
|
||||
dlen = 2;
|
||||
}
|
||||
|
||||
// send
|
||||
nfc_write_response_chaining_plain(buf0, pck, pcklen + dlen); // dlen for 61XX
|
||||
|
||||
// shift the buffer
|
||||
chain_buffer_len -= pcklen;
|
||||
memmove(chain_buffer, &chain_buffer[pcklen], chain_buffer_len);
|
||||
break;
|
||||
|
||||
case APDU_INS_SELECT:
|
||||
// if (apdu->p1 == 0 && apdu->p2 == 0x0c)
|
||||
// {
|
||||
@ -509,49 +599,49 @@ void nfc_process_iblock(uint8_t * buf, int len)
|
||||
// }
|
||||
// else
|
||||
{
|
||||
selected = select_applet(apdu.data, apdu.lc);
|
||||
selected = select_applet(apdu->data, apdu->lc);
|
||||
if (selected == APP_FIDO)
|
||||
{
|
||||
nfc_write_response_ex(buf[0], (uint8_t *)"U2F_V2", 6, SW_SUCCESS);
|
||||
nfc_write_response_ex(buf0, (uint8_t *)"U2F_V2", 6, SW_SUCCESS);
|
||||
printf1(TAG_NFC, "FIDO applet selected.\r\n");
|
||||
}
|
||||
else if (selected != APP_NOTHING)
|
||||
{
|
||||
nfc_write_response(buf[0], SW_SUCCESS);
|
||||
nfc_write_response(buf0, SW_SUCCESS);
|
||||
printf1(TAG_NFC, "SELECTED %d\r\n", selected);
|
||||
}
|
||||
else
|
||||
{
|
||||
nfc_write_response(buf[0], SW_FILE_NOT_FOUND);
|
||||
printf1(TAG_NFC, "NOT selected "); dump_hex1(TAG_NFC, apdu.data, apdu.lc);
|
||||
nfc_write_response(buf0, SW_FILE_NOT_FOUND);
|
||||
printf1(TAG_NFC, "NOT selected "); dump_hex1(TAG_NFC, apdu->data, apdu->lc);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case APDU_FIDO_U2F_VERSION:
|
||||
if (NFC_STATE.selected_applet != APP_FIDO) {
|
||||
nfc_write_response(buf[0], SW_INS_INVALID);
|
||||
nfc_write_response(buf0, SW_INS_INVALID);
|
||||
break;
|
||||
}
|
||||
|
||||
printf1(TAG_NFC, "U2F GetVersion command.\r\n");
|
||||
|
||||
u2f_request_nfc(&buf[block_offset], apdu.data, apdu.lc, &ctap_resp);
|
||||
nfc_write_response_chaining(buf[0], ctap_resp.data, ctap_resp.length);
|
||||
u2f_request_nfc(apduptr, apdu->data, apdu->lc, &ctap_resp);
|
||||
nfc_write_response_chaining(buf0, ctap_resp.data, ctap_resp.length, apdu->extended_apdu);
|
||||
break;
|
||||
|
||||
case APDU_FIDO_U2F_REGISTER:
|
||||
if (NFC_STATE.selected_applet != APP_FIDO) {
|
||||
nfc_write_response(buf[0], SW_INS_INVALID);
|
||||
nfc_write_response(buf0, SW_INS_INVALID);
|
||||
break;
|
||||
}
|
||||
|
||||
printf1(TAG_NFC, "U2F Register command.\r\n");
|
||||
|
||||
if (apdu.lc != 64)
|
||||
if (apdu->lc != 64)
|
||||
{
|
||||
printf1(TAG_NFC, "U2F Register request length error. len=%d.\r\n", apdu.lc);
|
||||
nfc_write_response(buf[0], SW_WRONG_LENGTH);
|
||||
printf1(TAG_NFC, "U2F Register request length error. len=%d.\r\n", apdu->lc);
|
||||
nfc_write_response(buf0, SW_WRONG_LENGTH);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -562,63 +652,63 @@ void nfc_process_iblock(uint8_t * buf, int len)
|
||||
// SystemClock_Config_LF32();
|
||||
// delay(300);
|
||||
if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_FAST);
|
||||
u2f_request_nfc(&buf[block_offset], apdu.data, apdu.lc, &ctap_resp);
|
||||
u2f_request_nfc(apduptr, apdu->data, apdu->lc, &ctap_resp);
|
||||
if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_IDLE);
|
||||
// if (!WTX_off())
|
||||
// return;
|
||||
|
||||
printf1(TAG_NFC, "U2F resp len: %d\r\n", ctap_resp.length);
|
||||
printf1(TAG_NFC,"U2F Register P2 took %d\r\n", timestamp());
|
||||
nfc_write_response_chaining(buf[0], ctap_resp.data, ctap_resp.length);
|
||||
nfc_write_response_chaining(buf0, ctap_resp.data, ctap_resp.length, apdu->extended_apdu);
|
||||
|
||||
printf1(TAG_NFC,"U2F Register answered %d (took %d)\r\n", millis(), timestamp());
|
||||
break;
|
||||
|
||||
case APDU_FIDO_U2F_AUTHENTICATE:
|
||||
if (NFC_STATE.selected_applet != APP_FIDO) {
|
||||
nfc_write_response(buf[0], SW_INS_INVALID);
|
||||
nfc_write_response(buf0, SW_INS_INVALID);
|
||||
break;
|
||||
}
|
||||
|
||||
printf1(TAG_NFC, "U2F Authenticate command.\r\n");
|
||||
|
||||
if (apdu.lc != 64 + 1 + apdu.data[64])
|
||||
if (apdu->lc != 64 + 1 + apdu->data[64])
|
||||
{
|
||||
delay(5);
|
||||
printf1(TAG_NFC, "U2F Authenticate request length error. len=%d keyhlen=%d.\r\n", apdu.lc, apdu.data[64]);
|
||||
nfc_write_response(buf[0], SW_WRONG_LENGTH);
|
||||
printf1(TAG_NFC, "U2F Authenticate request length error. len=%d keyhlen=%d.\r\n", apdu->lc, apdu->data[64]);
|
||||
nfc_write_response(buf0, SW_WRONG_LENGTH);
|
||||
return;
|
||||
}
|
||||
|
||||
timestamp();
|
||||
// WTX_on(WTX_TIME_DEFAULT);
|
||||
u2f_request_nfc(&buf[block_offset], apdu.data, apdu.lc, &ctap_resp);
|
||||
u2f_request_nfc(apduptr, apdu->data, apdu->lc, &ctap_resp);
|
||||
// if (!WTX_off())
|
||||
// return;
|
||||
|
||||
printf1(TAG_NFC, "U2F resp len: %d\r\n", ctap_resp.length);
|
||||
printf1(TAG_NFC,"U2F Authenticate processing %d (took %d)\r\n", millis(), timestamp());
|
||||
nfc_write_response_chaining(buf[0], ctap_resp.data, ctap_resp.length);
|
||||
nfc_write_response_chaining(buf0, ctap_resp.data, ctap_resp.length, apdu->extended_apdu);
|
||||
printf1(TAG_NFC,"U2F Authenticate answered %d (took %d)\r\n", millis(), timestamp);
|
||||
break;
|
||||
|
||||
case APDU_FIDO_NFCCTAP_MSG:
|
||||
if (NFC_STATE.selected_applet != APP_FIDO) {
|
||||
nfc_write_response(buf[0], SW_INS_INVALID);
|
||||
nfc_write_response(buf0, SW_INS_INVALID);
|
||||
return;
|
||||
}
|
||||
|
||||
printf1(TAG_NFC, "FIDO2 CTAP message. %d\r\n", timestamp());
|
||||
|
||||
WTX_on(WTX_TIME_DEFAULT);
|
||||
// WTX_on(WTX_TIME_DEFAULT);
|
||||
request_from_nfc(true);
|
||||
ctap_response_init(&ctap_resp);
|
||||
status = ctap_request(apdu.data, apdu.lc, &ctap_resp);
|
||||
status = ctap_request(apdu->data, apdu->lc, &ctap_resp);
|
||||
request_from_nfc(false);
|
||||
if (!WTX_off())
|
||||
return;
|
||||
// if (!WTX_off())
|
||||
// return;
|
||||
|
||||
printf1(TAG_NFC, "CTAP resp: 0x%02<EFBFBD> len: %d\r\n", status, ctap_resp.length);
|
||||
printf1(TAG_NFC, "CTAP resp: 0x%02x len: %d\r\n", status, ctap_resp.length);
|
||||
|
||||
if (status == CTAP1_ERR_SUCCESS)
|
||||
{
|
||||
@ -632,44 +722,106 @@ void nfc_process_iblock(uint8_t * buf, int len)
|
||||
ctap_resp.data[ctap_resp.length - 1] = SW_SUCCESS & 0xff;
|
||||
|
||||
printf1(TAG_NFC,"CTAP processing %d (took %d)\r\n", millis(), timestamp());
|
||||
nfc_write_response_chaining(buf[0], ctap_resp.data, ctap_resp.length);
|
||||
nfc_write_response_chaining(buf0, ctap_resp.data, ctap_resp.length, apdu->extended_apdu);
|
||||
printf1(TAG_NFC,"CTAP answered %d (took %d)\r\n", millis(), timestamp());
|
||||
break;
|
||||
|
||||
case APDU_INS_READ_BINARY:
|
||||
// response length
|
||||
reslen = apdu.le & 0xffff;
|
||||
reslen = apdu->le & 0xffff;
|
||||
switch(NFC_STATE.selected_applet)
|
||||
{
|
||||
case APP_CAPABILITY_CONTAINER:
|
||||
printf1(TAG_NFC,"APP_CAPABILITY_CONTAINER\r\n");
|
||||
if (reslen == 0 || reslen > sizeof(NFC_CC))
|
||||
reslen = sizeof(NFC_CC);
|
||||
nfc_write_response_ex(buf[0], (uint8_t *)&NFC_CC, reslen, SW_SUCCESS);
|
||||
nfc_write_response_ex(buf0, (uint8_t *)&NFC_CC, reslen, SW_SUCCESS);
|
||||
ams_wait_for_tx(10);
|
||||
break;
|
||||
case APP_NDEF_TAG:
|
||||
printf1(TAG_NFC,"APP_NDEF_TAG\r\n");
|
||||
if (reslen == 0 || reslen > sizeof(NDEF_SAMPLE) - 1)
|
||||
reslen = sizeof(NDEF_SAMPLE) - 1;
|
||||
nfc_write_response_ex(buf[0], NDEF_SAMPLE, reslen, SW_SUCCESS);
|
||||
nfc_write_response_ex(buf0, NDEF_SAMPLE, reslen, SW_SUCCESS);
|
||||
ams_wait_for_tx(10);
|
||||
break;
|
||||
default:
|
||||
nfc_write_response(buf[0], SW_FILE_NOT_FOUND);
|
||||
nfc_write_response(buf0, SW_FILE_NOT_FOUND);
|
||||
printf1(TAG_ERR, "No binary applet selected!\r\n");
|
||||
return;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case APDU_SOLO_RESET:
|
||||
if (apdu->lc == 4 && !memcmp(apdu->data, "\x12\x56\xab\xf0", 4)) {
|
||||
printf1(TAG_NFC, "Reset...\r\n");
|
||||
nfc_write_response(buf0, SW_SUCCESS);
|
||||
delay(20);
|
||||
device_reboot();
|
||||
while(1);
|
||||
} else {
|
||||
printf1(TAG_NFC, "Reset FAIL\r\n");
|
||||
nfc_write_response(buf0, SW_INS_INVALID);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
printf1(TAG_NFC, "Unknown INS %02x\r\n", apdu.ins);
|
||||
nfc_write_response(buf[0], SW_INS_INVALID);
|
||||
printf1(TAG_NFC, "Unknown INS %02x\r\n", apdu->ins);
|
||||
nfc_write_response(buf0, SW_INS_INVALID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_process_iblock(uint8_t * buf, int len)
|
||||
{
|
||||
uint8_t block_offset = p14443_block_offset(buf[0]);
|
||||
|
||||
// clear tx chain buffer if we have some other command than GET RESPONSE
|
||||
if (chain_buffer_tx && buf[block_offset + 1] != APDU_GET_RESPONSE) {
|
||||
chain_buffer_len = 0;
|
||||
chain_buffer_tx = false;
|
||||
}
|
||||
|
||||
APDU_STRUCT apdu;
|
||||
if (apdu_decode(buf + block_offset, len - block_offset, &apdu)) {
|
||||
printf1(TAG_NFC,"apdu decode error\r\n");
|
||||
nfc_write_response(buf[0], SW_COND_USE_NOT_SATISFIED);
|
||||
return;
|
||||
}
|
||||
printf1(TAG_NFC,"apdu ok. %scase=%02x cla=%02x ins=%02x p1=%02x p2=%02x lc=%d le=%d\r\n",
|
||||
apdu.extended_apdu ? "[e]":"", apdu.case_type, apdu.cla, apdu.ins, apdu.p1, apdu.p2, apdu.lc, apdu.le);
|
||||
|
||||
// APDU level chaining. ISO7816-4, 5.1.1. class byte
|
||||
if (!chain_buffer_tx && buf[block_offset] & 0x10) {
|
||||
|
||||
if (chain_buffer_len + len > sizeof(chain_buffer)) {
|
||||
nfc_write_response(buf[0], SW_WRONG_LENGTH);
|
||||
return;
|
||||
}
|
||||
|
||||
memmove(&chain_buffer[chain_buffer_len], apdu.data, apdu.lc);
|
||||
chain_buffer_len += apdu.lc;
|
||||
nfc_write_response(buf[0], SW_SUCCESS);
|
||||
printf1(TAG_NFC, "APDU chaining ok. %d/%d\r\n", apdu.lc, chain_buffer_len);
|
||||
return;
|
||||
}
|
||||
|
||||
// if we have ISO 7816 APDU chain - move there all the data
|
||||
if (!chain_buffer_tx && chain_buffer_len > 0) {
|
||||
memmove(&apdu.data[chain_buffer_len], apdu.data, apdu.lc);
|
||||
memmove(apdu.data, chain_buffer, chain_buffer_len);
|
||||
apdu.lc += chain_buffer_len; // here apdu struct does not match with memory!
|
||||
printf1(TAG_NFC, "APDU chaining merge. %d/%d\r\n", chain_buffer_len, apdu.lc);
|
||||
}
|
||||
|
||||
|
||||
apdu_process(buf[0], &buf[block_offset], &apdu);
|
||||
|
||||
printf1(TAG_NFC,"prev.Iblock: ");
|
||||
dump_hex1(TAG_NFC, buf, len);
|
||||
}
|
||||
|
||||
static uint8_t ibuf[1024];
|
||||
static int ibuflen = 0;
|
||||
|
||||
@ -687,14 +839,21 @@ void nfc_process_block(uint8_t * buf, unsigned int len)
|
||||
|
||||
if (IS_PPSS_CMD(buf[0]))
|
||||
{
|
||||
printf1(TAG_NFC, "NFC_CMD_PPSS\r\n");
|
||||
printf1(TAG_NFC, "NFC_CMD_PPSS [%d] 0x%02x\r\n", len, (len > 2) ? buf[2] : 0);
|
||||
|
||||
if (buf[1] == 0x11 && (buf[2] & 0x0f) == 0x00) {
|
||||
nfc_write_frame(buf, 1); // ack with correct start byte
|
||||
} else {
|
||||
printf1(TAG_NFC, "NFC_CMD_PPSS ERROR!!!\r\n");
|
||||
nfc_write_frame((uint8_t*)"\x00", 1); // this should not happend. but iso14443-4 dont have NACK here, so just 0x00
|
||||
}
|
||||
}
|
||||
else if (IS_IBLOCK(buf[0]))
|
||||
{
|
||||
uint8_t block_offset = p14443_block_offset(buf[0]);
|
||||
if (buf[0] & 0x10)
|
||||
{
|
||||
printf1(TAG_NFC_APDU, "NFC_CMD_IBLOCK chaining blen=%d len=%d\r\n", ibuflen, len);
|
||||
printf1(TAG_NFC_APDU, "NFC_CMD_IBLOCK chaining blen=%d len=%d offs=%d\r\n", ibuflen, len, block_offset);
|
||||
if (ibuflen + len > sizeof(ibuf))
|
||||
{
|
||||
printf1(TAG_NFC, "I block memory error! must have %d but have only %d\r\n", ibuflen + len, sizeof(ibuf));
|
||||
@ -727,14 +886,15 @@ void nfc_process_block(uint8_t * buf, unsigned int len)
|
||||
memmove(ibuf, buf, block_offset);
|
||||
ibuflen += block_offset;
|
||||
|
||||
printf1(TAG_NFC_APDU, "NFC_CMD_IBLOCK chaining last block. blen=%d len=%d\r\n", ibuflen, len);
|
||||
printf1(TAG_NFC_APDU, "NFC_CMD_IBLOCK chaining last block. blen=%d len=%d offset=%d\r\n", ibuflen, len, block_offset);
|
||||
|
||||
printf1(TAG_NFC_APDU,"i> ");
|
||||
dump_hex1(TAG_NFC_APDU, buf, len);
|
||||
|
||||
nfc_process_iblock(ibuf, ibuflen);
|
||||
} else {
|
||||
nfc_process_iblock(buf, len);
|
||||
memcpy(ibuf, buf, len); // because buf only 32b
|
||||
nfc_process_iblock(ibuf, len);
|
||||
}
|
||||
clear_ibuf();
|
||||
}
|
||||
@ -779,6 +939,8 @@ int nfc_loop()
|
||||
|
||||
|
||||
read_reg_block(&ams);
|
||||
uint8_t old_int0 = gl_int0;
|
||||
process_int0(ams.regs.int0);
|
||||
uint8_t state = AMS_STATE_MASK & ams.regs.rfid_status;
|
||||
|
||||
if (state != AMS_STATE_SELECTED && state != AMS_STATE_SELECTEDX)
|
||||
@ -792,7 +954,7 @@ int nfc_loop()
|
||||
// if (state != AMS_STATE_SENSE)
|
||||
// printf1(TAG_NFC," %s x%02x\r\n", ams_get_state_string(ams.regs.rfid_status), state);
|
||||
}
|
||||
if (ams.regs.int0 & AMS_INT_INIT)
|
||||
if (ams.regs.int0 & AMS_INT_INIT || old_int0 & AMS_INT_INIT)
|
||||
{
|
||||
nfc_state_init();
|
||||
}
|
||||
@ -801,7 +963,7 @@ int nfc_loop()
|
||||
// ams_print_int1(ams.regs.int1);
|
||||
}
|
||||
|
||||
if ((ams.regs.int0 & AMS_INT_RXE))
|
||||
if (ams.regs.int0 & AMS_INT_RXE || old_int0 & AMS_INT_RXE)
|
||||
{
|
||||
if (ams.regs.buffer_status2)
|
||||
{
|
||||
@ -830,6 +992,7 @@ int nfc_loop()
|
||||
printf1(TAG_NFC, "NFC_CMD_WUPA\r\n");
|
||||
break;
|
||||
case NFC_CMD_HLTA:
|
||||
ams_write_command(AMS_CMD_SLEEP);
|
||||
printf1(TAG_NFC, "HLTA/Halt\r\n");
|
||||
break;
|
||||
case NFC_CMD_RATS:
|
||||
|
Reference in New Issue
Block a user