Compare commits

...

17 Commits

Author SHA1 Message Date
2af747ddaa Merge pull request #229 from solokeys/fix-hmac-secret
Fix hmac secret
2019-07-27 12:49:30 -04:00
9ead11de8d Merge pull request #224 from solokeys/fault_tolerance
limit length of wLength
2019-07-27 12:47:28 -04:00
f17faca689 use correct size for auth_data for signature 2019-07-26 23:53:20 -04:00
ca66b6e43b verify signature for hmac-secret 2019-07-26 23:51:39 -04:00
1cd1b3c295 check attestation signature on all MC requests 2019-07-26 23:50:23 -04:00
df2cff2350 patch hmac final to use correct key 2019-07-26 23:49:55 -04:00
f5d50e001d test assertions work post reboot 2019-07-26 19:00:07 -04:00
235785b225 Bump stable version to 2.4.0 2019-07-17 23:42:56 +02:00
303c42901a limit length of wLength 2019-07-15 11:32:02 -04:00
df2f950e69 Merge pull request #217 from merlokk/extapdu
Extended length apdu, iso14443 chaining and select
2019-07-08 22:03:02 -04:00
10bf4242e1 fail with more import related info 2019-07-08 21:54:48 -04:00
9e95b0075c default no serial printing 2019-07-08 21:54:36 -04:00
ddbe31776c Merge pull request #220 from merlokk/obt_src
added text how to obtain source code
2019-07-08 21:22:48 -04:00
645ca6a5a0 add 3-space list 2019-07-08 18:12:28 +03:00
15fc39faed added text how to obtain source code 2019-07-08 17:58:57 +03:00
63ee003535 Merge pull request #202 from winksaville/patch-1
Update building.md
2019-07-05 10:29:40 -04:00
3f225f362f Update building.md
Adding `solo` as a prerequesite, it's required by `make build-hacker` to merge the hex files.
2019-05-29 15:11:18 -07:00
10 changed files with 109 additions and 27 deletions

View File

@ -1 +1 @@
2.3.0 2.4.0

View File

@ -1,22 +1,40 @@
# Building solo
To build, develop and debug the firmware for the STM32L432. This will work 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. 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)). 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. 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`, 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`. 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. To program your build, you'll need one of the following programs.
- [openocd](http://openocd.org) - [openocd](http://openocd.org)
- [stlink](https://github.com/texane/stlink) - [stlink](https://github.com/texane/stlink)
- [STM32CubeProg](https://www.st.com/en/development-tools/stm32cubeprog.html) - [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. 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 `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. 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!). 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/). 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 If your Solo device is already programmed (it flashes green when powered), we recommend
programming it using the Solo bootloader. 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). 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. 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 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". 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. 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. 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. 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. 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 If your Solo isn't locked, you can always reprogram it using a debugger connected directly
to the token. 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 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. someone from connecting a debugger to your token and stealing credentials.

View File

@ -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_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); 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_TRANSPORT_KEY ((uint8_t*)1)
#define CRYPTO_MASTER_KEY ((uint8_t*)0) #define CRYPTO_MASTER_KEY ((uint8_t*)0)

View File

@ -355,9 +355,9 @@ static int ctap_make_extensions(CTAP_extensions * ext, uint8_t * ext_encoder_buf
} }
// Generate credRandom // 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_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 // Decrypt saltEnc
crypto_aes256_init(shared_secret, NULL); 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_sha256_final(hashbuf);
crypto_ecc256_sign(hashbuf, 32, sigbuf); crypto_ecc256_sign(hashbuf, 32, sigbuf);
return ctap_encode_der_sig(sigbuf,sigder); return ctap_encode_der_sig(sigbuf,sigder);
} }
@ -1056,7 +1055,7 @@ uint8_t ctap_end_get_assertion(CborEncoder * map, CTAP_credentialDescriptor * cr
else else
#endif #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);
} }
{ {

View File

@ -342,6 +342,7 @@ static uint8_t USBD_HID_Setup (USBD_HandleTypeDef *pdev,
uint8_t *pbuf = NULL; uint8_t *pbuf = NULL;
uint16_t status_info = 0U; uint16_t status_info = 0U;
USBD_StatusTypeDef ret = USBD_OK; USBD_StatusTypeDef ret = USBD_OK;
req->wLength = req->wLength & 0x7f;
switch (req->bmRequest & USB_REQ_TYPE_MASK) switch (req->bmRequest & USB_REQ_TYPE_MASK)
{ {
@ -386,6 +387,7 @@ static uint8_t USBD_HID_Setup (USBD_HandleTypeDef *pdev,
break; break;
case USB_REQ_GET_DESCRIPTOR: case USB_REQ_GET_DESCRIPTOR:
req->wLength = req->wLength & 0x7f;
if(req->wValue >> 8 == HID_REPORT_DESC) if(req->wValue >> 8 == HID_REPORT_DESC)
{ {
len = MIN(HID_FIDO_REPORT_DESC_SIZE , req->wLength); len = MIN(HID_FIDO_REPORT_DESC_SIZE , req->wLength);

View File

@ -31,7 +31,7 @@
// #define DISABLE_CTAPHID_WINK // #define DISABLE_CTAPHID_WINK
// #define DISABLE_CTAPHID_CBOR // #define DISABLE_CTAPHID_CBOR
#define ENABLE_SERIAL_PRINTING // #define ENABLE_SERIAL_PRINTING
#if defined(SOLO_HACKER) #if defined(SOLO_HACKER)
#define SOLO_PRODUCT_NAME "Solo Hacker " SOLO_VERSION #define SOLO_PRODUCT_NAME "Solo Hacker " SOLO_VERSION

View File

@ -157,6 +157,11 @@ void crypto_sha256_hmac_final(uint8_t * key, uint32_t klen, uint8_t * hmac)
key = master_secret; key = master_secret;
klen = sizeof(master_secret)/2; klen = sizeof(master_secret)/2;
} }
else if (key == CRYPTO_TRANSPORT_KEY2)
{
key = transport_secret;
klen = 32;
}
if(klen > 64) if(klen > 64)

View File

@ -211,7 +211,7 @@ class FIDO2Tests(Tester):
assert "hmac-secret" in reg.auth_data.extensions assert "hmac-secret" in reg.auth_data.extensions
assert reg.auth_data.extensions["hmac-secret"] == True assert reg.auth_data.extensions["hmac-secret"] == True
reg = self.testMC( self.testMC(
"Send MC with fake extension set to true, expect SUCCESS", "Send MC with fake extension set to true, expect SUCCESS",
cdh, cdh,
rp, rp,
@ -278,6 +278,10 @@ class FIDO2Tests(Tester):
assert shannon_entropy(ext["hmac-secret"]) > 5.4 assert shannon_entropy(ext["hmac-secret"]) > 5.4
assert shannon_entropy(key) > 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,)) salt_enc, salt_auth = get_salt_params((salt3,))
auth = self.testGA( auth = self.testGA(
@ -743,6 +747,40 @@ class FIDO2Tests(Tester):
expectedError=CtapError.ERR.SUCCESS, 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"): with Test("Test auth_data is 37 bytes"):
assert len(prev_auth.auth_data) == 37 assert len(prev_auth.auth_data) == 37

View File

@ -2,6 +2,7 @@ import time, struct
from fido2.hid import CtapHidDevice from fido2.hid import CtapHidDevice
from fido2.client import Fido2Client from fido2.client import Fido2Client
from fido2.attestation import Attestation
from fido2.ctap1 import CTAP1 from fido2.ctap1 import CTAP1
from fido2.utils import Timeout from fido2.utils import Timeout
@ -73,14 +74,12 @@ class Tester:
dev = next(CtapHidDevice.list_devices(), None) dev = next(CtapHidDevice.list_devices(), None)
if not dev: if not dev:
try:
from fido2.pcsc import CtapPcscDevice from fido2.pcsc import CtapPcscDevice
print("--- NFC ---") print("--- NFC ---")
print(list(CtapPcscDevice.list_devices())) print(list(CtapPcscDevice.list_devices()))
dev = next(CtapPcscDevice.list_devices(), None) dev = next(CtapPcscDevice.list_devices(), None)
except (ModuleNotFoundError, ImportError):
print("One of NFC library is not installed properly.")
if not dev: if not dev:
raise RuntimeError("No FIDO device found") raise RuntimeError("No FIDO device found")
self.dev = dev self.dev = dev
@ -203,7 +202,18 @@ class Tester:
self.ctap.reset() self.ctap.reset()
def testMC(self, test, *args, **kwargs): 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): def testGA(self, test, *args, **kwargs):
return self.testFunc(self.ctap.get_assertion, test, *args, **kwargs) return self.testFunc(self.ctap.get_assertion, test, *args, **kwargs)

View File

@ -78,6 +78,16 @@ class U2FTests(Tester):
auth = self.authenticate(chal, appid, regs[i].key_handle) auth = self.authenticate(chal, appid, regs[i].key_handle)
auth.verify(appid, chal, regs[i].public_key) 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...") print("Check that all previous credentials are registered...")
for i in range(0, self.user_count): for i in range(0, self.user_count):
with Test("Check that previous credential %d is registered" % i): with Test("Check that previous credential %d is registered" % i):