Compare commits

...

75 Commits

Author SHA1 Message Date
3de4d0155e build 2020-10-30 15:43:25 +01:00
5f9537bccc build 2020-10-30 13:58:59 +01:00
967e35472f shell 2020-09-22 15:26:43 +02:00
78f7a7a979 allow pin removal
either change to PIN to an empty one or "4321"
2020-08-30 17:02:35 +02:00
72063049a7 use bit shifts 2020-06-14 16:49:43 +02:00
4743fd2326 make led colour depend on credential 2020-06-12 23:23:59 +02:00
299e91b91b dont return index >= ctap_rk_size()
Fixes issue found by @My1: https://github.com/solokeys/solo/issues/407
2020-03-28 15:45:16 -04:00
cbf40f4ec7 hmac-secret should be different when UV=1 2020-03-28 12:28:05 -04:00
8d93f88631 Update STABLE_VERSION 2020-03-27 11:29:11 -04:00
5f8a9a44fc refactor credmgmt 2020-03-27 10:56:51 -04:00
8aa1f4ad01 change parsing TAG_CM to TAG_PARSE 2020-03-27 10:56:51 -04:00
04cffb6509 allow depth-first-search and account for interleaved RK's 2020-03-27 10:56:51 -04:00
f002d08071 Add support for the security manager in Google Chrome
This patch fixes the following issues to make Google Chrome happy:
1. Adds CTAP_CBOR_CRED_MGMT(0x0A) which is an alias to CTAP_CBOR_CRED_MGMT_PRE(0x41)
2. Returns success instead of NO_CREDENTIALS when there are no RKs
3. Skip the "icon" property if it's empty

Tested with Google Chrome Version 80.0.3987.149
2020-03-27 00:22:28 -04:00
e53b83257d Do not return NO_CREDENTIALS if there are no RKs and meta is requested
Fixes-issue: #403
2020-03-27 00:22:28 -04:00
05e149fb17 Update STABLE_VERSION 2020-03-25 15:16:14 -04:00
530e175ad1 cleanup 2020-03-25 14:57:39 -04:00
6cd3873b37 add reboot command for better testing 2020-03-25 14:57:39 -04:00
241f58657b consider credProtect with exclude list, and also check user presence 2020-03-25 14:57:39 -04:00
3b42289cce add rpId to RK's, fix counting of unique RP's 2020-03-25 14:57:39 -04:00
b3712b57fc refactor to reuse more code 2020-03-25 14:57:39 -04:00
37769bb735 to support deleted credentials, need to scan all rk slots since it's no longer continuous 2020-03-25 14:57:39 -04:00
d677f8c346 add rk delete implementations 2020-03-25 14:57:39 -04:00
98bcf647c4 implement rk delete command for cred mgmt 2020-03-25 14:57:39 -04:00
682a443f4e refactor credMgmt to parse as subCommandParams, and get ready for delete command 2020-03-25 14:57:39 -04:00
a28a05673f definitely need to update rpIdHash 2020-03-25 14:57:39 -04:00
3a70ee0ec6 refactor authData and extension handling to work for getNextAssertion 2020-03-25 14:57:39 -04:00
872a320abc Fix credential order: need to start with most recent 2020-03-25 14:57:39 -04:00
3cbf7ec451 move credProtect checking to credential filtering step 2020-03-25 14:57:39 -04:00
748c552eea fix overflow error for 5th resident key 2020-03-25 14:57:39 -04:00
98f996fcfe save some ram 2020-03-25 14:57:39 -04:00
97eb6bba8a bug fix 2020-03-25 14:57:39 -04:00
fdc5a68fcd update info/feature detection details 2020-03-25 14:57:39 -04:00
1c1005a0e8 add credprotect parameter to output 2020-03-25 14:57:39 -04:00
4831410111 add credProtect extension 2020-03-25 14:57:39 -04:00
05bc8bee55 Check return values when parsing CTAP commands 2020-03-21 12:49:05 -04:00
7112633779 Fix user presence test when pinAuth is empty
The check_retr macro is evaluating its argument twice, so when we do:

    check_retr( ctap2_user_presence_test(...) )

the user presence function is called twice and the user has to press the
button twice. This is regression introduced with commit 3b53537.
2020-03-21 12:48:05 -04:00
79b43a90fd Implement commands for management of resident keys
Implement command 0x41 which is used by OpenSSH for reading RKs. It has
the following subcommands:
 * CMD_CRED_METADATA - get number of saved/remaining RKs
 * CMD_RP_BEGIN/CMD_RP_NEXT - iterate over the saved RPs
 * CMD_RK_BEGIN/CMD_RK_NEXT - iterate over the RKs for a given RP

Fixes issue #374 and issue #314
2020-03-21 11:59:22 -04:00
ec7a6fd740 Update STABLE_VERSION 2020-03-16 15:04:45 -04:00
f2d6698066 Update version.c 2020-03-16 14:59:01 -04:00
3c9315e34c Update README.md
Basic steps how to apply updates
2020-03-09 12:06:39 -04:00
8ed7157bfe bump 3.1.2 2020-02-27 15:50:46 -05:00
28a1b1cc06 limit possible recursions in tinycbor 2020-02-27 15:40:27 -05:00
5738bcc7a3 more strict checks in cbor parsing 2020-02-27 15:40:27 -05:00
4fb166631d remove SOLO_EXPERIMENTAL default 2020-02-18 11:18:08 -05:00
1b862d3b0c fix error return 2020-02-18 11:18:08 -05:00
094420b32b overwrite if >256 is more clear 2020-02-18 11:18:08 -05:00
349cbc39f2 fix offset 2020-02-18 11:18:08 -05:00
e294cb3458 replace counter instead of increment for large increment 2020-02-18 11:18:08 -05:00
da31f984dd add version check 2020-02-18 11:18:08 -05:00
9d3e8c06fc subsequent button presses do not need to wait long 2020-02-18 11:18:08 -05:00
b378bbf61d Update building.md 2020-02-18 10:53:47 -05:00
04b89a9739 Update customization.md 2020-02-18 10:38:53 -05:00
0d28a7bcf6 Update customization.md 2020-02-18 10:03:19 -05:00
aeafd09007 only use .flag section for hw builds 2020-02-17 13:41:05 -05:00
8b6148ac90 bump 3.1.1 2020-02-13 17:32:26 -05:00
15a4fdfa66 remove unused code in bootloader 2020-02-13 17:17:23 -05:00
e713daba26 add temporary command to force flash locking 2020-02-13 17:17:23 -05:00
b78f2cd2e7 keep initialize last_addr and reject if it doesnt change 2020-02-13 17:17:23 -05:00
601c98000a Correct path for gencert tools and use python3 2020-02-12 14:52:53 -05:00
ab1c9417b1 Fix certification information 2020-02-12 14:52:53 -05:00
f6d96013e1 bump 3.1.0 2020-02-06 13:41:07 -05:00
f74dba7ff0 enforce ascending writes in bootloader update 2020-02-06 13:05:57 -05:00
794accf3dc Added how to setup Manjaro 18.x.
Added Manjaro setup for passwordless and second factor login to
documentation of applcation ideas. Also did some text formating.
2020-02-06 12:47:36 -05:00
2ca0ced808 Update programming.md 2020-01-22 12:41:39 -05:00
17b430fd44 Remove stale python-fido2 dependency 2020-01-16 10:35:22 +01:00
0d4197fb2c Merge pull request #356 from jnaulty/jnaulty/no-root-artifacts
Use current user at build container runtime
2020-01-15 21:17:53 +01:00
f74a77d80b Use current user at build container runtime
Using the current user id and group removes the need to use `sudo` when
cleaning up build artifacts from the docker build stage.

Issue: #355
2020-01-06 01:55:47 -08:00
5f1d61a3ba bump 2019-12-01 18:25:45 -05:00
46f2920e63 bugfix hid cancel 2019-12-01 18:09:08 -05:00
53427c4279 update metadata statements 2019-12-01 18:09:08 -05:00
ac10933379 pin fido2 dependency 2019-12-01 18:09:08 -05:00
8a44d14fef adjust default impl 2019-12-01 18:09:08 -05:00
1d59bbfdd4 support different aaguid's in cert for different solo models 2019-12-01 18:09:08 -05:00
54c66d80b6 overwrite x509 fields for tap or somu 2019-12-01 18:09:08 -05:00
6217fc34b9 update solo_cert to include aaguid field 2019-12-01 18:09:08 -05:00
33 changed files with 1518 additions and 276 deletions

View File

@ -100,9 +100,10 @@ uncached-docker-build-toolchain:
docker tag $(DOCKER_TOOLCHAIN_IMAGE):latest $(DOCKER_TOOLCHAIN_IMAGE):${SOLO_VERSION_MAJ} docker tag $(DOCKER_TOOLCHAIN_IMAGE):latest $(DOCKER_TOOLCHAIN_IMAGE):${SOLO_VERSION_MAJ}
docker tag $(DOCKER_TOOLCHAIN_IMAGE):latest $(DOCKER_TOOLCHAIN_IMAGE):${SOLO_VERSION_MAJ}.${SOLO_VERSION_MIN} docker tag $(DOCKER_TOOLCHAIN_IMAGE):latest $(DOCKER_TOOLCHAIN_IMAGE):${SOLO_VERSION_MAJ}.${SOLO_VERSION_MIN}
docker-build-all: docker-build-all:
docker run --rm -v "$(CURDIR)/builds:/builds" \ docker run --rm -v "$(CURDIR)/builds:/builds" \
-v "$(CURDIR):/solo" \ -v "$(CURDIR):/solo" \
-u $(shell id -u ${USER}):$(shell id -g ${USER}) \
$(DOCKER_TOOLCHAIN_IMAGE) "solo/in-docker-build.sh" ${SOLO_VERSION_FULL} $(DOCKER_TOOLCHAIN_IMAGE) "solo/in-docker-build.sh" ${SOLO_VERSION_FULL}
CPPCHECK_FLAGS=--quiet --error-exitcode=2 CPPCHECK_FLAGS=--quiet --error-exitcode=2

View File

@ -61,9 +61,11 @@ git checkout ${VERSION_TO_BUILD}
git submodule update --init --recursive git submodule update --init --recursive
``` ```
## Installing the toolchain ## Installing the toolchain and applying updates
In order to compile ARM code, you need the ARM compiler and other things like bundling bootloader and firmware require the `solo-python` python package. Check our [documentation](https://docs.solokeys.io/solo/) for details In order to compile ARM code, you need the ARM compiler and other things like bundling bootloader and firmware require the [solo-python](https://github.com/solokeys/solo-python) python package. Check our [documentation](https://docs.solokeys.io/solo/) for details.
You can update your solokey after running `pip3 install solo-python` with `solo key update` for the latest version. To apply a custom image use `solo program bootloader <file>(.json|.hex)`.
## Installing the toolkit and compiling in Docker ## Installing the toolkit and compiling in Docker
Alternatively, you can use Docker to create a container with the toolchain. Alternatively, you can use Docker to create a container with the toolchain.

View File

@ -1 +1 @@
3.0.0 4.0.0

24
default.nix Normal file
View File

@ -0,0 +1,24 @@
let
pkgs = import (fetchTarball { url = "https://github.com/NixOS/nixpkgs/archive/20.03.tar.gz"; sha256 = "0182ys095dfx02vl2a20j1hz92dx3mfgz2a6fhn31bqlp1wa8hlq"; }) {};
pyPackages = (python-packages: with python-packages; ([
solo-python pytest
] ++ (with builtins; map (d: getAttr d python-packages) (filter (d: stringLength d > 0) (pkgs.lib.splitString "\n" (builtins.readFile ./tools/requirements.txt))))));
python-with-my-packages = pkgs.python3.withPackages pyPackages;
src = ./.;
in
with pkgs; stdenv.mkDerivation {
name = "solo";
outputs = [ "out" ];
src = with lib; builtins.filterSource (path: type: !(hasSuffix path "hex" || hasSuffix path "sha256")) src;
buildInputs = [ gnumake gcc gcc-arm-embedded-8 git python-with-my-packages ];
phases = [ "unpackPhase" "configurePhase" "buildPhase" "installPhase" ];
installPhase = ''
mkdir -p $out/firmware $out
cd targets/stm32l432
make cbor
make build-hacker
cp *.hex *.sha256 *.elf cubeconfig_stm32l442.ioc $out/firmware/
'';
keepDebugInfo = true;
}

View File

@ -1,16 +1,23 @@
# Using Solo for passwordless or second factor login on Linux # Using Solo for passwordless or second factor login on Linux
## Setup on Ubuntu 18.04 ## Setup on Ubuntu and Manjaro
Before you can use Solo for passwordless or second factor login in your Linux system you have to install some packages. Before you can use Solo for passwordless or second factor login in your Linux system you have to install some packages.
This was tested under **Linux Mint 19.2**. This was tested on **Linux Mint 19.3** and on **Manjaro 18.x**
First you have to install PAM modules for u2f. First you have to install PAM modules for u2f.
**Ubuntu (Linux Mint):**
``` ```
sudo apt install libpam-u2f pamu2fcfg sudo apt install libpam-u2f pamu2fcfg
``` ```
**Manjaro**
```
pacman -Syu pam-u2f
```
## Setting up key ## Setting up key
To use Solo as passwordless or second factor login, you have to setup your system with your Solo. To use Solo as passwordless or second factor login, you have to setup your system with your Solo.
First create a new folder named **Yubico** in your **.config** folder in your **home** directory First create a new folder named **Yubico** in your **.config** folder in your **home** directory
@ -28,35 +35,57 @@ If you want to register an additional key use this command instead:
pamu2fcfg >> ~/.config/Yubico/u2f_keys pamu2fcfg >> ~/.config/Yubico/u2f_keys
``` ```
Now press the button on your Solo. Now press the button on your Solo.
<br>
<br>
If you can't generate your key (error message), you may add Yubico Team from PPA and install latest libpam-u2f and pamu2fcfg and try again. If you can't generate your key on **Ubuntu** (error message), you may add Yubico Team from PPA and install latest libpam-u2f and pamu2fcfg and try again.
``` ```
sudo add-apt-repository ppa:yubico/stable sudo add-apt-repository ppa:yubico/stable
sudo apt-get update sudo apt-get update
sudo apt-get upgrade sudo apt-get upgrade
``` ```
**Manjaro** should work without problems.
## Login into Linux ## Login into Linux
### Passwordless ### Passwordless
To login passwordless into your Linux system, you have to edit the file **lightdm** (or **gdm** or which display manager you prefered). To login passwordless into your Linux system, you have to edit the file **lightdm** (or **gdm** or which display manager you prefered).
In case of lightdm: In case of lightdm and VIM as editor:
``` ```
sudo vim /etc/pam.d/lightdm sudo vim /etc/pam.d/lightdm
``` ```
Now search following entry:
**On Ubuntu:**<br>
Search following entry:
``` ```
@include common-auth @include common-auth
``` ```
and add and add
``` ```
auth sufficient pam_u2f.so auth sufficient pam_u2f.so
``` ```
**before** @include common-auth. **before** *@include common-auth.*
<br>
<br>
Save the file and test it.<br> **On Manjaro**<br>
Search following enrty
```
auth include system-login
```
and add
```
auth sufficient pam_u2f.so
```
** before** *auth include system-login*.
<br>
<br>
Now save the file and test it.<br>
Insert Solo in your USB port and logout. Insert Solo in your USB port and logout.
Now you should be able to login into Linux without password, only with pressing your button on Solo and press enter. Now you should be able to login into Linux without password, only with pressing your button on Solo and press enter.
@ -65,7 +94,7 @@ Why **sufficient**? The difference between the keyword sufficient and required i
The login mechanism can be also used for additional features like: The login mechanism can be also used for additional features like:
: - Login after screen timeout - edit /etc/pam.d/mate-screensaver (or kde-screensaver, ...) - Login after screen timeout - edit /etc/pam.d/mate-screensaver (or kde-screensaver, ...)
- Passwordless sudo - edit /etc/pam.d/sudo - Passwordless sudo - edit /etc/pam.d/sudo
Check out your folder **/etc/pam.d/** and do some experiments. Check out your folder **/etc/pam.d/** and do some experiments.
@ -78,17 +107,36 @@ The login passwordless won't make your system more secure, but maybe more comfor
To use Solo as second factor, for login into your Linux system, is nearly the same. To use Solo as second factor, for login into your Linux system, is nearly the same.
``` ```
sudo vim /etc/pam.d/lightdm sudo vim /etc/pam.d/lightdm
``` ```
Now search following entry:
**On Ubuntu**<br>
Search following entry:
``` ```
@include common-auth @include common-auth
``` ```
and add and add
``` ```
auth required pam_u2f.so auth required pam_u2f.so
``` ```
**after** @include common-auth. **after** *@include common-auth*.
<br>
<br>
**On Manjaro**<br>
Search following entry:
```
auth include system-login
```
Add following entry
```
auth required pam_u2f.so
```
**after** *auth include system-login*.
<br>
<br>
Save the file and test it. <br> Save the file and test it. <br>
In case your Solo is not present, your password will be incrorrect. If Solo is plugged into your USB port, it will signal pressing the button and you will be able to login into Linux. In case your Solo is not present, your password will be incrorrect. If Solo is plugged into your USB port, it will signal pressing the button and you will be able to login into Linux.

View File

@ -104,9 +104,24 @@ solo mergehex bootloader.hex solo.hex bundle.hex
`bundle.hex` is our complete firmware build. Note it is in this step that you can `bundle.hex` is our complete firmware build. Note it is in this step that you can
include a custom attestation certificate or lock the device from debugging/DFU. include a custom attestation certificate or lock the device from debugging/DFU.
By default the "hacker" attestation certifcate and key is used. By default the "hacker" attestation certifcate and key is used. Use the `--lock` flag
to make this permanent.
``` ```
solo mergehex \
--attestation-key "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF" \
--attestation-cert attestation.der \
solo.hex \
bootloader.hex \
bundle.hex
```
**Warning**: If you use `--lock`, this will permanently lock the device to this new bootloader. You
won't be able to program the bootloader again or be able to connect a hardware debugger.
The new bootloader may be able to accept (signed) updates still, depending on how you configured it.
```
# Permanent!
solo mergehex \ solo mergehex \
--attestation-key "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF" \ --attestation-key "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF" \
--attestation-cert attestation.der \ --attestation-cert attestation.der \
@ -118,9 +133,5 @@ solo mergehex \
See [here for more information on custom attestation](/solo/customization/). See [here for more information on custom attestation](/solo/customization/).
If you use `--lock`, this will permanently lock the device to this new bootloader. You
won't be able to program the bootloader again or be able to connect a hardware debugger.
The new bootloader may be able to accept (signed) updates still, depending on how you configured it.
To learn more about normal updates or a "full" update, you should [read more on Solo's boot stages](/solo/bootloader-mode). To learn more about normal updates or a "full" update, you should [read more on Solo's boot stages](/solo/bootloader-mode).

View File

@ -45,7 +45,7 @@ email=example@example.com
openssl ecparam -genkey -name "$curve" -out root_key.pem -rand seed.bin openssl ecparam -genkey -name "$curve" -out root_key.pem -rand seed.bin
# generate a "signing request" # 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" openssl req -new -key root_key.pem -out root_key.pem.csr -subj "/C=$country/ST=$state/O=$organization/OU=$unit/CN=$CN/emailAddress=$email"
# self sign the request # self sign the request
openssl x509 -trustout -req -days 18250 -in root_key.pem.csr -signkey root_key.pem -out root_cert.pem -sha256 openssl x509 -trustout -req -days 18250 -in root_key.pem.csr -signkey root_key.pem -out root_cert.pem -sha256
@ -74,7 +74,7 @@ Note you must use a prime256v1 curve for this step, and you must leave the unit/
country=US country=US
state=Maine state=Maine
organization=OpenSourceSecurity organization=OpenSourceSecurity
unit="Authenticator Attestation" unit="Authenticator Attestation" # MUST KEEP THIS AS "Authenticator Attestation" for FIDO2.
CN=example.com CN=example.com
email=example@example.com email=example@example.com
@ -82,7 +82,7 @@ email=example@example.com
openssl ecparam -genkey -name "$curve" -out device_key.pem -rand seed.bin openssl ecparam -genkey -name "$curve" -out device_key.pem -rand seed.bin
# generate a "signing request" # 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" openssl req -new -key device_key.pem -out device_key.pem.csr -subj "/C=$country/ST=$state/O=$organization/OU=$unit/CN=$CN/emailAddress=$email"
# sign the request # 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 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
@ -119,7 +119,7 @@ First, [Build your solo application and bootloader](/solo/building).
Print your attestation key in a hex string format. Using our utility script: Print your attestation key in a hex string format. Using our utility script:
``` ```
python tools/print_x_y.py device_key.pem python3 tools/gencert/print_x_y.py device_key.pem
``` ```
Merge the `bootloader.hex`, `solo.hex`, attestion key, and certificate into one firmware file. Merge the `bootloader.hex`, `solo.hex`, attestion key, and certificate into one firmware file.
@ -134,6 +134,8 @@ solo mergehex \
bundle.hex bundle.hex
``` ```
**Warning**: Using the `--lock` flag prevents the DFU from being accessed on the device again. It's recommended to try first without the `--lock` flag to make sure it works.
Now you have a newly created `bundle.hex` file with a custom attestation key and cert. You can [program this `bundle.hex` file Now you have a newly created `bundle.hex` file with a custom attestation key and cert. You can [program this `bundle.hex` file
with Solo in DFU mode](/solo/programming#procedure). with Solo in DFU mode](/solo/programming#procedure).

View File

@ -36,9 +36,13 @@ 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 You can use a firmware build from the [latest release](https://github.com/solokeys/solo/releases) or use
a build that you made yourself. 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). You need to use a firmware file that has the combined bootloader, application, and attestation key pair (bootloader + firmware + key).
This means using the `bundle-*.hex` file or the `bundle.hex` from your build. If you overwrite the Solo flash with a missing bootloader, This means using the `bundle-*.hex` file or the `bundle.hex` from your build.
it will be bricked.
#### *Warning*
* **If you overwrite the Solo flash with a missing bootloader, it will be bricked**.
* **If you program bootloader and firmware with no attestation, you will run into FIDO registration issues**
We provide two types of bundled builds. The `bundle-hacker-*.hex` build is the hacker build. If you update with this, 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` you will update the bootloader and application, but nothing will be secured. The `bundle-secure-non-solokeys.hex`

File diff suppressed because it is too large Load Diff

View File

@ -16,12 +16,11 @@
#define CTAP_CLIENT_PIN 0x06 #define CTAP_CLIENT_PIN 0x06
#define CTAP_RESET 0x07 #define CTAP_RESET 0x07
#define GET_NEXT_ASSERTION 0x08 #define GET_NEXT_ASSERTION 0x08
#define CTAP_CBOR_CRED_MGMT 0x0A
#define CTAP_VENDOR_FIRST 0x40 #define CTAP_VENDOR_FIRST 0x40
#define CTAP_CBOR_CRED_MGMT_PRE 0x41
#define CTAP_VENDOR_LAST 0xBF #define CTAP_VENDOR_LAST 0xBF
// AAGUID For Solo
#define CTAP_AAGUID ((uint8_t*)"\x88\x76\x63\x1b\xd4\xa0\x42\x7f\x57\x73\x0e\xc7\x1c\x9e\x02\x79")
#define MC_clientDataHash 0x01 #define MC_clientDataHash 0x01
#define MC_rp 0x02 #define MC_rp 0x02
#define MC_user 0x03 #define MC_user 0x03
@ -40,6 +39,19 @@
#define GA_pinAuth 0x06 #define GA_pinAuth 0x06
#define GA_pinProtocol 0x07 #define GA_pinProtocol 0x07
#define CM_cmd 0x01
#define CM_cmdMetadata 0x01
#define CM_cmdRPBegin 0x02
#define CM_cmdRPNext 0x03
#define CM_cmdRKBegin 0x04
#define CM_cmdRKNext 0x05
#define CM_cmdRKDelete 0x06
#define CM_subCommandParams 0x02
#define CM_subCommandRpId 0x01
#define CM_subCommandCred 0x02
#define CM_pinProtocol 0x03
#define CM_pinAuth 0x04
#define CP_pinProtocol 0x01 #define CP_pinProtocol 0x01
#define CP_subCommand 0x02 #define CP_subCommand 0x02
#define CP_cmdGetRetries 0x01 #define CP_cmdGetRetries 0x01
@ -61,6 +73,11 @@
#define EXT_HMAC_SECRET_REQUESTED 0x01 #define EXT_HMAC_SECRET_REQUESTED 0x01
#define EXT_HMAC_SECRET_PARSED 0x02 #define EXT_HMAC_SECRET_PARSED 0x02
#define EXT_CRED_PROTECT_INVALID 0x00
#define EXT_CRED_PROTECT_OPTIONAL 0x01
#define EXT_CRED_PROTECT_OPTIONAL_WITH_CREDID 0x02
#define EXT_CRED_PROTECT_REQUIRED 0x03
#define RESP_versions 0x1 #define RESP_versions 0x1
#define RESP_extensions 0x2 #define RESP_extensions 0x2
#define RESP_aaguid 0x3 #define RESP_aaguid 0x3
@ -144,16 +161,29 @@ typedef struct
typedef struct { typedef struct {
uint8_t tag[CREDENTIAL_TAG_SIZE]; uint8_t tag[CREDENTIAL_TAG_SIZE];
uint8_t nonce[CREDENTIAL_NONCE_SIZE]; union {
uint8_t nonce[CREDENTIAL_NONCE_SIZE];
struct {
uint8_t _pad[CREDENTIAL_NONCE_SIZE - 4];
uint32_t value;
}__attribute__((packed)) metadata;
}__attribute__((packed)) entropy;
uint8_t rpIdHash[32]; uint8_t rpIdHash[32];
uint32_t count; uint32_t count;
}__attribute__((packed)) CredentialId; }__attribute__((packed)) CredentialId;
struct Credential { struct __attribute__((packed)) Credential {
CredentialId id; CredentialId id;
CTAP_userEntity user; CTAP_userEntity user;
}; };
typedef struct Credential CTAP_residentKey; typedef struct {
CredentialId id;
CTAP_userEntity user;
// Maximum amount of "extra" space in resident key.
uint8_t rpId[48];
uint8_t rpIdSize;
} __attribute__((packed)) CTAP_residentKey;
typedef struct typedef struct
{ {
@ -220,6 +250,7 @@ typedef struct
{ {
uint8_t hmac_secret_present; uint8_t hmac_secret_present;
CTAP_hmac_secret hmac_secret; CTAP_hmac_secret hmac_secret;
uint32_t cred_protect;
} CTAP_extensions; } CTAP_extensions;
typedef struct typedef struct
@ -288,6 +319,26 @@ typedef struct
} CTAP_getAssertion; } CTAP_getAssertion;
typedef struct
{
int cmd;
struct {
uint8_t rpIdHash[32];
CTAP_credentialDescriptor credentialDescriptor;
} subCommandParams;
struct {
uint8_t cmd;
uint8_t subCommandParamsCborCopy[sizeof(CTAP_credentialDescriptor) + 16];
} hashed;
uint32_t subCommandParamsCborSize;
uint8_t pinAuth[16];
uint8_t pinAuthPresent;
int pinProtocol;
} CTAP_credMgmt;
typedef struct typedef struct
{ {
int pinProtocol; int pinProtocol;
@ -306,7 +357,12 @@ typedef struct
struct _getAssertionState { struct _getAssertionState {
CTAP_authDataHeader authData; // Room for both authData struct and extensions
struct {
CTAP_authDataHeader authData;
uint8_t extensions[80];
} __attribute__((packed)) buf;
CTAP_extensions extensions;
uint8_t clientDataHash[CLIENT_DATA_HASH_SIZE]; uint8_t clientDataHash[CLIENT_DATA_HASH_SIZE];
CTAP_credentialDescriptor creds[ALLOW_LIST_MAX_SIZE]; CTAP_credentialDescriptor creds[ALLOW_LIST_MAX_SIZE];
uint8_t lastcmd; uint8_t lastcmd;

View File

@ -666,8 +666,8 @@ uint8_t ctap_parse_extensions(CborValue * val, CTAP_extensions * ext)
if (ret == CborErrorOutOfMemory) if (ret == CborErrorOutOfMemory)
{ {
printf2(TAG_ERR,"Error, rp map key is too large. Ignoring.\n"); printf2(TAG_ERR,"Error, rp map key is too large. Ignoring.\n");
cbor_value_advance(&map); check_ret( cbor_value_advance(&map) );
cbor_value_advance(&map); check_ret( cbor_value_advance(&map) );
continue; continue;
} }
check_ret(ret); check_ret(ret);
@ -698,6 +698,14 @@ uint8_t ctap_parse_extensions(CborValue * val, CTAP_extensions * ext)
printf1(TAG_RED, "warning: hmac_secret request ignored for being wrong type\r\n"); printf1(TAG_RED, "warning: hmac_secret request ignored for being wrong type\r\n");
} }
} }
else if (strncmp(key, "credProtect",11) == 0) {
if (cbor_value_get_type(&map) == CborIntegerType) {
ret = cbor_value_get_int(&map, (int*)&ext->cred_protect);
check_ret(ret);
} else {
printf1(TAG_RED, "warning: credProtect request ignored for being wrong type\r\n");
}
}
ret = cbor_value_advance(&map); ret = cbor_value_advance(&map);
check_ret(ret); check_ret(ret);
@ -871,7 +879,7 @@ uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encod
{ {
return ret; return ret;
} }
cbor_value_advance(&map); ret = cbor_value_advance(&map);
check_ret(ret); check_ret(ret);
} }
@ -999,6 +1007,163 @@ uint8_t parse_allow_list(CTAP_getAssertion * GA, CborValue * it)
return 0; return 0;
} }
static uint8_t parse_cred_mgmt_subcommandparams(CborValue * val, CTAP_credMgmt * CM)
{
size_t map_length;
int key;
int ret;
unsigned int i;
CborValue map;
size_t sz = 32;
if (cbor_value_get_type(val) != CborMapType)
{
printf2(TAG_ERR,"error, wrong type\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
ret = cbor_value_enter_container(val,&map);
check_ret(ret);
const uint8_t * start_byte = cbor_value_get_next_byte(&map) - 1;
ret = cbor_value_get_map_length(val, &map_length);
check_ret(ret);
for (i = 0; i < map_length; i++)
{
if (cbor_value_get_type(&map) != CborIntegerType)
{
printf2(TAG_ERR,"Error, expecting integer type for map key, got %s\n", cbor_value_get_type_string(&map));
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
ret = cbor_value_get_int(&map, &key);
check_ret(ret);
ret = cbor_value_advance(&map);
check_ret(ret);
switch(key)
{
case CM_subCommandRpId:
ret = cbor_value_copy_byte_string(&map, CM->subCommandParams.rpIdHash, &sz, NULL);
if (ret == CborErrorOutOfMemory)
{
printf2(TAG_ERR,"Error, map key is too large\n");
return CTAP2_ERR_LIMIT_EXCEEDED;
}
check_ret(ret);
break;
case CM_subCommandCred:
ret = parse_credential_descriptor(&map, &CM->subCommandParams.credentialDescriptor);
check_ret(ret);;
break;
}
ret = cbor_value_advance(&map);
check_ret(ret);
}
const uint8_t * end_byte = cbor_value_get_next_byte(&map);
uint32_t length = (uint32_t)end_byte - (uint32_t)start_byte;
if (length > sizeof(CM->hashed.subCommandParamsCborCopy))
{
return CTAP2_ERR_LIMIT_EXCEEDED;
}
// Copy the details that were hashed so they can be verified later.
memmove(CM->hashed.subCommandParamsCborCopy, start_byte, length);
CM->subCommandParamsCborSize = length;
return 0;
}
uint8_t ctap_parse_cred_mgmt(CTAP_credMgmt * CM, uint8_t * request, int length)
{
int ret;
unsigned int i;
int key;
size_t map_length;
CborParser parser;
CborValue it,map;
memset(CM, 0, sizeof(CTAP_credMgmt));
ret = cbor_parser_init(request, length, CborValidateCanonicalFormat, &parser, &it);
check_ret(ret);
CborType type = cbor_value_get_type(&it);
if (type != CborMapType)
{
printf2(TAG_ERR,"Error, expecting cbor map\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
ret = cbor_value_enter_container(&it,&map);
check_ret(ret);
ret = cbor_value_get_map_length(&it, &map_length);
check_ret(ret);
printf1(TAG_PARSE, "CM map has %d elements\n", map_length);
for (i = 0; i < map_length; i++)
{
type = cbor_value_get_type(&map);
if (type != CborIntegerType)
{
printf2(TAG_ERR,"Error, expecting int for map key\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
ret = cbor_value_get_int_checked(&map, &key);
check_ret(ret);
ret = cbor_value_advance(&map);
check_ret(ret);
switch(key)
{
case CM_cmd:
printf1(TAG_PARSE, "CM_cmd\n");
if (cbor_value_get_type(&map) == CborIntegerType)
{
ret = cbor_value_get_int_checked(&map, &CM->cmd);
check_ret(ret);
CM->hashed.cmd = CM->cmd;
}
else
{
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
break;
case CM_subCommandParams:
printf1(TAG_PARSE, "CM_subCommandParams\n");
ret = parse_cred_mgmt_subcommandparams(&map, CM);
check_ret(ret);
break;
case CM_pinProtocol:
printf1(TAG_PARSE, "CM_pinProtocol\n");
if (cbor_value_get_type(&map) == CborIntegerType)
{
ret = cbor_value_get_int_checked(&map, &CM->pinProtocol);
check_ret(ret);
}
else
{
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
break;
case CM_pinAuth:
printf1(TAG_PARSE, "CM_pinAuth\n");
ret = parse_fixed_byte_string(&map, CM->pinAuth, 16);
check_retr(ret);
CM->pinAuthPresent = 1;
break;
}
ret = cbor_value_advance(&map);
check_ret(ret);
}
return 0;
}
uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int length) uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int length)
{ {
@ -1132,7 +1297,7 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int
return ret; return ret;
} }
cbor_value_advance(&map); ret = cbor_value_advance(&map);
check_ret(ret); check_ret(ret);
} }
@ -1353,11 +1518,21 @@ uint8_t ctap_parse_client_pin(CTAP_clientPin * CP, uint8_t * request, int length
break; break;
case CP_getKeyAgreement: case CP_getKeyAgreement:
printf1(TAG_CP,"CP_getKeyAgreement\n"); printf1(TAG_CP,"CP_getKeyAgreement\n");
if (cbor_value_get_type(&map) != CborBooleanType)
{
printf2(TAG_ERR,"Error, expecting cbor boolean\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
ret = cbor_value_get_boolean(&map, &CP->getKeyAgreement); ret = cbor_value_get_boolean(&map, &CP->getKeyAgreement);
check_ret(ret); check_ret(ret);
break; break;
case CP_getRetries: case CP_getRetries:
printf1(TAG_CP,"CP_getRetries\n"); printf1(TAG_CP,"CP_getRetries\n");
if (cbor_value_get_type(&map) != CborBooleanType)
{
printf2(TAG_ERR,"Error, expecting cbor boolean\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
ret = cbor_value_get_boolean(&map, &CP->getRetries); ret = cbor_value_get_boolean(&map, &CP->getRetries);
check_ret(ret); check_ret(ret);
break; break;

View File

@ -35,6 +35,7 @@ uint8_t parse_cose_key(CborValue * it, COSE_key * cose);
uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encoder, uint8_t * request, int length); uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encoder, uint8_t * request, int length);
uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int length); uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int length);
uint8_t ctap_parse_cred_mgmt(CTAP_credMgmt * CM, uint8_t * request, int length);
uint8_t ctap_parse_client_pin(CTAP_clientPin * CP, uint8_t * request, int length); uint8_t ctap_parse_client_pin(CTAP_clientPin * CP, uint8_t * request, int length);
uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor * cred); uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor * cred);

View File

@ -542,6 +542,9 @@ extern void _check_ret(CborError ret, int line, const char * filename);
uint8_t ctaphid_custom_command(int len, CTAP_RESPONSE * ctap_resp, CTAPHID_WRITE_BUFFER * wb); uint8_t ctaphid_custom_command(int len, CTAP_RESPONSE * ctap_resp, CTAPHID_WRITE_BUFFER * wb);
extern void solo_lock_if_not_already();
uint8_t ctaphid_handle_packet(uint8_t * pkt_raw) uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
{ {
uint8_t cmd = 0; uint8_t cmd = 0;
@ -635,6 +638,9 @@ uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
status = ctap_request(ctap_buffer, len, &ctap_resp); status = ctap_request(ctap_buffer, len, &ctap_resp);
wb.bcnt = (ctap_resp.length+1); wb.bcnt = (ctap_resp.length+1);
wb.cid = cid;
wb.cmd = cmd;
timestamp(); timestamp();
@ -665,6 +671,9 @@ uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
u2f_request((struct u2f_request_apdu*)ctap_buffer, &ctap_resp); u2f_request((struct u2f_request_apdu*)ctap_buffer, &ctap_resp);
wb.bcnt = (ctap_resp.length); wb.bcnt = (ctap_resp.length);
wb.cid = cid;
wb.cmd = cmd;
ctaphid_write(&wb, ctap_resp.data, ctap_resp.length); ctaphid_write(&wb, ctap_resp.data, ctap_resp.length);
ctaphid_write(&wb, NULL, 0); ctaphid_write(&wb, NULL, 0);
@ -725,6 +734,11 @@ uint8_t ctaphid_custom_command(int len, CTAP_RESPONSE * ctap_resp, CTAPHID_WRITE
ctaphid_write(wb, NULL, 0); ctaphid_write(wb, NULL, 0);
return 1; return 1;
#endif #endif
#if defined(SOLO)
case CTAPHID_REBOOT:
device_reboot();
return 1;
#endif
#if !defined(IS_BOOTLOADER) #if !defined(IS_BOOTLOADER)
case CTAPHID_GETRNG: case CTAPHID_GETRNG:
@ -756,34 +770,51 @@ uint8_t ctaphid_custom_command(int len, CTAP_RESPONSE * ctap_resp, CTAPHID_WRITE
return 1; return 1;
break; break;
// Remove on next release
#if !defined(IS_BOOTLOADER) && defined(SOLO)
case 0x99:
solo_lock_if_not_already();
wb->bcnt = 0;
ctaphid_write(wb, NULL, 0);
return 1;
break;
#endif
#if !defined(IS_BOOTLOADER) && (defined(SOLO_EXPERIMENTAL)) #if !defined(IS_BOOTLOADER) && (defined(SOLO_EXPERIMENTAL))
case CTAPHID_LOADKEY: case CTAPHID_LOADKEY:
/** /**
* Load external key. Useful for enabling backups. * Load external key. Useful for enabling backups.
* bytes: 4 96 * bytes: 4 4 96
* payload: | counter_increase (BE) | master_key | * payload: version [maj rev patch RFU]| counter_replacement (BE) | master_key |
* *
* Counter should be increased by a large amount, e.g. (0x10000000) * Counter should be increased by a large amount, e.g. (0x10000000)
* to outdo any previously lost/broken keys. * to outdo any previously lost/broken keys.
*/ */
printf1(TAG_HID,"CTAPHID_LOADKEY\n"); printf1(TAG_HID,"CTAPHID_LOADKEY\n");
if (len != 100) if (len != 104)
{ {
printf2(TAG_ERR,"Error, invalid length.\n"); printf2(TAG_ERR,"Error, invalid length.\n");
ctaphid_send_error(wb->cid, CTAP1_ERR_INVALID_LENGTH); ctaphid_send_error(wb->cid, CTAP1_ERR_INVALID_LENGTH);
return 1; return 1;
} }
param = ctap_buffer[0] << 16;
param |= ctap_buffer[1] << 8;
param |= ctap_buffer[2] << 0;
if (param != 0){
ctaphid_send_error(wb->cid, CTAP2_ERR_UNSUPPORTED_OPTION);
return 1;
}
// Ask for THREE button presses // Ask for THREE button presses
if (ctap_user_presence_test(8000) > 0) if (ctap_user_presence_test(8000) > 0)
if (ctap_user_presence_test(8000) > 0) if (ctap_user_presence_test(2000) > 0)
if (ctap_user_presence_test(8000) > 0) if (ctap_user_presence_test(2000) > 0)
{ {
ctap_load_external_keys(ctap_buffer + 4); ctap_load_external_keys(ctap_buffer + 8);
param = ctap_buffer[3]; param = ctap_buffer[7];
param |= ctap_buffer[2] << 8; param |= ctap_buffer[6] << 8;
param |= ctap_buffer[1] << 16; param |= ctap_buffer[5] << 16;
param |= ctap_buffer[0] << 24; param |= ctap_buffer[4] << 24;
ctap_atomic_count(param); ctap_atomic_count(param);
wb->bcnt = 0; wb->bcnt = 0;

View File

@ -27,6 +27,7 @@
#define CTAPHID_BOOT (TYPE_INIT | 0x50) #define CTAPHID_BOOT (TYPE_INIT | 0x50)
#define CTAPHID_ENTERBOOT (TYPE_INIT | 0x51) #define CTAPHID_ENTERBOOT (TYPE_INIT | 0x51)
#define CTAPHID_ENTERSTBOOT (TYPE_INIT | 0x52) #define CTAPHID_ENTERSTBOOT (TYPE_INIT | 0x52)
#define CTAPHID_REBOOT (TYPE_INIT | 0x53)
#define CTAPHID_GETRNG (TYPE_INIT | 0x60) #define CTAPHID_GETRNG (TYPE_INIT | 0x60)
#define CTAPHID_GETVERSION (TYPE_INIT | 0x61) #define CTAPHID_GETVERSION (TYPE_INIT | 0x61)
#define CTAPHID_LOADKEY (TYPE_INIT | 0x62) #define CTAPHID_LOADKEY (TYPE_INIT | 0x62)

View File

@ -59,7 +59,10 @@ static uint8_t _attestation_cert_der[] =
"\x06\xf1\xe3\xab\x16\x21\x8e\xd8\xc0\x14\xaf\x09\x4f\x5b\x73\xef\x5e\x9e\x4b\xe7" "\x06\xf1\xe3\xab\x16\x21\x8e\xd8\xc0\x14\xaf\x09\x4f\x5b\x73\xef\x5e\x9e\x4b\xe7"
"\x35\xeb\xdd\x9b\x6d\x8f\x7d\xf3\xc4\x3a\xd7"; "\x35\xeb\xdd\x9b\x6d\x8f\x7d\xf3\xc4\x3a\xd7";
__attribute__((weak)) const uint8_t * attestation_cert_der = _attestation_cert_der;
__attribute__((weak)) void device_attestation_read_cert_der(uint8_t * dst){
memmove(dst, _attestation_cert_der, device_attestation_cert_der_get_size());
}
__attribute__((weak)) uint8_t * device_get_attestation_key(){ __attribute__((weak)) uint8_t * device_get_attestation_key(){
static uint8_t attestation_key[] = static uint8_t attestation_key[] =
@ -182,6 +185,22 @@ __attribute__((weak)) void ctap_store_rk(int index, CTAP_residentKey * rk)
} }
__attribute__((weak)) void ctap_delete_rk(int index)
{
CTAP_residentKey rk;
memset(&rk, 0xff, sizeof(CTAP_residentKey));
if (index < RK_NUM)
{
memmove(RK_STORE.rks + index, &rk, sizeof(CTAP_residentKey));
}
else
{
printf1(TAG_ERR,"Out of bounds for delete_rk\r\n");
}
}
__attribute__((weak)) void ctap_load_rk(int index, CTAP_residentKey * rk) __attribute__((weak)) void ctap_load_rk(int index, CTAP_residentKey * rk)
{ {
memmove(rk, RK_STORE.rks + index, sizeof(CTAP_residentKey)); memmove(rk, RK_STORE.rks + index, sizeof(CTAP_residentKey));
@ -199,3 +218,8 @@ __attribute__((weak)) void ctap_overwrite_rk(int index, CTAP_residentKey * rk)
} }
} }
__attribute__((weak)) void device_read_aaguid(uint8_t * dst){
uint8_t * aaguid = (uint8_t *)"\x00\x76\x63\x1b\xd4\xa0\x42\x7f\x57\x73\x0e\xc7\x1c\x9e\x02\x79";
memmove(dst, aaguid, 16);
}

View File

@ -87,6 +87,24 @@ int device_is_button_pressed();
*/ */
int ctap_user_presence_test(uint32_t delay); int ctap_user_presence_test(uint32_t delay);
//
// Return 2 for disabled, 1 for user is present, 0 user not present, -1 if cancel is requested.
/** Test for user presence.
* Perform test that user is present. Returns status on user presence. This is used by FIDO and U2F layer
* to check if an operation should continue, or if the UP flag should be set.
*
* @param delay number of milliseconds to delay waiting for user before timeout.
* @param button_confirm_colour LED colour while waiting for confirmation.
*
* @return 2 - User presence is disabled. Operation should continue, but UP flag not set.
* 1 - User presence confirmed. Operation should continue, and UP flag is set.
* 0 - User presence is not confirmed. Operation should be denied.
* -1 - Operation was canceled. Do not continue, reset transaction state.
*
* *Optional*, the default implementation will return 1, unless a FIDO2 operation calls for no UP, where this will then return 2.
*/
int ctap_user_presence_test_colour(uint32_t delay, uint32_t button_confirm_colour);
/** Disable the next user presence test. This is called by FIDO2 layer when a transaction /** Disable the next user presence test. This is called by FIDO2 layer when a transaction
* requests UP to be disabled. The next call to ctap_user_presence_test should return 2, * requests UP to be disabled. The next call to ctap_user_presence_test should return 2,
* and then UP should be enabled again. * and then UP should be enabled again.
@ -140,6 +158,13 @@ uint32_t ctap_rk_size();
*/ */
void ctap_store_rk(int index,CTAP_residentKey * rk); void ctap_store_rk(int index,CTAP_residentKey * rk);
/** Delete a resident key from an index.
* @param index to delete resident key from. Has no effect if no RK exists at index.
*
* *Optional*, if not implemented, operates on non-persistant RK's.
*/
void ctap_delete_rk(int index);
/** Read a resident key from an index into memory /** Read a resident key from an index into memory
* @param index to read resident key from. * @param index to read resident key from.
* @param rk pointer to resident key structure to write into with RK. * @param rk pointer to resident key structure to write into with RK.
@ -199,13 +224,21 @@ int device_is_nfc();
*/ */
uint8_t * device_get_attestation_key(); uint8_t * device_get_attestation_key();
/** Pointer to a ASN.1/DER encoded byte array of the attestation certificate. /** Read the device's attestation certificate into buffer @dst.
* @param dst the destination to write the certificate.
*
* The size of the certificate can be retrieved using `device_attestation_cert_der_get_size()`.
*/ */
extern const uint8_t * attestation_cert_der; void device_attestation_read_cert_der(uint8_t * dst);
/** Returns the size in bytes of attestation_cert_der. /** Returns the size in bytes of attestation_cert_der.
* @return number of bytes in attestation_cert_der, not including any C string null byte. * @return number of bytes in attestation_cert_der, not including any C string null byte.
*/ */
uint16_t device_attestation_cert_der_get_size(); uint16_t device_attestation_cert_der_get_size();
/** Read the device's 16 byte AAGUID into a buffer.
* @param dst buffer to write 16 byte AAGUID into.
* */
void device_read_aaguid(uint8_t * dst);
#endif #endif

View File

@ -51,6 +51,7 @@ struct logtag tagtable[] = {
{TAG_NFC,"NFC"}, {TAG_NFC,"NFC"},
{TAG_NFC_APDU, "NAPDU"}, {TAG_NFC_APDU, "NAPDU"},
{TAG_CCID, "CCID"}, {TAG_CCID, "CCID"},
{TAG_CM, "CRED_MGMT"},
}; };

View File

@ -48,6 +48,7 @@ typedef enum
TAG_NFC = (1 << 19), TAG_NFC = (1 << 19),
TAG_NFC_APDU = (1 << 20), TAG_NFC_APDU = (1 << 20),
TAG_CCID = (1 << 21), TAG_CCID = (1 << 21),
TAG_CM = (1 << 22),
TAG_NO_TAG = (1UL << 30), TAG_NO_TAG = (1UL << 30),
TAG_FILENO = (1UL << 31) TAG_FILENO = (1UL << 31)

View File

@ -204,7 +204,7 @@ int8_t u2f_authenticate_credential(struct u2f_key_handle * kh, uint8_t key_handl
printf1(TAG_U2F, "APPID does not match rpIdHash.\n"); printf1(TAG_U2F, "APPID does not match rpIdHash.\n");
return 0; return 0;
} }
make_auth_tag(appid, cred->nonce, cred->count, tag); make_auth_tag(appid, (uint8_t*)&cred->entropy, cred->count, tag);
if (memcmp(cred->tag, tag, CREDENTIAL_TAG_SIZE) == 0){ if (memcmp(cred->tag, tag, CREDENTIAL_TAG_SIZE) == 0){
return 1; return 1;
@ -299,7 +299,7 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c
static int16_t u2f_register(struct u2f_register_request * req) static int16_t u2f_register(struct u2f_register_request * req)
{ {
uint8_t i[] = {0x0,U2F_EC_FMT_UNCOMPRESSED}; uint8_t i[] = {0x0,U2F_EC_FMT_UNCOMPRESSED};
uint8_t cert[1024];
struct u2f_key_handle key_handle; struct u2f_key_handle key_handle;
uint8_t pubkey[64]; uint8_t pubkey[64];
uint8_t hash[32]; uint8_t hash[32];
@ -308,6 +308,11 @@ static int16_t u2f_register(struct u2f_register_request * req)
const uint16_t attest_size = device_attestation_cert_der_get_size(); const uint16_t attest_size = device_attestation_cert_der_get_size();
if (attest_size > sizeof(cert)){
printf2(TAG_ERR,"Certificate is too large for buffer\r\n");
return U2F_SW_INSUFFICIENT_MEMORY;
}
if ( ! ctap_user_presence_test(750)) if ( ! ctap_user_presence_test(750))
{ {
return U2F_SW_CONDITIONS_NOT_SATISFIED; return U2F_SW_CONDITIONS_NOT_SATISFIED;
@ -341,7 +346,8 @@ static int16_t u2f_register(struct u2f_register_request * req)
u2f_response_writeback(i,1); u2f_response_writeback(i,1);
u2f_response_writeback((uint8_t*)&key_handle,U2F_KEY_HANDLE_SIZE); u2f_response_writeback((uint8_t*)&key_handle,U2F_KEY_HANDLE_SIZE);
u2f_response_writeback(attestation_cert_der,attest_size); device_attestation_read_cert_der(cert);
u2f_response_writeback(cert,attest_size);
dump_signature_der(sig); dump_signature_der(sig);

View File

@ -1,12 +1,17 @@
#include "version.h" #include "version.h"
#include "app.h"
const version_t firmware_version
#ifdef SOLO
__attribute__ ((section (".flag"))) __attribute__ ((__used__))
#endif
= {
.major = SOLO_VERSION_MAJ,
.minor = SOLO_VERSION_MIN,
.patch = SOLO_VERSION_PATCH,
.reserved = 0
};
const version_t firmware_version __attribute__ ((section (".flag"))) __attribute__ ((__used__)) = {
.major = SOLO_VERSION_MAJ,
.minor = SOLO_VERSION_MIN,
.patch = SOLO_VERSION_PATCH,
.reserved = 0
};
// from tinycbor, for a quick static_assert // from tinycbor, for a quick static_assert
#include <compilersupport_p.h> #include <compilersupport_p.h>

View File

@ -15,8 +15,7 @@
"authenticationAlgorithm": 1, "authenticationAlgorithm": 1,
"publicKeyAlgAndEncoding": 260, "publicKeyAlgAndEncoding": 260,
"attestationTypes": [ "attestationTypes": [
15879, 15879
15880
], ],
"userVerificationDetails": [ "userVerificationDetails": [
[ [

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -449,6 +449,12 @@ void ctap_store_rk(int index, CTAP_residentKey * rk)
} }
void ctap_delete_rk(int index)
{
CTAP_residentKey rk;
memset(&rk, 0xff, sizeof(CTAP_residentKey));
memmove(RK_STORE.rks + index, &rk, sizeof(CTAP_residentKey));
}
void ctap_load_rk(int index, CTAP_residentKey * rk) void ctap_load_rk(int index, CTAP_residentKey * rk)
{ {

13
shell.nix Normal file
View File

@ -0,0 +1,13 @@
let
nixpkgs_tar = fetchTarball { url = "https://github.com/NixOS/nixpkgs/archive/20.03.tar.gz"; sha256 = "0182ys095dfx02vl2a20j1hz92dx3mfgz2a6fhn31bqlp1wa8hlq"; };
pkgs = import "${nixpkgs_tar}" {};
pyPackages = (python-packages: with python-packages; ([
solo-python pytest
] ++ (with builtins; map (d: getAttr d python-packages) (filter (d: stringLength d > 0) (pkgs.lib.splitString "\n" (builtins.readFile ./tools/requirements.txt))))));
python-with-my-packages = pkgs.python3.withPackages pyPackages;
in
with pkgs;
stdenv.mkDerivation {
name = "solo";
buildInputs = [ gnumake gcc gcc-arm-embedded-8 python-with-my-packages ];
}

View File

@ -50,12 +50,15 @@ typedef struct {
uint8_t payload[255 - 10]; uint8_t payload[255 - 10];
} __attribute__((packed)) BootloaderReq; } __attribute__((packed)) BootloaderReq;
uint8_t * last_written_app_address = 0;
/** /**
* Erase all application pages. **APPLICATION_END_PAGE excluded**. * Erase all application pages. **APPLICATION_END_PAGE excluded**.
*/ */
static void erase_application() static void erase_application()
{ {
int page; int page;
last_written_app_address = (uint8_t*) 0;
for(page = APPLICATION_START_PAGE; page < APPLICATION_END_PAGE; page++) for(page = APPLICATION_START_PAGE; page < APPLICATION_END_PAGE; page++)
{ {
flash_erase_page(page); flash_erase_page(page);
@ -106,17 +109,20 @@ int is_bootloader_disabled()
uint32_t * auth = (uint32_t *)(AUTH_WORD_ADDR+4); uint32_t * auth = (uint32_t *)(AUTH_WORD_ADDR+4);
return *auth == 0; return *auth == 0;
} }
uint8_t * last_written_app_address;
#include "version.h" #include "version.h"
bool is_firmware_version_newer_or_equal() bool is_firmware_version_newer_or_equal()
{ {
if (last_written_app_address == 0) {
return false;
}
printf1(TAG_BOOT,"Current firmware version: %u.%u.%u.%u (%02x.%02x.%02x.%02x)\r\n", printf1(TAG_BOOT,"Current firmware version: %u.%u.%u.%u (%02x.%02x.%02x.%02x)\r\n",
current_firmware_version.major, current_firmware_version.minor, current_firmware_version.patch, current_firmware_version.reserved, current_firmware_version.major, current_firmware_version.minor, current_firmware_version.patch, current_firmware_version.reserved,
current_firmware_version.major, current_firmware_version.minor, current_firmware_version.patch, current_firmware_version.reserved current_firmware_version.major, current_firmware_version.minor, current_firmware_version.patch, current_firmware_version.reserved
); );
volatile version_t * new_version = ((volatile version_t *) last_written_app_address); volatile version_t * new_version = ((volatile version_t *) (last_written_app_address-8+4));
printf1(TAG_BOOT,"Uploaded firmware version: %u.%u.%u.%u (%02x.%02x.%02x.%02x)\r\n", printf1(TAG_BOOT,"Uploaded firmware version: %u.%u.%u.%u (%02x.%02x.%02x.%02x)\r\n",
new_version->major, new_version->minor, new_version->patch, new_version->reserved, new_version->major, new_version->minor, new_version->patch, new_version->reserved,
new_version->major, new_version->minor, new_version->patch, new_version->reserved new_version->major, new_version->minor, new_version->patch, new_version->reserved
@ -170,6 +176,7 @@ int bootloader_bridge(int klen, uint8_t * keyh)
uint32_t addr = ((*((uint32_t*)req->addr)) & 0xffffff) | 0x8000000; uint32_t addr = ((*((uint32_t*)req->addr)) & 0xffffff) | 0x8000000;
uint32_t * ptr = (uint32_t *)addr; uint32_t * ptr = (uint32_t *)addr;
uint32_t current_address;
switch(req->op){ switch(req->op){
case BootWrite: case BootWrite:
@ -196,9 +203,16 @@ int bootloader_bridge(int klen, uint8_t * keyh)
printf2(TAG_ERR, "Error, boot check bypassed\n"); printf2(TAG_ERR, "Error, boot check bypassed\n");
exit(1); exit(1);
} }
current_address = addr + len;
if (current_address < (uint32_t) last_written_app_address) {
printf2(TAG_ERR, "Error, only ascending writes allowed.\n");
has_erased = 0;
return CTAP2_ERR_NOT_ALLOWED;
}
last_written_app_address = (uint8_t*) current_address;
// Do the actual write // Do the actual write
flash_write((uint32_t)ptr,req->payload, len); flash_write((uint32_t)ptr,req->payload, len);
last_written_app_address = (uint8_t *)ptr + len - 8 + 4;
break; break;
case BootDone: case BootDone:
// Writing to flash finished. Request code validation. // Writing to flash finished. Request code validation.

View File

@ -84,4 +84,5 @@ cbor:
cd ../../tinycbor/ && make clean cd ../../tinycbor/ && make clean
cd ../../tinycbor/ && make CC="$(CC)" AR=$(AR) \ cd ../../tinycbor/ && make CC="$(CC)" AR=$(AR) \
LDFLAGS="$(LDFLAGS_LIB)" \ LDFLAGS="$(LDFLAGS_LIB)" \
CFLAGS="$(CFLAGS) -Os" CFLAGS="$(CFLAGS) -Os -DCBOR_PARSER_MAX_RECURSIONS=3"

View File

@ -61,7 +61,7 @@ SECTIONS
*(.data*) *(.data*)
. = ALIGN(8); . = ALIGN(8);
_edata = .; _edata = .;
} >ram AT> flash } >sram2 AT> flash
.flag : .flag :
{ {

View File

@ -5,12 +5,16 @@
// http://opensource.org/licenses/MIT>, at your option. This file may not be // http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms. // copied, modified, or distributed except according to those terms.
#include <stdint.h> #include <stdint.h>
#include <string.h>
#include "crypto.h" #include "crypto.h"
#include "memory_layout.h" #include "memory_layout.h"
#include "device.h"
#include "sense.h"
#include "log.h"
const uint8_t attestation_solo_cert_der[] = const uint8_t attestation_solo_cert_der[] =
"\x30\x82\x02\xe1\x30\x82\x02\x88\xa0\x03\x02\x01\x02\x02\x01\x01\x30\x0a\x06\x08" "\x30\x82\x03\x03\x30\x82\x02\xaa\xa0\x03\x02\x01\x02\x02\x01\x01\x30\x0a\x06\x08"
"\x2a\x86\x48\xce\x3d\x04\x03\x02\x30\x81\x80\x31\x0b\x30\x09\x06\x03\x55\x04\x06" "\x2a\x86\x48\xce\x3d\x04\x03\x02\x30\x81\x80\x31\x0b\x30\x09\x06\x03\x55\x04\x06"
"\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08\x4d\x61\x72\x79\x6c" "\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08\x4d\x61\x72\x79\x6c"
"\x61\x6e\x64\x31\x12\x30\x10\x06\x03\x55\x04\x0a\x0c\x09\x53\x6f\x6c\x6f\x20\x4b" "\x61\x6e\x64\x31\x12\x30\x10\x06\x03\x55\x04\x0a\x0c\x09\x53\x6f\x6c\x6f\x20\x4b"
@ -18,36 +22,37 @@ const uint8_t attestation_solo_cert_der[] =
"\x41\x31\x15\x30\x13\x06\x03\x55\x04\x03\x0c\x0c\x73\x6f\x6c\x6f\x6b\x65\x79\x73" "\x41\x31\x15\x30\x13\x06\x03\x55\x04\x03\x0c\x0c\x73\x6f\x6c\x6f\x6b\x65\x79\x73"
"\x2e\x63\x6f\x6d\x31\x21\x30\x1f\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01\x16" "\x2e\x63\x6f\x6d\x31\x21\x30\x1f\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01\x16"
"\x12\x68\x65\x6c\x6c\x6f\x40\x73\x6f\x6c\x6f\x6b\x65\x79\x73\x2e\x63\x6f\x6d\x30" "\x12\x68\x65\x6c\x6c\x6f\x40\x73\x6f\x6c\x6f\x6b\x65\x79\x73\x2e\x63\x6f\x6d\x30"
"\x20\x17\x0d\x31\x38\x31\x31\x31\x31\x31\x32\x35\x32\x30\x30\x5a\x18\x0f\x32\x30" "\x20\x17\x0d\x31\x39\x31\x32\x30\x31\x31\x39\x32\x33\x34\x39\x5a\x18\x0f\x32\x30"
"\x36\x38\x31\x30\x32\x39\x31\x32\x35\x32\x30\x30\x5a\x30\x81\x92\x31\x0b\x30\x09" "\x36\x39\x31\x31\x31\x38\x31\x39\x32\x33\x34\x39\x5a\x30\x81\x91\x31\x0b\x30\x09"
"\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08" "\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08"
"\x4d\x61\x72\x79\x6c\x61\x6e\x64\x31\x12\x30\x10\x06\x03\x55\x04\x0a\x0c\x09\x53" "\x4d\x61\x72\x79\x6c\x61\x6e\x64\x31\x11\x30\x0f\x06\x03\x55\x04\x0a\x0c\x08\x53"
"\x6f\x6c\x6f\x20\x4b\x65\x79\x73\x31\x22\x30\x20\x06\x03\x55\x04\x0b\x0c\x19\x41" "\x6f\x6c\x6f\x4b\x65\x79\x73\x31\x22\x30\x20\x06\x03\x55\x04\x0b\x0c\x19\x41\x75"
"\x75\x74\x68\x65\x6e\x74\x69\x63\x61\x74\x6f\x72\x20\x41\x74\x74\x65\x73\x74\x61" "\x74\x68\x65\x6e\x74\x69\x63\x61\x74\x6f\x72\x20\x41\x74\x74\x65\x73\x74\x61\x74"
"\x74\x69\x6f\x6e\x31\x15\x30\x13\x06\x03\x55\x04\x03\x0c\x0c\x73\x6f\x6c\x6f\x6b" "\x69\x6f\x6e\x31\x15\x30\x13\x06\x03\x55\x04\x03\x0c\x0c\x73\x6f\x6c\x6f\x6b\x65"
"\x65\x79\x73\x2e\x63\x6f\x6d\x31\x21\x30\x1f\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01"
"\x09\x01\x16\x12\x68\x65\x6c\x6c\x6f\x40\x73\x6f\x6c\x6f\x6b\x65\x79\x73\x2e\x63"
"\x6f\x6d\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48"
"\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x22\xfe\x0f\xb5\x2a\x78\xbe\xc6\x45\x37\x1a"
"\x28\xa7\x57\x43\x49\xa4\x6f\x85\x4d\xca\x4e\x25\x1c\x9f\x75\x30\x3d\xbf\x10\xd5"
"\xd2\xd2\x0b\xb9\x69\x2c\xdd\xb2\x5c\x14\xd8\x39\x85\x12\xf6\x23\xee\x91\xba\xc6"
"\xac\xff\x4a\x1a\x27\xef\xe0\xc1\x54\x3f\xd4\xd9\xc5\xa3\x81\xdc\x30\x81\xd9\x30"
"\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x3b\xe6\xd2\xc0\x6f\xf2\xe7\xb0\x7c\x9d"
"\x9e\x28\xc0\x20\xb0\x0d\x07\xc8\x15\xc8\x30\x81\x9f\x06\x03\x55\x1d\x23\x04\x81"
"\x97\x30\x81\x94\xa1\x81\x86\xa4\x81\x83\x30\x81\x80\x31\x0b\x30\x09\x06\x03\x55"
"\x04\x06\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08\x4d\x61\x72"
"\x79\x6c\x61\x6e\x64\x31\x12\x30\x10\x06\x03\x55\x04\x0a\x0c\x09\x53\x6f\x6c\x6f"
"\x20\x4b\x65\x79\x73\x31\x10\x30\x0e\x06\x03\x55\x04\x0b\x0c\x07\x52\x6f\x6f\x74"
"\x20\x43\x41\x31\x15\x30\x13\x06\x03\x55\x04\x03\x0c\x0c\x73\x6f\x6c\x6f\x6b\x65"
"\x79\x73\x2e\x63\x6f\x6d\x31\x21\x30\x1f\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09" "\x79\x73\x2e\x63\x6f\x6d\x31\x21\x30\x1f\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09"
"\x01\x16\x12\x68\x65\x6c\x6c\x6f\x40\x73\x6f\x6c\x6f\x6b\x65\x79\x73\x2e\x63\x6f" "\x01\x16\x12\x68\x65\x6c\x6c\x6f\x40\x73\x6f\x6c\x6f\x6b\x65\x79\x73\x2e\x63\x6f"
"\x6d\x82\x09\x00\xc4\x47\x63\x92\x8f\xf4\xbe\x8c\x30\x09\x06\x03\x55\x1d\x13\x04" "\x6d\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce"
"\x02\x30\x00\x30\x0b\x06\x03\x55\x1d\x0f\x04\x04\x03\x02\x04\xf0\x30\x0a\x06\x08" "\x3d\x03\x01\x07\x03\x42\x00\x04\x22\xfe\x0f\xb5\x2a\x78\xbe\xc6\x45\x37\x1a\x28"
"\x2a\x86\x48\xce\x3d\x04\x03\x02\x03\x47\x00\x30\x44\x02\x20\x71\x10\x46\x2c\xf5" "\xa7\x57\x43\x49\xa4\x6f\x85\x4d\xca\x4e\x25\x1c\x9f\x75\x30\x3d\xbf\x10\xd5\xd2"
"\x16\x18\x97\x55\xca\x64\x50\x3b\x69\xb2\xdf\x17\x71\xab\xad\x8e\xc0\xd6\xa6\x07" "\xd2\x0b\xb9\x69\x2c\xdd\xb2\x5c\x14\xd8\x39\x85\x12\xf6\x23\xee\x91\xba\xc6\xac"
"\x3d\x66\x8a\x3b\xbb\xfe\x61\x02\x20\x1e\x82\xef\xeb\x5e\x4e\x3a\x00\x84\x64\xd2" "\xff\x4a\x1a\x27\xef\xe0\xc1\x54\x3f\xd4\xd9\xc5\xa3\x81\xff\x30\x81\xfc\x30\x1d"
"\xf8\x84\xc3\x78\x35\x93\x63\x81\x2e\xbe\xa6\x12\x32\x6e\x29\x90\xc8\x91\x4b\x71" "\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x3b\xe6\xd2\xc0\x6f\xf2\xe7\xb0\x7c\x9d\x9e"
"\x52" "\x28\xc0\x20\xb0\x0d\x07\xc8\x15\xc8\x30\x81\x9f\x06\x03\x55\x1d\x23\x04\x81\x97"
"\x30\x81\x94\xa1\x81\x86\xa4\x81\x83\x30\x81\x80\x31\x0b\x30\x09\x06\x03\x55\x04"
"\x06\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08\x4d\x61\x72\x79"
"\x6c\x61\x6e\x64\x31\x12\x30\x10\x06\x03\x55\x04\x0a\x0c\x09\x53\x6f\x6c\x6f\x20"
"\x4b\x65\x79\x73\x31\x10\x30\x0e\x06\x03\x55\x04\x0b\x0c\x07\x52\x6f\x6f\x74\x20"
"\x43\x41\x31\x15\x30\x13\x06\x03\x55\x04\x03\x0c\x0c\x73\x6f\x6c\x6f\x6b\x65\x79"
"\x73\x2e\x63\x6f\x6d\x31\x21\x30\x1f\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01"
"\x16\x12\x68\x65\x6c\x6c\x6f\x40\x73\x6f\x6c\x6f\x6b\x65\x79\x73\x2e\x63\x6f\x6d"
"\x82\x09\x00\xc4\x47\x63\x92\x8f\xf4\xbe\x8c\x30\x09\x06\x03\x55\x1d\x13\x04\x02"
"\x30\x00\x30\x0b\x06\x03\x55\x1d\x0f\x04\x04\x03\x02\x04\xf0\x30\x21\x06\x0b\x2b"
"\x06\x01\x04\x01\x82\xe5\x1c\x01\x01\x04\x04\x12\x04\x10\x88\x76\x63\x1b\xd4\xa0"
"\x42\x7f\x57\x73\x0e\xc7\x1c\x9e\x02\x79\x30\x0a\x06\x08\x2a\x86\x48\xce\x3d\x04"
"\x03\x02\x03\x47\x00\x30\x44\x02\x20\x4d\xea\x09\x15\x6c\x86\x48\x57\x2a\xa8\x8d"
"\x87\xc3\xfa\xb6\x6b\x29\x9b\xfb\x8b\x4d\x4d\x29\x77\x5b\xa1\x04\x4c\x7f\x12\x8d"
"\x71\x02\x20\x47\x4c\x3d\xb2\xa1\x74\xe3\x9c\xfe\xe1\x23\xbf\xec\x47\x96\xf4\xe5"
"\x9b\x65\x76\xac\xc8\x69\x1d\xe2\x74\xff\x4e\xa4\xcf\x02\x6d"
; ;
const uint8_t attestation_hacker_cert_der[] = const uint8_t attestation_hacker_cert_der[] =
@ -95,7 +100,6 @@ const uint8_t attestation_hacker_cert_der[] =
const uint16_t attestation_solo_cert_der_size = sizeof(attestation_solo_cert_der)-1; const uint16_t attestation_solo_cert_der_size = sizeof(attestation_solo_cert_der)-1;
const uint16_t attestation_hacker_cert_der_size = sizeof(attestation_hacker_cert_der)-1; const uint16_t attestation_hacker_cert_der_size = sizeof(attestation_hacker_cert_der)-1;
const uint8_t * attestation_cert_der = ((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->attestation_cert;
uint8_t * device_get_attestation_key(){ uint8_t * device_get_attestation_key(){
flash_attestation_page * page =(flash_attestation_page *)ATTESTATION_PAGE_ADDR; flash_attestation_page * page =(flash_attestation_page *)ATTESTATION_PAGE_ADDR;
@ -106,3 +110,22 @@ uint16_t device_attestation_cert_der_get_size(){
uint16_t sz = (uint16_t)((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->attestation_cert_size; uint16_t sz = (uint16_t)((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->attestation_cert_size;
return sz; return sz;
} }
void device_attestation_read_cert_der(uint8_t * dst){
const uint8_t * der = ((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->attestation_cert;
uint16_t sz = device_attestation_cert_der_get_size();
memmove(dst, der, sz);
// Overwrite respective x509 fields if Tap or Somu.
if (memcmp(dst + 0x2c6, "\xea\x09\x15\x6c\x86\x48\x57\x2a\xa8\x8d", 10) == 0){
if (device_is_nfc()){
dst[0x2a2] = 0x89;//tap aaguid byte
memmove(dst + 0xac, "\x34\x33\x38\x5a\x18\x0f\x32\x30\x36\x39\x31\x31\x31\x38\x31\x39\x32\x34\x33\x38", 20);//tap-id
memmove(dst + 0x2c5, "\x6d\x7b\x41\x2b\xff\x57\xf0\x03\xbd\x5b\x39\x4a\xf7\xa9\x2d\x6d\xcb\x9e\x2d\x88\xbf\xb3\x93\xc5\x66\x3b\xd1\xbc\x34\xfa\x5c\x4c\x02\x20\x59\x01\x49\x39\x1b\xb7\xa9\x1c\xed\x49\x78\x4f\x92\xa9\x61\x14\xa5\x6e\x96\x3f\x29\x02\x93\xe0\x5d\xe2\x75\xd0\x60\xd9\x74\xc2", 66);//tap-sig
} else if (tsc_sensor_exists()) {
dst[0x2a2] = 0x98;//somu aaguid byte
memmove(dst + 0xac, "\x35\x30\x32\x5a\x18\x0f\x32\x30\x36\x39\x31\x31\x31\x38\x31\x39\x32\x35\x30\x32", 20);//somu-id
memmove(dst + 0x2c5, "\x4d\x08\xc8\x9d\xc4\x50\x49\x70\x48\x4d\xd0\x12\xd9\x7c\x62\x5e\x6b\xd3\x84\xd5\x36\x42\xfe\x86\x8e\x7a\x23\x59\xa0\x20\xf0\xc5\x02\x20\x5f\x70\x93\x61\x5a\xe4\x20\xcf\xb9\x8a\xf5\xdd\x87\xd0\x48\x6d\x7d\x59\xef\x9e\x0e\x11\xa3\x8e\xf7\xe3\xe2\xf5\x35\x37\x99\x1a", 66);//somu-sig
}
}
}

View File

@ -199,6 +199,20 @@ int solo_is_locked(){
return tag == ATTESTATION_CONFIGURED_TAG && (device_settings & SOLO_FLAG_LOCKED) != 0; return tag == ATTESTATION_CONFIGURED_TAG && (device_settings & SOLO_FLAG_LOCKED) != 0;
} }
// Locks solo flash from debugging. Locks on next reboot.
// This should be removed in next Solo release.
void solo_lock_if_not_already() {
uint8_t buf[2048];
memmove(buf, (uint8_t*)ATTESTATION_PAGE_ADDR, 2048);
((flash_attestation_page *)buf)->device_settings |= SOLO_FLAG_LOCKED;
flash_erase_page(ATTESTATION_PAGE);
flash_write(ATTESTATION_PAGE_ADDR, buf, 2048);
}
/** device_migrate /** device_migrate
* Depending on version of device, migrates: * Depending on version of device, migrates:
* * Moves attestation certificate to data segment. * * Moves attestation certificate to data segment.
@ -563,7 +577,11 @@ uint32_t ctap_atomic_count(uint32_t amount)
return lastc; return lastc;
} }
lastc += amount; if (amount > 256){
lastc = amount;
} else {
lastc += amount;
}
if (lastc/256 > erases) if (lastc/256 > erases)
{ {
@ -685,7 +703,11 @@ static int wait_for_button_release(uint32_t wait)
return 0; return 0;
} }
int ctap_user_presence_test(uint32_t up_delay) int ctap_user_presence_test(uint32_t up_delay)
{
return ctap_user_presence_test_colour(up_delay, 0);
}
int ctap_user_presence_test_colour(uint32_t up_delay, uint32_t button_confirm_colour)
{ {
int ret; int ret;
@ -723,8 +745,7 @@ int ctap_user_presence_test(uint32_t up_delay)
} }
// Set LED status and wait. // Set LED status and wait.
led_rgb(0xff3520); led_rgb(button_confirm_colour==0?0xff3520:button_confirm_colour);
// Block and wait for some time. // Block and wait for some time.
ret = wait_for_button_activate(up_delay); ret = wait_for_button_activate(up_delay);
if (ret) return ret; if (ret) return ret;
@ -772,33 +793,28 @@ uint32_t ctap_rk_size(void)
void ctap_store_rk(int index,CTAP_residentKey * rk) void ctap_store_rk(int index,CTAP_residentKey * rk)
{ {
int page_offset = (sizeof(CTAP_residentKey) * index) / PAGE_SIZE; ctap_overwrite_rk(index, rk);
uint32_t addr = flash_addr(page_offset + RK_START_PAGE) + ((sizeof(CTAP_residentKey)*index) % PAGE_SIZE); }
printf1(TAG_GREEN, "storing RK %d @ %04x\r\n", index,addr); void ctap_delete_rk(int index)
{
if (page_offset < RK_NUM_PAGES) CTAP_residentKey rk;
{ memset(&rk, 0xff, sizeof(CTAP_residentKey));
flash_write(addr, (uint8_t*)rk, sizeof(CTAP_residentKey)); ctap_overwrite_rk(index, &rk);
//dump_hex1(TAG_GREEN,rk,sizeof(CTAP_residentKey));
}
else
{
printf2(TAG_ERR,"Out of bounds reading index %d for rk\n", index);
}
} }
void ctap_load_rk(int index,CTAP_residentKey * rk) void ctap_load_rk(int index,CTAP_residentKey * rk)
{ {
int page_offset = (sizeof(CTAP_residentKey) * index) / PAGE_SIZE; int byte_offset_into_page = (sizeof(CTAP_residentKey) * (index % (PAGE_SIZE/sizeof(CTAP_residentKey))));
uint32_t addr = flash_addr(page_offset + RK_START_PAGE) + ((sizeof(CTAP_residentKey)*index) % PAGE_SIZE); int page_offset = (index)/(PAGE_SIZE/sizeof(CTAP_residentKey));
uint32_t addr = flash_addr(page_offset + RK_START_PAGE) + byte_offset_into_page;
printf1(TAG_GREEN, "reading RK %d @ %04x\r\n", index, addr); printf1(TAG_GREEN, "reading RK %d @ %04x\r\n", index, addr);
if (page_offset < RK_NUM_PAGES) if (page_offset < RK_NUM_PAGES)
{ {
uint32_t * ptr = (uint32_t *)addr; uint32_t * ptr = (uint32_t *)addr;
memmove((uint8_t*)rk,ptr,sizeof(CTAP_residentKey)); memmove((uint8_t*)rk,ptr,sizeof(CTAP_residentKey));
//dump_hex1(TAG_GREEN,rk,sizeof(CTAP_residentKey));
} }
else else
{ {
@ -809,22 +825,28 @@ void ctap_load_rk(int index,CTAP_residentKey * rk)
void ctap_overwrite_rk(int index,CTAP_residentKey * rk) void ctap_overwrite_rk(int index,CTAP_residentKey * rk)
{ {
uint8_t tmppage[PAGE_SIZE]; uint8_t tmppage[PAGE_SIZE];
int page_offset = (sizeof(CTAP_residentKey) * index) / PAGE_SIZE;
int page = page_offset + RK_START_PAGE;
printf1(TAG_GREEN, "overwriting RK %d\r\n", index); int byte_offset_into_page = (sizeof(CTAP_residentKey) * (index % (PAGE_SIZE/sizeof(CTAP_residentKey))));
int page_offset = (index)/(PAGE_SIZE/sizeof(CTAP_residentKey));
printf1(TAG_GREEN, "overwriting RK %d @ page %d @ addr 0x%08x-0x%08x\r\n",
index, RK_START_PAGE + page_offset,
flash_addr(RK_START_PAGE + page_offset) + byte_offset_into_page,
flash_addr(RK_START_PAGE + page_offset) + byte_offset_into_page + sizeof(CTAP_residentKey)
);
if (page_offset < RK_NUM_PAGES) if (page_offset < RK_NUM_PAGES)
{ {
memmove(tmppage, (uint8_t*)flash_addr(page), PAGE_SIZE); memmove(tmppage, (uint8_t*)flash_addr(RK_START_PAGE + page_offset), PAGE_SIZE);
memmove(tmppage + (sizeof(CTAP_residentKey) * index) % PAGE_SIZE, rk, sizeof(CTAP_residentKey)); memmove(tmppage + byte_offset_into_page, rk, sizeof(CTAP_residentKey));
flash_erase_page(page); flash_erase_page(RK_START_PAGE + page_offset);
flash_write(flash_addr(page), tmppage, PAGE_SIZE); flash_write(flash_addr(RK_START_PAGE + page_offset), tmppage, PAGE_SIZE);
} }
else else
{ {
printf2(TAG_ERR,"Out of bounds reading index %d for rk\n", index); printf2(TAG_ERR,"Out of bounds reading index %d for rk\n", index);
} }
printf1(TAG_GREEN, "4\r\n");
} }
void boot_st_bootloader(void) void boot_st_bootloader(void)
@ -859,6 +881,17 @@ void boot_solo_bootloader(void)
} }
void device_read_aaguid(uint8_t * dst){
uint8_t * aaguid = (uint8_t *)"\x88\x76\x63\x1b\xd4\xa0\x42\x7f\x57\x73\x0e\xc7\x1c\x9e\x02\x79";
memmove(dst, aaguid, 16);
if (device_is_nfc()){
dst[0] = 0x89;
}
else if (tsc_sensor_exists()){
dst[0] = 0x98;
}
dump_hex1(TAG_GREEN,dst, 16);
}
void _Error_Handler(char *file, int line) void _Error_Handler(char *file, int line)

View File

@ -146,12 +146,14 @@ void device_set_clock_rate(DEVICE_CLOCK_RATE param)
case DEVICE_LOW_POWER_IDLE: case DEVICE_LOW_POWER_IDLE:
SET_CLOCK_RATE0(); SET_CLOCK_RATE0();
break; break;
#if !defined(IS_BOOTLOADER)
case DEVICE_LOW_POWER_FAST: case DEVICE_LOW_POWER_FAST:
SET_CLOCK_RATE1(); SET_CLOCK_RATE1();
break; break;
case DEVICE_FAST: case DEVICE_FAST:
SET_CLOCK_RATE2(); SET_CLOCK_RATE2();
break; break;
#endif
} }
} }

View File

@ -67,7 +67,7 @@ typedef struct flash_memory_st flash_memory_st;
#include <assert.h> #include <assert.h>
static_assert(sizeof(flash_memory_st) == 256*1024, "Data structure doesn't match flash size"); static_assert(sizeof(flash_memory_st) == 256*1024, "Data structure doesn't match flash size");
#define ATTESTATION_CONFIGURED_TAG 0xaa551e78 #define ATTESTATION_CONFIGURED_TAG 0xaa551e79
struct flash_attestation_page{ struct flash_attestation_page{
uint8_t attestation_key[32]; uint8_t attestation_key[32];

View File

@ -1,5 +1,4 @@
ecdsa ecdsa
fido2
intelhex intelhex
pyserial pyserial
solo-python solo-python