add some security relevant documentation
This commit is contained in:
parent
f6a21672dd
commit
64c3ea5271
1
.gitignore
vendored
1
.gitignore
vendored
@ -73,3 +73,4 @@ tools/python-fido2/*
|
|||||||
*.bin
|
*.bin
|
||||||
*.key
|
*.key
|
||||||
site/
|
site/
|
||||||
|
_site/
|
||||||
|
@ -2,11 +2,11 @@ We are very open to contributions!
|
|||||||
|
|
||||||
[Currently](https://github.com/solokeyssec/solo/issues), most work will go towards
|
[Currently](https://github.com/solokeyssec/solo/issues), most work will go towards
|
||||||
|
|
||||||
* implementing SAM L11
|
* implementing STM32L442
|
||||||
* implementing NFC
|
* implementing NFC
|
||||||
* adding documentation and improving accessability of the code
|
* adding documentation and improving accessability of the code
|
||||||
|
|
||||||
In the future, we would love to see creative plugins/extensions, putting the TRNG and TrustZone of the SAM L11 to good use!
|
In the future, we would love to see creative plugins/extensions, putting the TRNG and other features of the STM32L442 to good use!
|
||||||
|
|
||||||
Feel free to send a [pull request](https://github.com/SoloKeysSec/solo/pulls) at any time, we don't currently have a formal contribution process.
|
Feel free to send a [pull request](https://github.com/SoloKeysSec/solo/pulls) at any time, we don't currently have a formal contribution process.
|
||||||
|
|
||||||
|
82
docs/fido2-impl.md
Normal file
82
docs/fido2-impl.md
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
This page aims to document the security related aspects of the FIDO2
|
||||||
|
implementation on Solo. This is to make it easier for public review and
|
||||||
|
comments.
|
||||||
|
|
||||||
|
# Key generation
|
||||||
|
|
||||||
|
Solo aims to achieve 256 bit (32 byte) security with its FIDO2 implementation,
|
||||||
|
even in light of physical side channels.
|
||||||
|
|
||||||
|
When Solo is first programmed, it will be "uninitialized," meaning it won't
|
||||||
|
have any secret material, until the first time it boots, then it will leverage
|
||||||
|
the TRNG to generate all necessary material. This only happens once.
|
||||||
|
|
||||||
|
A master secret, `M`, is generated at initialization. This is only used for
|
||||||
|
all key generation and derivation in FIDO2. Solo uses a key wrapping method
|
||||||
|
for FIDO2 operation.
|
||||||
|
|
||||||
|
## Key wrapping
|
||||||
|
|
||||||
|
When you register a service with a FIDO2 or U2F authenticator, the
|
||||||
|
authenticator must generate a new keypair unique to that service. This keypair
|
||||||
|
could be stored on the authenticator to be used in subsequent authentications,
|
||||||
|
but now a certain amount of memory needs to be allocated for this. On embedded
|
||||||
|
devices, there isn't much memory to spare and users will allows frustratingly
|
||||||
|
hit the limit of this memory.
|
||||||
|
|
||||||
|
The answer to this problem is to do key wrapping. The authenticator just
|
||||||
|
stores `M` and uses `M` and the TRNG to generate new keys and derive previous
|
||||||
|
keys on the fly. A random number, `R`, is generated, and is placed in the
|
||||||
|
FIDO2/U2F `KEYID` parameter. The service stores `KEYID` after registering a
|
||||||
|
key and will issue it back to the authenticator for subsequent authentications.
|
||||||
|
|
||||||
|
In essence, the following happens at registration.
|
||||||
|
|
||||||
|
1. Generate `R`, calculate private key, `K`, using `HMAC(M,R)`
|
||||||
|
2. Derive public key, `P`, from `K`
|
||||||
|
3. Return `P` and `R` to service. (`R` is in `KEYID` parameter)
|
||||||
|
4. Service stores `P` and `R`.
|
||||||
|
|
||||||
|
Now on authenication.
|
||||||
|
|
||||||
|
1. Service issues authentication request with `R` in `KEYID` parameter.
|
||||||
|
2. \* Authenticator generates `K` by calculating `HMAC(M,R)`.
|
||||||
|
3. Proceed normally as if `K` was loaded from storage memory.
|
||||||
|
|
||||||
|
|
||||||
|
<!-- As part of FIDO2/U2F, there is a `KEYID` parameter that is bascially a
|
||||||
|
binary blob that the authenticator returns to the service after registering,
|
||||||
|
and the service must store it and provide it to the authenticator on subsquent
|
||||||
|
authentications.
|
||||||
|
|
||||||
|
64 bytes of secrets will be generated to make master secret parts `M1` and
|
||||||
|
`M2`, 32 bytes each. The master secrets are only used for generating signing
|
||||||
|
keys which are then used for FIDO2/U2F. -->
|
||||||
|
|
||||||
|
## Key derivation
|
||||||
|
|
||||||
|
Master secret `M` consists of 64 bytes, split into equal parts `M1` and `M2`.
|
||||||
|
In theory, we should only need 32 bytes to achieve 256 security, but we also
|
||||||
|
plan to have side channel security hence the added bytes.
|
||||||
|
|
||||||
|
Our HMAC currently is a two step process. First, just generate a normal
|
||||||
|
`SHA256-HMAC`.
|
||||||
|
|
||||||
|
1. `tmp = SHA256_HMAC(M1, R)`
|
||||||
|
|
||||||
|
We could proceed using `tmp` as our secret key, `K`. But our `SHA256-HMAC`
|
||||||
|
implementation isn't side channel resistant and we won't bother trying to add
|
||||||
|
side channel resistance. So we add an additional stage that is side channel
|
||||||
|
resistant.
|
||||||
|
|
||||||
|
2. `K = aes256_masked(M2, tmp)`
|
||||||
|
|
||||||
|
We add a masked AES encryption to provide side channel resistance. Masked AES
|
||||||
|
is well studied and relatively easy to implement. An adversary may be able to
|
||||||
|
recover `M1` via SCA but not `M2`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<sup>* There are other details I leave out. There's also an authentication tag
|
||||||
|
in the `KEYID` parameter to ensure this is a key generated by the Solo
|
||||||
|
key.</sup>
|
26
docs/signed-updates.md
Normal file
26
docs/signed-updates.md
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
|
||||||
|
Solo has a bootloader that's fixed in memory to allow for signed firmware updates. It is not a built-in bootloader provided by the chip
|
||||||
|
manufacturer, it is our own.
|
||||||
|
|
||||||
|
On the STM32L442, there is 256 KB of memory. The first 14 KB of memory is reserved for the bootloader.
|
||||||
|
The bootloader is the first thing that boots, and if the button of the device is not held for 2 seconds, the
|
||||||
|
application is immediately booted.
|
||||||
|
|
||||||
|
Consider the following memory layout of the device.
|
||||||
|
|
||||||
|
| 14 KB | 226 KB | 16KB |
|
||||||
|
|---|---|---|
|
||||||
|
| --boot-- | -------application------- | --data-- |
|
||||||
|
|
||||||
|
Our bootloader resides at address 0, followed by the application, and then the final 16 KB allocated for secret data.
|
||||||
|
|
||||||
|
The bootloader is allowed to replace any data in the application segment. When the application is first written to,
|
||||||
|
a mass erase of the application segment is triggered and a flag in the data segment is set indicating the application
|
||||||
|
is not safe to boot.
|
||||||
|
|
||||||
|
In order to boot the application, a valid signature must be provided to the bootloader. The bootloader will verify the
|
||||||
|
signature using a public key stored in the bootloader section, and the data in the application section. If the signature
|
||||||
|
is valid, the boot flag in the data section will be changed to allow boot.
|
||||||
|
|
||||||
|
Signature checks and checks to the data section boot flag are made redundantly to make glitching attacks more difficult. Random delays
|
||||||
|
between redundant checks are also made.
|
21
docs/udev.md
21
docs/udev.md
@ -1,3 +1,18 @@
|
|||||||
|
# tl;dr
|
||||||
|
|
||||||
|
Create `/etc/udev/fido.rules` and add the following.
|
||||||
|
|
||||||
|
```
|
||||||
|
# U2F Zero
|
||||||
|
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="8acf", TAG+="uaccess"
|
||||||
|
```
|
||||||
|
|
||||||
|
Then run
|
||||||
|
|
||||||
|
```
|
||||||
|
udevadm trigger
|
||||||
|
```
|
||||||
|
|
||||||
# How do udev rules work and why are they needed
|
# How do udev rules work and why are they needed
|
||||||
|
|
||||||
In Linux, `udev` (part of `systemd`, read `man 7 udev`) handles "hot-pluggable" devices, of which Solo and U2F Zero are examples. In particular, it creates nodes in the `/dev` filesystem (in Linux, everything is a file), which allow accessing the device.
|
In Linux, `udev` (part of `systemd`, read `man 7 udev`) handles "hot-pluggable" devices, of which Solo and U2F Zero are examples. In particular, it creates nodes in the `/dev` filesystem (in Linux, everything is a file), which allow accessing the device.
|
||||||
@ -34,6 +49,12 @@ KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="10c4", MODE="0644", GR
|
|||||||
```
|
```
|
||||||
which sets MODE of the device node to readable by anyone.
|
which sets MODE of the device node to readable by anyone.
|
||||||
|
|
||||||
|
Now reload the device events.
|
||||||
|
|
||||||
|
```
|
||||||
|
udevadm trigger
|
||||||
|
```
|
||||||
|
|
||||||
## What about vendor and product ID for Solo?
|
## What about vendor and product ID for Solo?
|
||||||
Current prototypes reuse the IDs of the U2F Zero (10c4/8acf). The final Solo will probably be assigned new IDs; read about it here first :)
|
Current prototypes reuse the IDs of the U2F Zero (10c4/8acf). The final Solo will probably be assigned new IDs; read about it here first :)
|
||||||
|
|
||||||
|
@ -8,6 +8,8 @@ copyright: 'Copyright © 2018 SoloKeys'
|
|||||||
nav:
|
nav:
|
||||||
- Home: index.md
|
- Home: index.md
|
||||||
- README.md: repo-readme.md
|
- README.md: repo-readme.md
|
||||||
|
- FIDO2 Implementation: fido2-impl.md
|
||||||
|
- Signed update process: signed-updates.md
|
||||||
- Contributing Code: contributing.md
|
- Contributing Code: contributing.md
|
||||||
- Contributing Docs: documenting.md
|
- Contributing Docs: documenting.md
|
||||||
- What the udev?!: udev.md
|
- What the udev?!: udev.md
|
||||||
|
Loading…
x
Reference in New Issue
Block a user