Compare commits

...

30 Commits
1.0.2 ... 1.1.1

Author SHA1 Message Date
9f60caf9c1 for docker on windows 2019-02-26 22:00:21 -05:00
46ada5a8b9 WRONG_DATA apdu error code fix 2019-02-26 20:34:07 -05:00
54241ecd42 add option 'sim' to select UDP/simulated backend 2019-02-26 18:37:42 +01:00
e537d00173 update to new fido2 version 2019-02-26 18:37:42 +01:00
f6ff3c1b87 Fetch tags in docker build script. More robust udev rules in docs 2019-02-19 00:32:07 +01:00
afd3218358 Create CHANGELOG.md 2019-02-17 18:19:30 -05:00
ed6da0ba1e Merge pull request #111 from solokeys/fido2-ext
Fido2 ext
2019-02-17 15:51:57 -05:00
46d7be865d fix upper byte U2F for backwards compatibility 2019-02-17 15:33:24 -05:00
596c6c1077 manually specify order for reproducible builds 2019-02-16 22:30:49 -05:00
6c3014575f Update application.mk 2019-02-16 22:02:26 -05:00
190ecc8fd8 make work for windows 2019-02-16 21:27:56 -05:00
0d2e03a5a9 Change firmware-hacker ROP level, add ST DFU udev
- later we can set ROP=1 for hacker firmware builds again,
  right now it causes issues in solo-python tool
2019-02-17 02:30:16 +01:00
991530f88b generate the serial number same as DFU 2019-02-16 14:08:59 -05:00
de31924be3 Lock down reproducible make targets and use in docker build 2019-02-16 18:46:13 +01:00
6b97807f51 Easier hex make targets for docker build 2019-02-16 18:36:04 +01:00
35022775cd Ensure the docker image has all public commits to build from 2019-02-16 18:10:16 +01:00
6fecb3c035 base serial number off of chip Unique device ID 2019-02-16 00:23:11 -05:00
3fed8cebdf reduce RNG to 71 2019-02-14 18:01:23 -05:00
c81bc9fb98 Detailed version in product name 2019-02-14 23:13:06 +01:00
99f09790f1 deterministic 2019-02-14 16:03:19 -05:00
6745c9a0cb bugfix/skip-auth for fido2 extension 2019-02-14 15:53:02 -05:00
0651316da5 catch U2F check by extension 2019-02-14 15:16:13 -05:00
f48becc6dc bridge extension to fido2 interface 2019-02-14 15:15:58 -05:00
85c58e9d5b TAG_EXT typo 2019-02-14 15:15:24 -05:00
c9862977bf delete old key 2019-02-13 19:22:45 -05:00
1a40299dcb add solokeys cert 2019-02-13 19:16:44 -05:00
8f9ff17bef ability to build solo versions via make docker-build SOLO_VERSION=... 2019-02-14 00:35:28 +01:00
9e9d26e604 Split building and merging firmware in two, use volumes 2019-02-14 00:35:28 +01:00
b3d76d56e0 Add docker-build make target, adjust instructions, remove Python2 support 2019-02-14 00:35:28 +01:00
13424fdbcd add Dockerfile 2019-02-14 00:35:28 +01:00
28 changed files with 359 additions and 58 deletions

2
.gitignore vendored
View File

@ -91,3 +91,5 @@ targets/efm32/EFM32_EFM32JG1B200F128GM32.hwconf
targets/efm32/emlib/em_adc.c targets/efm32/emlib/em_adc.c
targets/efm32/emlib/em_assert.c targets/efm32/emlib/em_assert.c
targets/efm32/emlib/em_cmu.c targets/efm32/emlib/em_cmu.c
builds/*

View File

@ -10,9 +10,12 @@ LABEL="mm_usb_device_blacklist_end"
# Solo # Solo
## access ## bootloader + firmware access
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess", GROUP="plugdev" ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess", GROUP="plugdev"
## DFU access
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", TAG+="uaccess", GROUP="plugdev"
## Solo Secure symlink ## Solo Secure symlink
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", ATTRS{product}=="Solo [1-9]*", SYMLINK+="solokey" SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", ATTRS{product}=="Solo [1-9]*", SYMLINK+="solokey"
## Solo Hacker symlink ## Solo Hacker symlink

17
CHANGELOG.md Normal file
View File

@ -0,0 +1,17 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [1.1.0] - 2019-02-17
### Added
- Code cleanup
- Buffer over-read bug fix
- U2F counter endianness bug fix
- More testing
- Extension interface to U2F and FIDO2
- Read firmware version
- Read RNG bytes

31
Dockerfile Normal file
View File

@ -0,0 +1,31 @@
FROM debian:stretch-slim
MAINTAINER SoloKeys <hello@solokeys.com>
RUN apt-get update -qq
RUN apt-get install -qq bzip2 git make wget >/dev/null
# 1. ARM GCC: for compilation
RUN wget -q -O gcc.tar.bz2 https://developer.arm.com/-/media/Files/downloads/gnu-rm/8-2018q4/gcc-arm-none-eabi-8-2018-q4-major-linux.tar.bz2?revision=d830f9dd-cd4f-406d-8672-cca9210dd220?product=GNU%20Arm%20Embedded%20Toolchain,64-bit,,Linux,8-2018-q4-major
# from website
RUN echo "f55f90d483ddb3bcf4dae5882c2094cd gcc.tar.bz2" > gcc.md5
RUN md5sum -c gcc.md5
# self-generated
RUN echo "fb31fbdfe08406ece43eef5df623c0b2deb8b53e405e2c878300f7a1f303ee52 gcc.tar.bz2" > gcc.sha256
RUN sha256sum -c gcc.sha256
RUN tar -C /opt -xf gcc.tar.bz2
# 2. Python3.7: for solotool (merging etc.)
RUN wget -q -O miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-4.5.12-Linux-x86_64.sh
# from website
RUN echo "866ae9dff53ad0874e1d1a60b1ad1ef8 miniconda.sh" > miniconda.md5
RUN md5sum -c miniconda.md5
# self-generated
RUN echo "e5e5b4cd2a918e0e96b395534222773f7241dc59d776db1b9f7fedfcb489157a miniconda.sh" > miniconda.sha256
RUN sha256sum -c miniconda.sha256
RUN bash ./miniconda.sh -b -p /opt/conda
RUN ln -s /opt/conda/bin/python3 /usr/local/bin/python3
RUN ln -s /opt/conda/bin/python3 /usr/local/bin/python
# 3. Source code
RUN git clone --recurse-submodules https://github.com/solokeys/solo /solo --config core.autocrlf=input

View File

@ -30,7 +30,7 @@ CFLAGS += -DAES256=1 -DAPP_CONFIG=\"app.h\"
name = main name = main
.PHONY: all $(LIBCBOR) black blackcheck cppcheck wink fido2-test clean full-clean travis test clean .PHONY: all $(LIBCBOR) black blackcheck cppcheck wink fido2-test clean full-clean travis test clean version
all: main all: main
tinycbor/Makefile crypto/tiny-AES-c/aes.c: tinycbor/Makefile crypto/tiny-AES-c/aes.c:
@ -42,6 +42,9 @@ cbor: $(LIBCBOR)
$(LIBCBOR): $(LIBCBOR):
cd tinycbor/ && $(MAKE) clean && $(MAKE) -j8 cd tinycbor/ && $(MAKE) clean && $(MAKE) -j8
version:
@git describe
test: venv test: venv
$(MAKE) clean $(MAKE) clean
$(MAKE) -C . main $(MAKE) -C . main
@ -71,6 +74,14 @@ wink: venv
fido2-test: venv fido2-test: venv
venv/bin/python tools/ctap_test.py venv/bin/python tools/ctap_test.py
DOCKER_IMAGE := "solokeys/solo-firmware:local"
SOLO_VERSIONISH := "master"
docker-build:
docker build -t $(DOCKER_IMAGE) .
docker run --rm -v "$(CURDIR)/builds:/builds" \
-v "$(CURDIR)/in-docker-build.sh:/in-docker-build.sh" \
$(DOCKER_IMAGE) "./in-docker-build.sh" $(SOLO_VERSIONISH)
CPPCHECK_FLAGS=--quiet --error-exitcode=2 CPPCHECK_FLAGS=--quiet --error-exitcode=2
cppcheck: cppcheck:

View File

@ -35,7 +35,7 @@ Solo for Hacker is a special version of Solo that let you customize its firmware
You can only buy Solo for Hacker at [solokeys.com](https://solokeys.com), as we don't sell it on Amazon and other places to avoid confusing customers. If you buy a Hacker, you can permanently lock it into a regular Solo, but viceversa you can NOT take a regular Solo and turn it a Hacker. You can only buy Solo for Hacker at [solokeys.com](https://solokeys.com), as we don't sell it on Amazon and other places to avoid confusing customers. If you buy a Hacker, you can permanently lock it into a regular Solo, but viceversa you can NOT take a regular Solo and turn it a Hacker.
If you have a Solo for Hacker, here's how you can load your own code on it. You can find more details, including how to permanently lock it, in our [documentation](https://docs.solokeys.io/solo/building/). If you have a Solo for Hacker, here's how you can load your own code on it. You can find more details, including how to permanently lock it, in our [documentation](https://docs.solokeys.io/solo/building/). We only support Python3.
```bash ```bash
git clone --recurse-submodules https://github.com/solokeys/solo git clone --recurse-submodules https://github.com/solokeys/solo
@ -43,7 +43,7 @@ cd solo
cd targets/stm32l432 cd targets/stm32l432
make cbor make cbor
make all-hacker make build-hacker
cd ../.. cd ../..
make venv make venv
@ -51,6 +51,8 @@ source venv/bin/activate
python tools/solotool.py program targets/stm32l432/solo.hex python tools/solotool.py program targets/stm32l432/solo.hex
``` ```
Alternatively, run `make docker-build` and use the firmware generated in `/tmp`.
If you forgot the `--recurse-submodules` when cloning, simply `git submodule update --init --recursive`. If you forgot the `--recurse-submodules` when cloning, simply `git submodule update --init --recursive`.
For example, if you want to turn off any blue light emission, you can edit [`led_rgb()`](https://github.com/solokeys/solo/blob/master/targets/stm32l432/src/led.c#L15) and force: For example, if you want to turn off any blue light emission, you can edit [`led_rgb()`](https://github.com/solokeys/solo/blob/master/targets/stm32l432/src/led.c#L15) and force:

1
STABLE_VERSION Normal file
View File

@ -0,0 +1 @@
1.1.1

0
builds/.gitkeep Normal file
View File

View File

@ -1,13 +1,16 @@
# tl;dr # tl;dr
Create [`/etc/udev/rules.d/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`): Create a file like [`/etc/udev/rules.d/99-solo.rules`](https://github.com/solokeys/solo/blob/master/99-solo.rules), for instance the following rules should cover access in all cases:
``` ```
# Solo # Solo bootloader + firmware
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess", GROUP="plugdev", SYMLINK+="solokey" ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess", GROUP="plugdev"
# ST DFU bootloader
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", TAG+="uaccess", GROUP="plugdev"
# U2F Zero # U2F Zero
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="8acf", TAG+="uaccess", GROUP="plugdev", SYMLINK+="u2fzero" ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="8acf", TAG+="uaccess", GROUP="plugdev"
``` ```
Then run Then run
@ -50,7 +53,7 @@ This contains rules for Yubico's keys, the U2F Zero, and many others. The releva
``` ```
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="8acf", TAG+="uaccess" 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 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" KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="10c4", MODE="0644", GROUP="plugdev"
``` ```

View File

@ -21,6 +21,7 @@
#include "device.h" #include "device.h"
#include APP_CONFIG #include APP_CONFIG
#include "wallet.h" #include "wallet.h"
#include "extensions.h"
#include "device.h" #include "device.h"
@ -776,7 +777,18 @@ int ctap_filter_invalid_credentials(CTAP_getAssertion * GA)
if (! ctap_authenticate_credential(&GA->rp, &GA->creds[i])) if (! ctap_authenticate_credential(&GA->rp, &GA->creds[i]))
{ {
printf1(TAG_GA, "CRED #%d is invalid\n", GA->creds[i].credential.id.count); printf1(TAG_GA, "CRED #%d is invalid\n", GA->creds[i].credential.id.count);
GA->creds[i].credential.id.count = 0; // invalidate #ifdef ENABLE_U2F_EXTENSIONS
if (is_extension_request((uint8_t*)&GA->creds[i].credential.id, sizeof(CredentialId)))
{
printf1(TAG_EXT, "CRED #%d is extension\n", GA->creds[i].credential.id.count);
count++;
}
else
#endif
{
GA->creds[i].credential.id.count = 0; // invalidate
}
} }
else else
{ {
@ -856,6 +868,7 @@ uint8_t ctap_end_get_assertion(CborEncoder * map, CTAP_credentialDescriptor * cr
int ret; int ret;
uint8_t sigbuf[64]; uint8_t sigbuf[64];
uint8_t sigder[72]; uint8_t sigder[72];
int sigder_sz;
if (add_user) if (add_user)
{ {
@ -869,7 +882,16 @@ uint8_t ctap_end_get_assertion(CborEncoder * map, CTAP_credentialDescriptor * cr
crypto_ecc256_load_key((uint8_t*)&cred->credential.id, sizeof(CredentialId), NULL, 0); crypto_ecc256_load_key((uint8_t*)&cred->credential.id, sizeof(CredentialId), NULL, 0);
int sigder_sz = ctap_calculate_signature(auth_data_buf, sizeof(CTAP_authDataHeader), clientDataHash, auth_data_buf, sigbuf, sigder); #ifdef ENABLE_U2F_EXTENSIONS
if ( extend_fido2(&cred->credential.id, sigder) )
{
sigder_sz = 72;
}
else
#endif
{
sigder_sz = ctap_calculate_signature(auth_data_buf, sizeof(CTAP_authDataHeader), clientDataHash, auth_data_buf, sigbuf, sigder);
}
{ {
ret = cbor_encode_int(map, RESP_signature); ret = cbor_encode_int(map, RESP_signature);
@ -988,8 +1010,21 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
ret = cbor_encoder_create_map(encoder, &map, map_size); ret = cbor_encoder_create_map(encoder, &map, map_size);
check_ret(ret); check_ret(ret);
ret = ctap_make_auth_data(&GA.rp, &map, auth_data_buf, sizeof(auth_data_buf), NULL, 0,0,NULL, 0); #ifdef ENABLE_U2F_EXTENSIONS
check_retr(ret); if ( is_extension_request((uint8_t*)&GA.creds[validCredCount - 1].credential.id, sizeof(CredentialId)) )
{
ret = cbor_encode_int(&map,RESP_authData);
check_ret(ret);
memset(auth_data_buf,0,sizeof(auth_data_buf));
ret = cbor_encode_byte_string(&map, auth_data_buf, sizeof(auth_data_buf));
check_ret(ret);
}
else
#endif
{
ret = ctap_make_auth_data(&GA.rp, &map, auth_data_buf, sizeof(auth_data_buf), NULL, 0,0,NULL, 0);
check_retr(ret);
}
/*for (int j = 0; j < GA.credLen; j++)*/ /*for (int j = 0; j < GA.credLen; j++)*/
/*{*/ /*{*/

View File

@ -8,6 +8,7 @@
#include <stdint.h> #include <stdint.h>
#include "extensions.h" #include "extensions.h"
#include "u2f.h" #include "u2f.h"
#include "ctap.h"
#include "wallet.h" #include "wallet.h"
#include "solo.h" #include "solo.h"
#include "device.h" #include "device.h"
@ -57,7 +58,8 @@ int16_t bridge_u2f_to_extensions(uint8_t * _chal, uint8_t * _appid, uint8_t klen
#elif defined(WALLET_EXTENSION) #elif defined(WALLET_EXTENSION)
ret = bridge_u2f_to_wallet(_chal, _appid, klen, keyh); ret = bridge_u2f_to_wallet(_chal, _appid, klen, keyh);
#else #else
ret = bridge_u2f_to_solo(_chal, _appid, klen, keyh); ret = bridge_u2f_to_solo(sig, keyh, klen);
u2f_response_writeback(sig,72);
#endif #endif
if (ret != 0) if (ret != 0)
@ -74,6 +76,21 @@ int16_t bridge_u2f_to_extensions(uint8_t * _chal, uint8_t * _appid, uint8_t klen
return U2F_SW_NO_ERROR; return U2F_SW_NO_ERROR;
} }
// Returns 1 if this is a extension request.
// Else 0 if nothing is done.
int16_t extend_fido2(CredentialId * credid, uint8_t * output)
{
if (is_extension_request((uint8_t*)credid, sizeof(CredentialId)))
{
output[0] = bridge_u2f_to_solo(output+1, (uint8_t*)credid, sizeof(CredentialId));
return 1;
}
else
{
return 0;
}
}
int16_t extend_u2f(struct u2f_request_apdu* req, uint32_t len) int16_t extend_u2f(struct u2f_request_apdu* req, uint32_t len)
{ {
@ -93,7 +110,7 @@ int16_t extend_u2f(struct u2f_request_apdu* req, uint32_t len)
{ {
rcode = U2F_SW_WRONG_DATA; rcode = U2F_SW_WRONG_DATA;
} }
printf1(TAG_EXT,"Ignoring U2F request\n"); printf1(TAG_EXT,"Ignoring U2F check request\n");
dump_hex1(TAG_EXT, (uint8_t *) &auth->kh, auth->khl); dump_hex1(TAG_EXT, (uint8_t *) &auth->kh, auth->khl);
goto end; goto end;
} }
@ -101,8 +118,8 @@ int16_t extend_u2f(struct u2f_request_apdu* req, uint32_t len)
{ {
if ( ! is_extension_request((uint8_t *) &auth->kh, auth->khl)) // Pin requests if ( ! is_extension_request((uint8_t *) &auth->kh, auth->khl)) // Pin requests
{ {
rcode = U2F_SW_WRONG_PAYLOAD; rcode = U2F_SW_WRONG_DATA;
printf1(TAG_EXT, "Ignoring U2F request\n"); printf1(TAG_EXT, "Ignoring U2F auth request\n");
dump_hex1(TAG_EXT, (uint8_t *) &auth->kh, auth->khl); dump_hex1(TAG_EXT, (uint8_t *) &auth->kh, auth->khl);
goto end; goto end;
} }

View File

@ -10,6 +10,10 @@
int16_t extend_u2f(struct u2f_request_apdu* req, uint32_t len); int16_t extend_u2f(struct u2f_request_apdu* req, uint32_t len);
int16_t extend_fido2(CredentialId * credid, uint8_t * output);
int bootloader_bridge(int klen, uint8_t * keyh); int bootloader_bridge(int klen, uint8_t * keyh);
int is_extension_request(uint8_t * kh, int len);
#endif /* EXTENSIONS_H_ */ #endif /* EXTENSIONS_H_ */

View File

@ -31,27 +31,26 @@
#include "log.h" #include "log.h"
#include APP_CONFIG #include APP_CONFIG
int16_t bridge_u2f_to_solo(uint8_t * _chal, uint8_t * _appid, uint8_t klen, uint8_t * keyh) // output must be at least 71 bytes
int16_t bridge_u2f_to_solo(uint8_t * output, uint8_t * keyh, int keylen)
{ {
static uint8_t msg_buf[72];
int8_t ret = 0; int8_t ret = 0;
wallet_request * req = (wallet_request *) keyh; wallet_request * req = (wallet_request *) keyh;
printf1(TAG_WALLET, "u2f-solo [%d]: ", klen); dump_hex1(TAG_WALLET, keyh, klen); printf1(TAG_WALLET, "u2f-solo [%d]: ", keylen); dump_hex1(TAG_WALLET, keyh, keylen);
switch(req->operation) switch(req->operation)
{ {
case WalletVersion: case WalletVersion:
msg_buf[0] = SOLO_VERSION_MAJ; output[0] = SOLO_VERSION_MAJ;
msg_buf[1] = SOLO_VERSION_MIN; output[1] = SOLO_VERSION_MIN;
msg_buf[2] = SOLO_VERSION_PATCH; output[2] = SOLO_VERSION_PATCH;
u2f_response_writeback(msg_buf, 3);
break; break;
case WalletRng: case WalletRng:
printf1(TAG_WALLET,"SoloRng\n"); printf1(TAG_WALLET,"SoloRng\n");
ret = ctap_generate_rng(msg_buf, 72); ret = ctap_generate_rng(output, 71);
if (ret != 1) if (ret != 1)
{ {
printf1(TAG_WALLET,"Rng failed\n"); printf1(TAG_WALLET,"Rng failed\n");
@ -60,7 +59,6 @@ int16_t bridge_u2f_to_solo(uint8_t * _chal, uint8_t * _appid, uint8_t klen, uint
} }
ret = 0; ret = 0;
u2f_response_writeback((uint8_t *)msg_buf,72);
break; break;
default: default:

View File

@ -22,6 +22,6 @@
#ifndef SOLO_H_ #ifndef SOLO_H_
#define SOLO_H_ #define SOLO_H_
int16_t bridge_u2f_to_solo(uint8_t * _chal, uint8_t * _appid, uint8_t klen, uint8_t * keyh); int16_t bridge_u2f_to_solo(uint8_t * output, uint8_t * keyh, int keylen);
#endif #endif

View File

@ -47,7 +47,7 @@ struct logtag tagtable[] = {
{TAG_WALLET,"WALLET"}, {TAG_WALLET,"WALLET"},
{TAG_STOR,"STOR"}, {TAG_STOR,"STOR"},
{TAG_BOOT,"BOOT"}, {TAG_BOOT,"BOOT"},
{TAG_BOOT,"EXT"}, {TAG_EXT,"EXT"},
}; };

View File

@ -41,7 +41,7 @@ typedef enum
TAG_STOR = (1 << 15), TAG_STOR = (1 << 15),
TAG_DUMP2 = (1 << 16), TAG_DUMP2 = (1 << 16),
TAG_BOOT = (1 << 17), TAG_BOOT = (1 << 17),
TAG_EXT = (1 << 17), TAG_EXT = (1 << 18),
TAG_FILENO = (1u << 31) TAG_FILENO = (1u << 31)
} LOG_TAG; } LOG_TAG;

View File

@ -44,7 +44,7 @@ void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp)
#ifdef ENABLE_U2F_EXTENSIONS #ifdef ENABLE_U2F_EXTENSIONS
rcode = extend_u2f(req, len); rcode = extend_u2f(req, len);
#endif #endif
if (rcode != U2F_SW_NO_ERROR) // If the extension didn't do anything... if (rcode != U2F_SW_NO_ERROR && rcode != U2F_SW_CONDITIONS_NOT_SATISFIED) // If the extension didn't do anything...
{ {
#ifdef ENABLE_U2F #ifdef ENABLE_U2F
switch(req->ins) switch(req->ins)
@ -196,6 +196,7 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c
if (control == U2F_AUTHENTICATE_CHECK) if (control == U2F_AUTHENTICATE_CHECK)
{ {
printf1(TAG_U2F, "CHECK-ONLY\r\n");
if (u2f_appid_eq(&req->kh, req->app) == 0) if (u2f_appid_eq(&req->kh, req->app) == 0)
{ {
return U2F_SW_CONDITIONS_NOT_SATISFIED; return U2F_SW_CONDITIONS_NOT_SATISFIED;
@ -213,7 +214,7 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c
) )
{ {
return U2F_SW_WRONG_PAYLOAD; return U2F_SW_WRONG_DATA;
} }
@ -224,7 +225,7 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c
} }
count = ctap_atomic_count(0); count = ctap_atomic_count(0);
hash[0] = (count >> 24) & 0xff; hash[0] = 0xff;
hash[1] = (count >> 16) & 0xff; hash[1] = (count >> 16) & 0xff;
hash[2] = (count >> 8) & 0xff; hash[2] = (count >> 8) & 0xff;
hash[3] = (count >> 0) & 0xff; hash[3] = (count >> 0) & 0xff;
@ -241,7 +242,7 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c
crypto_ecc256_sign(hash, 32, sig); crypto_ecc256_sign(hash, 32, sig);
u2f_response_writeback(&up,1); u2f_response_writeback(&up,1);
hash[0] = (count >> 24) & 0xff; hash[0] = 0xff;
hash[1] = (count >> 16) & 0xff; hash[1] = (count >> 16) & 0xff;
hash[2] = (count >> 8) & 0xff; hash[2] = (count >> 8) & 0xff;
hash[3] = (count >> 0) & 0xff; hash[3] = (count >> 0) & 0xff;

View File

@ -42,12 +42,11 @@
// Command status responses // Command status responses
#define U2F_SW_NO_ERROR 0x9000 #define U2F_SW_NO_ERROR 0x9000
#define U2F_SW_WRONG_DATA 0x6984
#define U2F_SW_CONDITIONS_NOT_SATISFIED 0x6985 #define U2F_SW_CONDITIONS_NOT_SATISFIED 0x6985
#define U2F_SW_INS_NOT_SUPPORTED 0x6d00 #define U2F_SW_INS_NOT_SUPPORTED 0x6d00
#define U2F_SW_WRONG_LENGTH 0x6700 #define U2F_SW_WRONG_LENGTH 0x6700
#define U2F_SW_CLASS_NOT_SUPPORTED 0x6E00 #define U2F_SW_CLASS_NOT_SUPPORTED 0x6E00
#define U2F_SW_WRONG_PAYLOAD 0x6a80 #define U2F_SW_WRONG_DATA 0x6a80
#define U2F_SW_INSUFFICIENT_MEMORY 0x9210 #define U2F_SW_INSUFFICIENT_MEMORY 0x9210
// Delay in milliseconds to wait for user input // Delay in milliseconds to wait for user input

37
in-docker-build.sh Executable file
View File

@ -0,0 +1,37 @@
#!/bin/bash -xe
version=${1:-master}
export PREFIX=/opt/gcc-arm-none-eabi-8-2018-q4-major/bin/
cd /solo/targets/stm32l432
git fetch --tags
git checkout ${version}
version=$(git describe)
make cbor
out_dir="/builds"
function build() {
part=${1}
variant=${2}
output=${3:-${part}}
what="${part}-${variant}"
make full-clean
make ${what}
out_hex="${what}-${version}.hex"
out_sha2="${what}-${version}.sha2"
mv ${output}.hex ${out_hex}
sha256sum ${out_hex} > ${out_sha2}
cp ${out_hex} ${out_sha2} ${out_dir}
}
build bootloader nonverifying
build bootloader verifying
build firmware hacker solo
build firmware secure solo

View File

@ -9,6 +9,25 @@ merge_hex=../../tools/solotool.py mergehex
.PHONY: all all-hacker all-locked debugboot-app debugboot-boot boot-sig-checking boot-no-sig build-release-locked build-release build-release build-hacker build-debugboot clean clean2 flash flash_dfu flashboot detach cbor test .PHONY: all all-hacker all-locked debugboot-app debugboot-boot boot-sig-checking boot-no-sig build-release-locked build-release build-release build-hacker build-debugboot clean clean2 flash flash_dfu flashboot detach cbor test
# The following are the main targets for reproducible builds.
# TODO: better explanation
firmware-hacker:
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=0 EXTRA_DEFINES='-DSOLO_HACKER -DFLASH_ROP=0'
firmware-secure:
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=0 EXTRA_DEFINES='-DUSE_SOLOKEYS_CERT -DFLASH_ROP=2'
bootloader-nonverifying:
$(MAKE) -f $(BOOTMAKE) -j8 bootloader.hex PREFIX=$(PREFIX) EXTRA_DEFINES='-DSOLO_HACKER' DEBUG=0
bootloader-verifying:
$(MAKE) -f $(BOOTMAKE) -j8 bootloader.hex PREFIX=$(PREFIX) DEBUG=0
full-clean: clean2
# The older targets, may be re-organised
all: all:
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=$(DEBUG) EXTRA_DEFINES='-DFLASH_ROP=1' $(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=$(DEBUG) EXTRA_DEFINES='-DFLASH_ROP=1'

View File

@ -4,7 +4,7 @@ include build/common.mk
SRC = src/main.c src/init.c src/redirect.c src/flash.c src/rng.c src/led.c src/device.c SRC = src/main.c src/init.c src/redirect.c src/flash.c src/rng.c src/led.c src/device.c
SRC += src/fifo.c src/crypto.c src/attestation.c SRC += src/fifo.c src/crypto.c src/attestation.c
SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c
SRC += $(wildcard lib/*.c) $(wildcard lib/usbd/*.c) SRC += $(DRIVER_LIBS) $(USB_LIB)
# FIDO2 lib # FIDO2 lib
SRC += ../../fido2/util.c ../../fido2/u2f.c ../../fido2/test_power.c SRC += ../../fido2/util.c ../../fido2/u2f.c ../../fido2/test_power.c

View File

@ -5,7 +5,7 @@ SRC = bootloader/main.c bootloader/bootloader.c
SRC += src/init.c src/redirect.c src/flash.c src/rng.c src/led.c src/device.c SRC += src/init.c src/redirect.c src/flash.c src/rng.c src/led.c src/device.c
SRC += src/fifo.c src/crypto.c src/attestation.c SRC += src/fifo.c src/crypto.c src/attestation.c
SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c
SRC += $(wildcard lib/*.c) $(wildcard lib/usbd/*.c) SRC += $(DRIVER_LIBS) $(USB_LIB)
# FIDO2 lib # FIDO2 lib
SRC += ../../fido2/util.c ../../fido2/u2f.c ../../fido2/extensions/extensions.c SRC += ../../fido2/util.c ../../fido2/u2f.c ../../fido2/extensions/extensions.c

View File

@ -3,6 +3,15 @@ CP=$(PREFIX)arm-none-eabi-objcopy
SZ=$(PREFIX)arm-none-eabi-size SZ=$(PREFIX)arm-none-eabi-size
AR=$(PREFIX)arm-none-eabi-ar AR=$(PREFIX)arm-none-eabi-ar
DRIVER_LIBS := lib/stm32l4xx_hal_pcd.c lib/stm32l4xx_hal_pcd_ex.c lib/stm32l4xx_ll_gpio.c \
lib/stm32l4xx_ll_rcc.c lib/stm32l4xx_ll_rng.c lib/stm32l4xx_ll_tim.c \
lib/stm32l4xx_ll_usb.c lib/stm32l4xx_ll_utils.c lib/stm32l4xx_ll_pwr.c \
lib/stm32l4xx_ll_usart.c
USB_LIB := lib/usbd/usbd_cdc.c lib/usbd/usbd_cdc_if.c lib/usbd/usbd_composite.c \
lib/usbd/usbd_conf.c lib/usbd/usbd_core.c lib/usbd/usbd_ioreq.c \
lib/usbd/usbd_ctlreq.c lib/usbd/usbd_desc.c lib/usbd/usbd_hid.c
VERSION:=$(shell git describe --abbrev=0 ) VERSION:=$(shell git describe --abbrev=0 )
VERSION_FULL:=$(shell git describe) VERSION_FULL:=$(shell git describe)
VERSION_MAJ:=$(shell python -c 'print("$(VERSION)".split(".")[0])') VERSION_MAJ:=$(shell python -c 'print("$(VERSION)".split(".")[0])')
@ -10,7 +19,7 @@ VERSION_MIN:=$(shell python -c 'print("$(VERSION)".split(".")[1])')
VERSION_PAT:=$(shell python -c 'print("$(VERSION)".split(".")[2])') VERSION_PAT:=$(shell python -c 'print("$(VERSION)".split(".")[2])')
VERSION_FLAGS= -DSOLO_VERSION_MAJ=$(VERSION_MAJ) -DSOLO_VERSION_MIN=$(VERSION_MIN) \ VERSION_FLAGS= -DSOLO_VERSION_MAJ=$(VERSION_MAJ) -DSOLO_VERSION_MIN=$(VERSION_MIN) \
-DSOLO_VERSION_PATCH=$(VERSION_PAT) -DVERSION=\"$(VERSION_FULL)\" -DSOLO_VERSION_PATCH=$(VERSION_PAT) -DSOLO_VERSION=\"$(VERSION_FULL)\"
_all: _all:
echo $(VERSION_FULL) echo $(VERSION_FULL)

View File

@ -166,6 +166,32 @@ uint8_t *USBD_HID_ManufacturerStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *l
*/ */
uint8_t *USBD_HID_SerialStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) uint8_t *USBD_HID_SerialStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{ {
USBD_GetString((uint8_t *)USBD_SERIAL_NUM, USBD_StrDesc, length); // Match the same alg as the DFU to make serial number
return USBD_StrDesc; volatile uint8_t * UUID = (volatile uint8_t *)0x1FFF7590;
const char hexdigit[] = "0123456789ABCDEF";
uint8_t uuid[6];
uint8_t uuid_str[13];
uint8_t c;
int i;
uuid_str[12] = 0;
uuid[0] = UUID[11];
uuid[1] = UUID[10] + UUID[2];
uuid[2] = UUID[9];
uuid[3] = UUID[8] + UUID[0];
uuid[4] = UUID[7];
uuid[5] = UUID[6];
// quick method to convert to hex string
for (i = 0; i < 6; i++)
{
c = (uuid[i]>>4) & 0x0f;
uuid_str[i * 2 + 0] = hexdigit[ c ];
c = (uuid[i]>>0) & 0x0f;
uuid_str[i * 2 + 1] = hexdigit[ c ];
}
USBD_GetString((uint8_t *)uuid_str, USBD_StrDesc, length);
return USBD_StrDesc;
} }

View File

@ -7,6 +7,50 @@
#include <stdint.h> #include <stdint.h>
#include "crypto.h" #include "crypto.h"
#ifdef USE_SOLOKEYS_CERT
const uint8_t attestation_cert_der[] =
"\x30\x82\x02\xe1\x30\x82\x02\x88\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"
"\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\x30"
"\x20\x17\x0d\x31\x38\x31\x31\x31\x31\x31\x32\x35\x32\x30\x30\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"
"\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\x22\x30\x20\x06\x03\x55\x04\x0b\x0c\x19\x41"
"\x75\x74\x68\x65\x6e\x74\x69\x63\x61\x74\x6f\x72\x20\x41\x74\x74\x65\x73\x74\x61"
"\x74\x69\x6f\x6e\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\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"
"\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\x0a\x06\x08"
"\x2a\x86\x48\xce\x3d\x04\x03\x02\x03\x47\x00\x30\x44\x02\x20\x71\x10\x46\x2c\xf5"
"\x16\x18\x97\x55\xca\x64\x50\x3b\x69\xb2\xdf\x17\x71\xab\xad\x8e\xc0\xd6\xa6\x07"
"\x3d\x66\x8a\x3b\xbb\xfe\x61\x02\x20\x1e\x82\xef\xeb\x5e\x4e\x3a\x00\x84\x64\xd2"
"\xf8\x84\xc3\x78\x35\x93\x63\x81\x2e\xbe\xa6\x12\x32\x6e\x29\x90\xc8\x91\x4b\x71"
"\x52"
;
#else
// For testing/development only // For testing/development only
const uint8_t attestation_cert_der[] = const uint8_t attestation_cert_der[] =
@ -50,9 +94,8 @@ const uint8_t attestation_cert_der[] =
"\xf3\x87\x61\x82\xd8\xcd\x48\xfc\x57" "\xf3\x87\x61\x82\xd8\xcd\x48\xfc\x57"
; ;
#endif
const uint16_t attestation_cert_der_size = sizeof(attestation_cert_der)-1; const uint16_t attestation_cert_der_size = sizeof(attestation_cert_der)-1;
const uint16_t attestation_key_size = 32;
const uint8_t attestation_key[] = "\x1b\x26\x26\xec\xc8\xf6\x9b\x0f\x69\xe3\x4f\xb2\x36\xd7\x64\x66\xba\x12\xac\x16\xc3\xab\x57\x50\xba\x06\x4e\x8b\x90\xe0\x24\x48";
const uint16_t attestation_key_size = sizeof(attestation_key)-1;

View File

@ -12,6 +12,10 @@
# Script for testing correctness of CTAP2/CTAP1 security token # Script for testing correctness of CTAP2/CTAP1 security token
from __future__ import print_function, absolute_import, unicode_literals from __future__ import print_function, absolute_import, unicode_literals
import sys, os, time
from random import randint
from binascii import hexlify
import array, struct, socket
from fido2.hid import CtapHidDevice, CTAPHID from fido2.hid import CtapHidDevice, CTAPHID
from fido2.client import Fido2Client, ClientError from fido2.client import Fido2Client, ClientError
@ -20,10 +24,10 @@ from fido2.ctap1 import CTAP1
from fido2.ctap2 import * from fido2.ctap2 import *
from fido2.cose import * from fido2.cose import *
from fido2.utils import Timeout, sha256 from fido2.utils import Timeout, sha256
import sys, os, time from fido2.attestation import Attestation
from random import randint
from binascii import hexlify from solo.fido2 import forceUDPBackend
import array, struct, socket
# Set up a FIDO 2 client using the origin https://example.com # Set up a FIDO 2 client using the origin https://example.com
@ -35,6 +39,11 @@ def ForceU2F(client, device):
client._do_get_assertion = client._ctap1_get_assertion client._do_get_assertion = client._ctap1_get_assertion
def VerifyAttestation(attest, data):
verifier = Attestation.for_type(attest.fmt)
verifier().verify(attest.att_statement, attest.auth_data, data.hash)
class Packet(object): class Packet(object):
def __init__(self, data): def __init__(self, data):
l = len(data) l = len(data)
@ -383,12 +392,16 @@ class Tester:
def test_u2f(self,): def test_u2f(self,):
chal = sha256(b"AAA") chal = sha256(b"AAA")
appid = sha256(b"BBB") appid = sha256(b"BBB")
lastc = 0
for i in range(0, 5): for i in range(0, 5):
reg = self.ctap1.register(chal, appid) reg = self.ctap1.register(chal, appid)
reg.verify(appid, chal) reg.verify(appid, chal)
auth = self.ctap1.authenticate(chal, appid, reg.key_handle) auth = self.ctap1.authenticate(chal, appid, reg.key_handle)
# check endianness # check endianness
assert auth.counter < 0x10000 if lastc:
assert (auth.counter - lastc) < 10
lastc = auth.counter
print(hex(lastc))
print("U2F reg + auth pass %d/5" % (i + 1)) print("U2F reg + auth pass %d/5" % (i + 1))
def test_fido2_simple(self, pin_token=None): def test_fido2_simple(self, pin_token=None):
@ -411,7 +424,7 @@ class Tester:
rp, user, challenge, pin=PIN, exclude_list=[] rp, user, challenge, pin=PIN, exclude_list=[]
) )
t2 = time.time() * 1000 t2 = time.time() * 1000
attest.verify(data.hash) VerifyAttestation(attest, data)
print("Register valid (%d ms)" % (t2 - t1)) print("Register valid (%d ms)" % (t2 - t1))
cred = attest.auth_data.credential_data cred = attest.auth_data.credential_data
@ -461,7 +474,7 @@ class Tester:
) )
print(attest.auth_data.counter) print(attest.auth_data.counter)
t2 = time.time() * 1000 t2 = time.time() * 1000
attest.verify(data.hash) VerifyAttestation(attest, data)
print("Register valid (%d ms)" % (t2 - t1)) print("Register valid (%d ms)" % (t2 - t1))
sys.stdout.flush() sys.stdout.flush()
@ -507,7 +520,7 @@ class Tester:
attest, data = self.client.make_credential( attest, data = self.client.make_credential(
rp, user, challenge, pin=PIN, exclude_list=[] rp, user, challenge, pin=PIN, exclude_list=[]
) )
attest.verify(data.hash) VerifyAttestation(attest, data)
# verify endian-ness is correct # verify endian-ness is correct
assert attest.auth_data.counter < 0x10000 assert attest.auth_data.counter < 0x10000
cred = attest.auth_data.credential_data cred = attest.auth_data.credential_data
@ -531,7 +544,7 @@ class Tester:
attest, data = self.client.make_credential( attest, data = self.client.make_credential(
rp, user, challenge, pin=PIN, exclude_list=exclude_list rp, user, challenge, pin=PIN, exclude_list=exclude_list
) )
attest.verify(data.hash) VerifyAttestation(attest, data)
cred = attest.auth_data.credential_data cred = attest.auth_data.credential_data
creds.append(cred) creds.append(cred)
print("PASS") print("PASS")
@ -661,7 +674,7 @@ class Tester:
rp, user0, challenge, pin=PIN, exclude_list=[], rk=True rp, user0, challenge, pin=PIN, exclude_list=[], rk=True
) )
t2 = time.time() * 1000 t2 = time.time() * 1000
attest.verify(data.hash) VerifyAttestation(attest, data)
creds.append(attest.auth_data.credential_data) creds.append(attest.auth_data.credential_data)
print("Register valid (%d ms)" % (t2 - t1)) print("Register valid (%d ms)" % (t2 - t1))
@ -683,7 +696,7 @@ class Tester:
rp, users[i], challenge, pin=PIN, exclude_list=[], rk=True rp, users[i], challenge, pin=PIN, exclude_list=[], rk=True
) )
t2 = time.time() * 1000 t2 = time.time() * 1000
attest.verify(data.hash) VerifyAttestation(attest, data)
print("Register valid (%d ms)" % (t2 - t1)) print("Register valid (%d ms)" % (t2 - t1))
creds.append(attest.auth_data.credential_data) creds.append(attest.auth_data.credential_data)
@ -706,7 +719,7 @@ class Tester:
rp, users[1], challenge, pin=PIN, exclude_list=[], rk=True rp, users[1], challenge, pin=PIN, exclude_list=[], rk=True
) )
t2 = time.time() * 1000 t2 = time.time() * 1000
attest.verify(data.hash) VerifyAttestation(attest, data)
creds = creds[:2] + creds[3:] + [attest.auth_data.credential_data] creds = creds[:2] + creds[3:] + [attest.auth_data.credential_data]
print("Register valid (%d ms)" % (t2 - t1)) print("Register valid (%d ms)" % (t2 - t1))
@ -771,7 +784,7 @@ class Tester:
rp, user, challenge, pin=PIN, exclude_list=[], rk=True rp, user, challenge, pin=PIN, exclude_list=[], rk=True
) )
t2 = time.time() * 1000 t2 = time.time() * 1000
attest.verify(data.hash) VerifyAttestation(attest, data)
creds = [attest.auth_data.credential_data] creds = [attest.auth_data.credential_data]
print("Register valid (%d ms)" % (t2 - t1)) print("Register valid (%d ms)" % (t2 - t1))
@ -821,6 +834,10 @@ def test_find_brute_force():
if __name__ == "__main__": if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "sim":
print("Using UDP backend.")
forceUDPBackend()
t = Tester() t = Tester()
t.find_device() t.find_device()
# t.test_hid() # t.test_hid()

View File

@ -1,5 +1,6 @@
ecdsa ecdsa
fido2
intelhex intelhex
pyserial pyserial
fido2
pyusb pyusb
wheel

View File

@ -201,6 +201,24 @@ class SoloClient:
return res.signature[1:] return res.signature[1:]
def exchange_fido2(self, cmd, addr=0, data=b"A" * 16):
chal = "B" * 32
req = SoloClient.format_request(cmd, addr, data)
assertions, client_data = self.client.get_assertion(
self.host, chal, [{"id": req, "type": "public-key"}]
)
if len(assertions) < 1:
raise RuntimeError("Device didn't respond to FIDO2 extended assertion")
res = assertions[0]
ret = res.signature[0]
if ret != CtapError.ERR.SUCCESS:
raise RuntimeError("Device returned non-success code %02x" % (ret,))
return res.signature[1:]
def bootloader_version(self,): def bootloader_version(self,):
data = self.exchange(SoloBootloader.version) data = self.exchange(SoloBootloader.version)
if len(data) > 2: if len(data) > 2:
@ -208,7 +226,7 @@ class SoloClient:
return data[0] return data[0]
def solo_version(self,): def solo_version(self,):
data = self.exchange_u2f(SoloExtension.version) data = self.exchange_fido2(SoloExtension.version)
return (data[0], data[1], data[2]) return (data[0], data[1], data[2])
def write_flash(self, addr, data): def write_flash(self, addr, data):
@ -585,6 +603,7 @@ def solo_main():
action="store_true", action="store_true",
help="Continuously dump random numbers generated from Solo.", help="Continuously dump random numbers generated from Solo.",
) )
parser.add_argument("--wink", action="store_true", help="HID Wink command.") parser.add_argument("--wink", action="store_true", help="HID Wink command.")
parser.add_argument( parser.add_argument(
"--reset", "--reset",
@ -596,6 +615,9 @@ def solo_main():
action="store_true", action="store_true",
help="Verify that the Solo firmware is from SoloKeys. Check firmware version.", help="Verify that the Solo firmware is from SoloKeys. Check firmware version.",
) )
parser.add_argument(
"--version", action="store_true", help="Check firmware version on Solo."
)
args = parser.parse_args() args = parser.parse_args()
p = SoloClient() p = SoloClient()
@ -627,6 +649,9 @@ def solo_main():
else: else:
print("Unknown fingerprint! ", cert.fingerprint(hashes.SHA256())) print("Unknown fingerprint! ", cert.fingerprint(hashes.SHA256()))
args.version = True
if args.version:
try: try:
v = p.solo_version() v = p.solo_version()
print("Version: ", v) print("Version: ", v)