Merge branch 'master' of github.com:solokeyssec/solo
This commit is contained in:
185
docs/solo/building.md
Normal file
185
docs/solo/building.md
Normal file
@@ -0,0 +1,185 @@
|
||||
To build, develop and debug the firmware for the STM32L432. This will work
|
||||
for Solo Hacker, the Nucleo development board, or you own homemade Solo.
|
||||
|
||||
There exists a development board [NUCLEO-L432KC](https://www.st.com/en/evaluation-tools/nucleo-l432kc.html) you can use; The board does contain a debugger, so all you need is a USB cable (and some [udev](/udev) [rules](https://rust-embedded.github.io/book/intro/install/linux.html#udev-rules)).
|
||||
|
||||
# Prerequisites
|
||||
|
||||
Install the [latest ARM compiler toolchain](https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads) for your system. We recommend getting the latest compilers from ARM.
|
||||
|
||||
You can also install the ARM toolchain using a package manage like `apt-get` or `pacman`,
|
||||
but be warned they might be out of date. Typically it will be called `gcc-arm-none-eabi binutils-arm-none-eabi`.
|
||||
|
||||
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)
|
||||
|
||||
# Compilation
|
||||
|
||||
Enter the `stm32l4xx` target directory.
|
||||
|
||||
```
|
||||
cd targets/stm32l432
|
||||
```
|
||||
|
||||
Build the cbor library.
|
||||
|
||||
```bash
|
||||
make cbor
|
||||
```
|
||||
|
||||
Now build Solo.
|
||||
|
||||
```
|
||||
make build-hacker
|
||||
```
|
||||
|
||||
The `build-hacker` recipe does a few things. First it builds the bootloader, with
|
||||
signature checking disabled. Then it builds the Solo application with "hacker" features
|
||||
enabled, like being able to jump to the bootloader on command. It then merges bootloader
|
||||
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,
|
||||
as this can be risky if done often. Just use `solo.hex`.
|
||||
|
||||
### Building with debug messages
|
||||
|
||||
If you're developing, you probably want to see debug messages! Solo has a USB
|
||||
Serial port that it will send debug messages through (from `printf`). You can read them using
|
||||
a normal serial terminal like `picocom` or `putty`.
|
||||
|
||||
Just add `DEBUG=1` or `DEBUG=2` to your build recipe, like this.
|
||||
|
||||
```
|
||||
make build-hacker DEBUG=1
|
||||
```
|
||||
|
||||
If you use `DEBUG=2`, that means Solo will not boot until something starts reading
|
||||
it's debug messages. So it basically it waits to tether to a serial terminal so that you don't
|
||||
miss any debug messages.
|
||||
|
||||
We recommend using our `solotool.py` as a serial emulator since it will automatically
|
||||
reconnect each time you program Solo.
|
||||
|
||||
```
|
||||
python tools/solotool.py monitor <serial-port>
|
||||
```
|
||||
|
||||
#### Linux Users:
|
||||
|
||||
[See issue 62](https://github.com/solokeys/solo/issues/62).
|
||||
|
||||
### Building a Solo release
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
# Programming
|
||||
|
||||
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!).
|
||||
|
||||
We recommend using our `solotool.py` 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.
|
||||
|
||||
```
|
||||
python tools/solotool.py program 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.
|
||||
|
||||
```
|
||||
python tools/solotool.py program all.hex --use-dfu --detach
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
```
|
||||
python tools/solotool.py program /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.
|
||||
|
||||
```
|
||||
python tools/solotool.py 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.
|
||||
python tools/programmer.py --disable
|
||||
```
|
40
docs/solo/code-overview.md
Normal file
40
docs/solo/code-overview.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Overview of firmware
|
||||
|
||||
This is a high level overview of the code. We aim to make the code self documenting
|
||||
and easy to understand, especially when paired with a high level overview.
|
||||
|
||||
## FIDO2 codebase
|
||||
|
||||
* main.c - calls high level functions and implements event loop.
|
||||
|
||||
* ctaphid.c - implements [USBHID protocol](https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#usb) for FIDO.
|
||||
|
||||
* u2f.c - implements [U2F protocol](https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html).
|
||||
|
||||
* ctap.c - implements [CTAP2 protocol](https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html).
|
||||
|
||||
* ctap_parse.c - implements parsing for CTAP protocol.
|
||||
* this could use some work minimizing.
|
||||
|
||||
* log.c - embedded friendly debug logging.
|
||||
|
||||
* crypto.c - software implementation of the crypto needs of the application. Generally this will be copied and edited for different platforms. API defined in crypto.h should be the same.
|
||||
|
||||
* device.h - definitions of functions that are platform specific and should be implemented separately. See device.c in any of the implementations to see examples.
|
||||
|
||||
## Data flow
|
||||
|
||||
The main loop will poll the USB peripheral to see if any messages arrived,
|
||||
and then pass each one to the USBHID layer.
|
||||
|
||||
Once a USBHID message is fully buffered, it will be acted on, unless there was a previous error.
|
||||
This will get passed up to U2F or CTAP2 layer. The response is buffered and then written out to USB.
|
||||
|
||||
Depending on platform, there should be a minimum number of interrupts configured. USB will need interrupts,
|
||||
and possibly timer interrupts for keeping track of time. ST implementation users a 16-bit timer to track time,
|
||||
and interrupts to count overflows.
|
||||
|
||||
If the application is waiting on user input in CTAP2, then USBHID messages need to be continued to be polled,
|
||||
to catch any [cancel command](https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#usb-hid-cancel).
|
||||
Also, every 100ms or so, an update needs to be sent via USBHID if the CTAP2 application is still processing a getAssertion request,
|
||||
a makeCredential request, or is waiting on user input. ST leverages same 16-bit timer interrupt for this.
|
15
docs/solo/contributing.md
Normal file
15
docs/solo/contributing.md
Normal file
@@ -0,0 +1,15 @@
|
||||
We are very open to contributions!
|
||||
|
||||
[Currently](https://github.com/solokeys/solo/issues), most work will go towards
|
||||
|
||||
* implementing STM32L432
|
||||
* 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 other features of the STM32L432 to good use!
|
||||
|
||||
Feel free to send a [pull request](https://github.com/solokeys/solo/pulls) at any time, we don't currently have a formal contribution process.
|
||||
|
||||
If you want to discuss your plans in quasi-realtime beforehand, you can also join our [solokeys.public](https://keybase.io/team/solokeys.public) Keybase team.
|
||||
|
||||
But first: [join our mailing list!](https://solokeys.us19.list-manage.com/subscribe/post?u=cc0c298fb99cd136bdec8294b&id=b9cb3de62d)
|
11
docs/solo/documenting.md
Normal file
11
docs/solo/documenting.md
Normal file
@@ -0,0 +1,11 @@
|
||||
Documentation of the `master` branch is deployed to Netlify automatically.
|
||||
|
||||
To host or develop locally:
|
||||
|
||||
```
|
||||
pip install mkdocs mkdocs-material
|
||||
```
|
||||
|
||||
`mkdocs serve` and visit [localhost:8000](http://localhost:8000).
|
||||
|
||||
The file `runtime.txt` is necessary to tell Netlify to use Python3.
|
82
docs/solo/fido2-impl.md
Normal file
82
docs/solo/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>
|
BIN
docs/solo/images/favicon.ico
Normal file
BIN
docs/solo/images/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
14
docs/solo/images/logo.svg
Normal file
14
docs/solo/images/logo.svg
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 300 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
|
||||
<g transform="matrix(1,0,0,1,-20.3892,11.4354)">
|
||||
<path d="M53.089,63.558C64.091,63.558 72.618,58.03 72.618,48.032C72.618,37.829 64.624,35.095 55.758,33.028C48.62,31.427 44.952,30.627 44.952,27.293C44.952,24.626 47.687,22.825 51.822,22.825C56.224,22.825 59.026,24.892 59.493,28.56L71.019,28.56C70.553,18.562 62.559,13.9 52.222,13.9C41.554,13.9 33.293,18.363 33.293,28.56C33.293,38.563 42.02,41.097 50.288,43.03C56.825,44.631 60.693,45.431 60.693,49.232C60.693,52.833 56.891,54.233 53.023,54.233C47.353,54.233 44.219,52.233 43.686,47.432L31.827,47.432C32.093,57.963 40.422,63.558 53.089,63.558Z" style="fill:rgb(35,31,32);fill-rule:nonzero;"/>
|
||||
<path d="M93.756,55.767C89.02,55.767 86.552,51.632 86.552,45.429C86.552,39.227 89.02,35.025 93.756,35.025C98.492,35.025 101.027,39.227 101.027,45.429C101.027,51.632 98.492,55.767 93.756,55.767ZM93.823,63.492C104.824,63.492 112.018,55.7 112.018,45.429C112.018,35.159 104.824,27.367 93.823,27.367C82.888,27.367 75.56,35.159 75.56,45.429C75.56,55.7 82.888,63.492 93.823,63.492Z" style="fill:rgb(35,31,32);fill-rule:nonzero;"/>
|
||||
<rect x="115.76" y="14.767" width="10.992" height="47.658" style="fill:rgb(35,31,32);fill-rule:nonzero;"/>
|
||||
<path d="M148.689,55.767C143.953,55.767 141.485,51.632 141.485,45.429C141.485,39.227 143.953,35.025 148.689,35.025C153.425,35.025 155.96,39.227 155.96,45.429C155.96,51.632 153.425,55.767 148.689,55.767ZM148.756,63.492C159.757,63.492 166.952,55.7 166.952,45.429C166.952,35.159 159.757,27.367 148.756,27.367C137.821,27.367 130.493,35.159 130.493,45.429C130.493,55.7 137.821,63.492 148.756,63.492Z" style="fill:rgb(35,31,32);fill-rule:nonzero;"/>
|
||||
<path d="M174.493,62.425L180.485,62.425L180.485,45.538L188.152,37.862L206.085,62.425L213.085,62.425L192.218,33.79L211.485,14.7L203.952,14.7L180.485,38.396L180.485,14.7L174.493,14.7L174.493,62.425Z" style="fill:rgb(35,31,32);fill-rule:nonzero;"/>
|
||||
<path d="M228.689,63.358C236.493,63.358 241.888,59.166 243.486,51.762L238.227,51.762C237.026,56.432 233.691,58.833 228.689,58.833C221.753,58.833 217.952,53.496 217.552,46.492L244.218,46.492C244.218,35.095 238.693,27.1 228.356,27.1C218.552,27.1 212.027,35.228 212.027,45.228C212.027,55.231 218.152,63.358 228.689,63.358ZM228.356,31.358C234.558,31.358 238.093,35.562 238.427,42.433L217.685,42.433C218.485,36.162 221.953,31.358 228.356,31.358Z" style="fill:rgb(35,31,32);fill-rule:nonzero;"/>
|
||||
<path d="M253.891,73.825C258.09,73.825 260.756,72.361 263.556,65.233L278.285,27.967L272.553,27.967L264.689,49.433C263.622,52.3 262.422,56.167 262.422,56.167L262.289,56.167C262.289,56.167 261.023,52.3 259.956,49.433L251.825,27.967L245.96,27.967L259.423,61.433L258.09,64.833C256.757,68.167 255.224,69.033 252.958,69.033C251.159,69.033 250.026,68.7 249.293,68.3L249.026,68.3L249.026,73.093C250.492,73.692 251.825,73.825 253.891,73.825Z" style="fill:rgb(35,31,32);fill-rule:nonzero;"/>
|
||||
<path d="M294.823,63.425C302.76,63.425 308.418,59.898 308.418,52.965C308.418,45.029 302.627,43.495 295.69,42.095C289.753,40.895 286.418,40.161 286.418,36.426C286.418,33.692 288.419,31.492 293.355,31.492C298.491,31.492 300.959,33.492 301.493,38.027L307.021,38.027C306.222,31.292 302.026,27.233 293.489,27.233C285.487,27.233 281.093,30.959 281.093,36.693C281.093,44.229 287.152,45.629 293.956,47.03C300.292,48.363 302.96,49.364 302.96,53.232C302.96,56.433 300.626,58.9 294.956,58.9C289.82,58.9 285.819,57.233 285.087,51.231L279.56,51.231C280.093,58.967 285.353,63.425 294.823,63.425Z" style="fill:rgb(35,31,32);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.9 KiB |
4
docs/solo/index.md
Normal file
4
docs/solo/index.md
Normal file
@@ -0,0 +1,4 @@
|
||||
Welcome to the technical documentation for [solokeys/solo](https://github.com/solokeys/solo).
|
||||
|
||||
For now, you can read the repository `README.md`, more documentation to come!
|
||||
|
1
docs/solo/repo-readme.md
Symbolic link
1
docs/solo/repo-readme.md
Symbolic link
@@ -0,0 +1 @@
|
||||
../../README.md
|
26
docs/solo/signed-updates.md
Normal file
26
docs/solo/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. We plan to use Ed25519 signatures, which have [efficient constant-time implementations on Cortex-M4 chips](http://www.cs.haifa.ac.il/~orrd/LC17/paper39.pdf).
|
||||
|
||||
On the STM32L432, 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.
|
72
docs/solo/udev.md
Normal file
72
docs/solo/udev.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# tl;dr
|
||||
|
||||
Create [`/etc/udev/99-solo.rules`](https://github.com/solokeys/solo/blob/master/99-solo.rules) and add the following (which assumes your user is in group `plugdev`):
|
||||
|
||||
```
|
||||
# Solo
|
||||
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess", GROUP="plugdev", SYMLINK+="solokey"
|
||||
|
||||
# U2F Zero
|
||||
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="8acf", TAG+="uaccess", GROUP="plugdev", SYMLINK+="u2fzero"
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
By default, for security reasons often only the `root` user can access these nodes, unless they are whitelisted using a so-called "udev rule". So depending on your system setup, such a udev rule may be necessary to allow non-root users access to the device, for instance yourself when using a browser to perform two-factor authentication.
|
||||
|
||||
## What does a udev rule do?
|
||||
It matches events it receives (typically, comparing with the `==` operator), and performs actions (typically, setting attributes of the node with the `=` or `+=` operators).
|
||||
|
||||
## What is `hidraw`?
|
||||
HID are human-interface devices (keyboards, mice, Solo keys), attached via USB. The `hidraw` system gives software direct ("raw") access to the device.
|
||||
|
||||
## Which node is my Solo or U2F Zero security key?
|
||||
You can either compare `ls /dev` before and after inserting, or use the `udevadm` tool, e.g., by running
|
||||
```
|
||||
udevadm monitor --environment --udev | grep DEVNAME
|
||||
```
|
||||
Typically, you will detect `/dev/hidraw0`. Using the symlinks above, you can follow symlinks from `/dev/solokey` and `/dev/u2fzero`.
|
||||
|
||||
## How do you know if your system is configured correctly?
|
||||
Try reading and writing to the device node you identified in the previous step. Assuming the node is called `/dev/hidraw0`:
|
||||
|
||||
* read: try `cat /dev/solokey`, if you don't get "permission denied", you can access.
|
||||
* write: try `echo "hello, Solo" > /dev/solokey`. Again, if you don't get denied permission, you're OK.
|
||||
|
||||
## Which rule should I use, and how do I do it?
|
||||
Simplest is probably to copy [Yubico's rule file](https://github.com/Yubico/libu2f-host/blob/master/70-u2f.rules) to `/etc/udev/rules.d/fido.rules` on your system, for instance:
|
||||
```
|
||||
$ (cd /etc/udev/rules.d/ && sudo curl https://raw.githubusercontent.com/Yubico/libu2f-host/master/70-u2f.rules -O)
|
||||
```
|
||||
This contains rules for Yubico's keys, the U2F Zero, and many others. The relevant line for U2F Zero is:
|
||||
```
|
||||
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="8acf", TAG+="uaccess"
|
||||
```
|
||||
It matches on the correct vendor/product IDs of 10c4/8acf, and adds the TAG `uaccess`. Older versions of udev use rules such as
|
||||
```
|
||||
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="10c4", MODE="0644", GROUP="plugdev"
|
||||
```
|
||||
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?
|
||||
| Key | Vendor ID | Product ID |
|
||||
| --- | --- | --- |
|
||||
| Solo | 10c4 | 8acf |
|
||||
| U2F Zero | 0483 | a2ca |
|
||||
|
||||
## You got this all wrong, I can't believe it!
|
||||
Are you suffering from [us being wrong](https://xkcd.com/386/)? Please, send us a [pull request](https://github.com/solokeys/solo/pulls) and prove us wrong :D
|
Reference in New Issue
Block a user