Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
2af747ddaa | |||
9ead11de8d | |||
f17faca689 | |||
ca66b6e43b | |||
1cd1b3c295 | |||
df2cff2350 | |||
f5d50e001d | |||
235785b225 | |||
303c42901a | |||
df2f950e69 | |||
10bf4242e1 | |||
9e95b0075c | |||
ddbe31776c | |||
645ca6a5a0 | |||
15fc39faed | |||
63ee003535 | |||
3f225f362f |
@ -1 +1 @@
|
||||
2.3.0
|
||||
2.4.0
|
||||
|
@ -1,22 +1,40 @@
|
||||
# Building solo
|
||||
|
||||
To build, develop and debug the firmware for the STM32L432. This will work
|
||||
for Solo Hacker, the Nucleo development board, or your own homemade Solo.
|
||||
|
||||
There exists a development board [NUCLEO-L432KC](https://www.st.com/en/evaluation-tools/nucleo-l432kc.html) you can use; The board does contain a debugger, so all you need is a USB cable (and some [udev](/udev) [rules](https://rust-embedded.github.io/book/intro/install/linux.html#udev-rules)).
|
||||
|
||||
# Prerequisites
|
||||
## Prerequisites
|
||||
|
||||
Install the [latest ARM compiler toolchain](https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads) for your system. We recommend getting the latest compilers from ARM.
|
||||
|
||||
You can also install the ARM toolchain using a package manager like `apt-get` or `pacman`,
|
||||
but be warned they might be out of date. Typically it will be called `gcc-arm-none-eabi binutils-arm-none-eabi`.
|
||||
|
||||
Install `solo-python` usually with `pip3 install solo-python`. The `solo` python application may also be used for [programming](#programming).
|
||||
|
||||
To program your build, you'll need one of the following programs.
|
||||
|
||||
- [openocd](http://openocd.org)
|
||||
- [stlink](https://github.com/texane/stlink)
|
||||
- [STM32CubeProg](https://www.st.com/en/development-tools/stm32cubeprog.html)
|
||||
- [openocd](http://openocd.org)
|
||||
- [stlink](https://github.com/texane/stlink)
|
||||
- [STM32CubeProg](https://www.st.com/en/development-tools/stm32cubeprog.html)
|
||||
|
||||
# Compilation
|
||||
## Obtain source code and solo tool
|
||||
|
||||
Source code can be downloaded from:
|
||||
|
||||
- [github releases list](https://github.com/solokeys/solo/releases)
|
||||
- [github repository](https://github.com/solokeys/solo)
|
||||
|
||||
**solo** tool can be downloaded from:
|
||||
|
||||
- from python programs [repository](https://pypi.org/project/solo-python/) `pip install solo-python`
|
||||
- from installing prerequisites `pip3 install -r tools/requirements.txt`
|
||||
- github repository: [repository](https://github.com/solokeys/solo-python)
|
||||
- installation python enviroment witn command `make venv` from root directory of source code
|
||||
|
||||
## Compilation
|
||||
|
||||
Enter the `stm32l4xx` target directory.
|
||||
|
||||
@ -80,8 +98,7 @@ make build-release-locked
|
||||
|
||||
Programming `all.hex` will cause the device to permanently lock itself.
|
||||
|
||||
|
||||
# Programming
|
||||
## Programming
|
||||
|
||||
It's recommended to test a debug/hacker build first to make sure Solo is working as expected.
|
||||
Then you can switch to a locked down build, which cannot be reprogrammed as easily (or not at all!).
|
||||
@ -95,7 +112,7 @@ pip3 install -r tools/requirements.txt
|
||||
|
||||
If you're on Windows, you must also install [libusb](https://sourceforge.net/projects/libusb-win32/files/libusb-win32-releases/1.2.6.0/).
|
||||
|
||||
## Pre-programmed Solo Hacker
|
||||
### Pre-programmed Solo Hacker
|
||||
|
||||
If your Solo device is already programmed (it flashes green when powered), we recommend
|
||||
programming it using the Solo bootloader.
|
||||
@ -118,7 +135,7 @@ If something bad happens, you can always boot the Solo bootloader by doing the f
|
||||
If you hold the button for an additional 5 seconds, it will boot to the ST DFU (device firmware update).
|
||||
Don't use the ST DFU unless you know what you're doing.
|
||||
|
||||
## ST USB DFU
|
||||
### ST USB DFU
|
||||
|
||||
If your Solo has never been programmed, it will boot the ST USB DFU. The LED is turned
|
||||
off and it enumerates as "STM BOOTLOADER".
|
||||
@ -136,7 +153,7 @@ Make sure to program `all.hex`, as this contains both the bootloader and the Sol
|
||||
|
||||
If all goes well, you should see a slow-flashing green light.
|
||||
|
||||
## Solo Hacker vs Solo
|
||||
### Solo Hacker vs Solo
|
||||
|
||||
A Solo hacker device doesn't need to be in bootloader mode to be programmed, it will automatically switch.
|
||||
|
||||
@ -144,7 +161,7 @@ Solo (locked) needs the button to be held down when plugged in to boot to the bo
|
||||
|
||||
A locked Solo will only accept signed updates.
|
||||
|
||||
## Signed updates
|
||||
### Signed updates
|
||||
|
||||
If this is not a device with a hacker build, you can only program signed updates.
|
||||
|
||||
@ -162,7 +179,7 @@ solo sign /path/to/signing-key.pem /path/to/solo.hex /output-path/to/firmware.js
|
||||
If your Solo isn't locked, you can always reprogram it using a debugger connected directly
|
||||
to the token.
|
||||
|
||||
# Permanently locking the device
|
||||
## Permanently locking the device
|
||||
|
||||
If you plan to be using your Solo for real, you should lock it permanently. This prevents
|
||||
someone from connecting a debugger to your token and stealing credentials.
|
||||
|
@ -38,6 +38,7 @@ void generate_private_key(uint8_t * data, int len, uint8_t * data2, int len2, ui
|
||||
void crypto_ecc256_make_key_pair(uint8_t * pubkey, uint8_t * privkey);
|
||||
void crypto_ecc256_shared_secret(const uint8_t * pubkey, const uint8_t * privkey, uint8_t * shared_secret);
|
||||
|
||||
#define CRYPTO_TRANSPORT_KEY2 ((uint8_t*)2)
|
||||
#define CRYPTO_TRANSPORT_KEY ((uint8_t*)1)
|
||||
#define CRYPTO_MASTER_KEY ((uint8_t*)0)
|
||||
|
||||
|
@ -355,9 +355,9 @@ static int ctap_make_extensions(CTAP_extensions * ext, uint8_t * ext_encoder_buf
|
||||
}
|
||||
|
||||
// Generate credRandom
|
||||
crypto_sha256_hmac_init(CRYPTO_TRANSPORT_KEY, 0, credRandom);
|
||||
crypto_sha256_hmac_init(CRYPTO_TRANSPORT_KEY2, 0, credRandom);
|
||||
crypto_sha256_update((uint8_t*)&ext->hmac_secret.credential->id, sizeof(CredentialId));
|
||||
crypto_sha256_hmac_final(CRYPTO_TRANSPORT_KEY, 0, credRandom);
|
||||
crypto_sha256_hmac_final(CRYPTO_TRANSPORT_KEY2, 0, credRandom);
|
||||
|
||||
// Decrypt saltEnc
|
||||
crypto_aes256_init(shared_secret, NULL);
|
||||
@ -605,7 +605,6 @@ int ctap_calculate_signature(uint8_t * data, int datalen, uint8_t * clientDataHa
|
||||
crypto_sha256_final(hashbuf);
|
||||
|
||||
crypto_ecc256_sign(hashbuf, 32, sigbuf);
|
||||
|
||||
return ctap_encode_der_sig(sigbuf,sigder);
|
||||
}
|
||||
|
||||
@ -1056,7 +1055,7 @@ uint8_t ctap_end_get_assertion(CborEncoder * map, CTAP_credentialDescriptor * cr
|
||||
else
|
||||
#endif
|
||||
{
|
||||
sigder_sz = ctap_calculate_signature(auth_data_buf, sizeof(CTAP_authDataHeader), clientDataHash, auth_data_buf, sigbuf, sigder);
|
||||
sigder_sz = ctap_calculate_signature(auth_data_buf, auth_data_buf_sz, clientDataHash, auth_data_buf, sigbuf, sigder);
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -342,6 +342,7 @@ static uint8_t USBD_HID_Setup (USBD_HandleTypeDef *pdev,
|
||||
uint8_t *pbuf = NULL;
|
||||
uint16_t status_info = 0U;
|
||||
USBD_StatusTypeDef ret = USBD_OK;
|
||||
req->wLength = req->wLength & 0x7f;
|
||||
|
||||
switch (req->bmRequest & USB_REQ_TYPE_MASK)
|
||||
{
|
||||
@ -386,6 +387,7 @@ static uint8_t USBD_HID_Setup (USBD_HandleTypeDef *pdev,
|
||||
break;
|
||||
|
||||
case USB_REQ_GET_DESCRIPTOR:
|
||||
req->wLength = req->wLength & 0x7f;
|
||||
if(req->wValue >> 8 == HID_REPORT_DESC)
|
||||
{
|
||||
len = MIN(HID_FIDO_REPORT_DESC_SIZE , req->wLength);
|
||||
|
@ -31,7 +31,7 @@
|
||||
// #define DISABLE_CTAPHID_WINK
|
||||
// #define DISABLE_CTAPHID_CBOR
|
||||
|
||||
#define ENABLE_SERIAL_PRINTING
|
||||
// #define ENABLE_SERIAL_PRINTING
|
||||
|
||||
#if defined(SOLO_HACKER)
|
||||
#define SOLO_PRODUCT_NAME "Solo Hacker " SOLO_VERSION
|
||||
|
@ -157,6 +157,11 @@ void crypto_sha256_hmac_final(uint8_t * key, uint32_t klen, uint8_t * hmac)
|
||||
key = master_secret;
|
||||
klen = sizeof(master_secret)/2;
|
||||
}
|
||||
else if (key == CRYPTO_TRANSPORT_KEY2)
|
||||
{
|
||||
key = transport_secret;
|
||||
klen = 32;
|
||||
}
|
||||
|
||||
|
||||
if(klen > 64)
|
||||
|
@ -211,7 +211,7 @@ class FIDO2Tests(Tester):
|
||||
assert "hmac-secret" in reg.auth_data.extensions
|
||||
assert reg.auth_data.extensions["hmac-secret"] == True
|
||||
|
||||
reg = self.testMC(
|
||||
self.testMC(
|
||||
"Send MC with fake extension set to true, expect SUCCESS",
|
||||
cdh,
|
||||
rp,
|
||||
@ -278,6 +278,10 @@ class FIDO2Tests(Tester):
|
||||
assert shannon_entropy(ext["hmac-secret"]) > 5.4
|
||||
assert shannon_entropy(key) > 5.4
|
||||
|
||||
with Test("Check that the assertion is valid"):
|
||||
credential_data = AttestedCredentialData(reg.auth_data.credential_data)
|
||||
auth.verify(cdh, credential_data.public_key)
|
||||
|
||||
salt_enc, salt_auth = get_salt_params((salt3,))
|
||||
|
||||
auth = self.testGA(
|
||||
@ -743,6 +747,40 @@ class FIDO2Tests(Tester):
|
||||
expectedError=CtapError.ERR.SUCCESS,
|
||||
)
|
||||
|
||||
with Test("Check assertion is correct"):
|
||||
credential_data = AttestedCredentialData(prev_reg.auth_data.credential_data)
|
||||
prev_auth.verify(cdh, credential_data.public_key)
|
||||
assert (
|
||||
prev_auth.credential["id"]
|
||||
== prev_reg.auth_data.credential_data.credential_id
|
||||
)
|
||||
|
||||
self.reboot()
|
||||
|
||||
prev_auth = self.testGA(
|
||||
"Send GA request after reboot, expect success",
|
||||
rp["id"],
|
||||
cdh,
|
||||
allow_list,
|
||||
expectedError=CtapError.ERR.SUCCESS,
|
||||
)
|
||||
|
||||
with Test("Check assertion is correct"):
|
||||
credential_data = AttestedCredentialData(prev_reg.auth_data.credential_data)
|
||||
prev_auth.verify(cdh, credential_data.public_key)
|
||||
assert (
|
||||
prev_auth.credential["id"]
|
||||
== prev_reg.auth_data.credential_data.credential_id
|
||||
)
|
||||
|
||||
prev_auth = self.testGA(
|
||||
"Send GA request, expect success",
|
||||
rp["id"],
|
||||
cdh,
|
||||
allow_list,
|
||||
expectedError=CtapError.ERR.SUCCESS,
|
||||
)
|
||||
|
||||
with Test("Test auth_data is 37 bytes"):
|
||||
assert len(prev_auth.auth_data) == 37
|
||||
|
||||
|
@ -2,6 +2,7 @@ import time, struct
|
||||
|
||||
from fido2.hid import CtapHidDevice
|
||||
from fido2.client import Fido2Client
|
||||
from fido2.attestation import Attestation
|
||||
from fido2.ctap1 import CTAP1
|
||||
from fido2.utils import Timeout
|
||||
|
||||
@ -73,14 +74,12 @@ class Tester:
|
||||
dev = next(CtapHidDevice.list_devices(), None)
|
||||
|
||||
if not dev:
|
||||
try:
|
||||
from fido2.pcsc import CtapPcscDevice
|
||||
from fido2.pcsc import CtapPcscDevice
|
||||
|
||||
print("--- NFC ---")
|
||||
print(list(CtapPcscDevice.list_devices()))
|
||||
dev = next(CtapPcscDevice.list_devices(), None)
|
||||
|
||||
print("--- NFC ---")
|
||||
print(list(CtapPcscDevice.list_devices()))
|
||||
dev = next(CtapPcscDevice.list_devices(), None)
|
||||
except (ModuleNotFoundError, ImportError):
|
||||
print("One of NFC library is not installed properly.")
|
||||
if not dev:
|
||||
raise RuntimeError("No FIDO device found")
|
||||
self.dev = dev
|
||||
@ -203,7 +202,18 @@ class Tester:
|
||||
self.ctap.reset()
|
||||
|
||||
def testMC(self, test, *args, **kwargs):
|
||||
return self.testFunc(self.ctap.make_credential, test, *args, **kwargs)
|
||||
attestation_object = self.testFunc(
|
||||
self.ctap.make_credential, test, *args, **kwargs
|
||||
)
|
||||
if attestation_object:
|
||||
verifier = Attestation.for_type(attestation_object.fmt)
|
||||
client_data = args[0]
|
||||
verifier().verify(
|
||||
attestation_object.att_statement,
|
||||
attestation_object.auth_data,
|
||||
client_data,
|
||||
)
|
||||
return attestation_object
|
||||
|
||||
def testGA(self, test, *args, **kwargs):
|
||||
return self.testFunc(self.ctap.get_assertion, test, *args, **kwargs)
|
||||
|
@ -78,6 +78,16 @@ class U2FTests(Tester):
|
||||
auth = self.authenticate(chal, appid, regs[i].key_handle)
|
||||
auth.verify(appid, chal, regs[i].public_key)
|
||||
|
||||
self.reboot()
|
||||
|
||||
for i in range(0, self.user_count):
|
||||
with Test(
|
||||
"Post reboot, Checking previous registration %d/%d"
|
||||
% (i + 1, self.user_count)
|
||||
):
|
||||
auth = self.authenticate(chal, appid, regs[i].key_handle)
|
||||
auth.verify(appid, chal, regs[i].public_key)
|
||||
|
||||
print("Check that all previous credentials are registered...")
|
||||
for i in range(0, self.user_count):
|
||||
with Test("Check that previous credential %d is registered" % i):
|
||||
|
Reference in New Issue
Block a user