add some security relevant documentation

This commit is contained in:
Conor Patrick 2018-10-14 22:49:48 -04:00
parent f6a21672dd
commit 64c3ea5271
6 changed files with 134 additions and 2 deletions

1
.gitignore vendored
View File

@ -73,3 +73,4 @@ tools/python-fido2/*
*.bin
*.key
site/
_site/

View File

@ -2,11 +2,11 @@ We are very open to contributions!
[Currently](https://github.com/solokeyssec/solo/issues), most work will go towards
* implementing SAM L11
* implementing STM32L442
* implementing NFC
* 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.

82
docs/fido2-impl.md Normal file
View 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
View 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.

View File

@ -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
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.
Now reload the device events.
```
udevadm trigger
```
## 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 :)

View File

@ -8,6 +8,8 @@ copyright: 'Copyright &copy; 2018 SoloKeys'
nav:
- Home: index.md
- README.md: repo-readme.md
- FIDO2 Implementation: fido2-impl.md
- Signed update process: signed-updates.md
- Contributing Code: contributing.md
- Contributing Docs: documenting.md
- What the udev?!: udev.md