Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
9f60caf9c1 | |||
46ada5a8b9 | |||
54241ecd42 | |||
e537d00173 | |||
f6ff3c1b87 | |||
afd3218358 |
17
CHANGELOG.md
Normal file
17
CHANGELOG.md
Normal 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
|
2
Makefile
2
Makefile
@ -80,7 +80,7 @@ docker-build:
|
|||||||
docker build -t $(DOCKER_IMAGE) .
|
docker build -t $(DOCKER_IMAGE) .
|
||||||
docker run --rm -v "$(CURDIR)/builds:/builds" \
|
docker run --rm -v "$(CURDIR)/builds:/builds" \
|
||||||
-v "$(CURDIR)/in-docker-build.sh:/in-docker-build.sh" \
|
-v "$(CURDIR)/in-docker-build.sh:/in-docker-build.sh" \
|
||||||
$(DOCKER_IMAGE) /in-docker-build.sh $(SOLO_VERSIONISH)
|
$(DOCKER_IMAGE) "./in-docker-build.sh" $(SOLO_VERSIONISH)
|
||||||
|
|
||||||
CPPCHECK_FLAGS=--quiet --error-exitcode=2
|
CPPCHECK_FLAGS=--quiet --error-exitcode=2
|
||||||
|
|
||||||
|
1
STABLE_VERSION
Normal file
1
STABLE_VERSION
Normal file
@ -0,0 +1 @@
|
|||||||
|
1.1.1
|
@ -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"
|
||||||
```
|
```
|
||||||
|
@ -118,7 +118,7 @@ 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 auth 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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -5,7 +5,7 @@ version=${1:-master}
|
|||||||
export PREFIX=/opt/gcc-arm-none-eabi-8-2018-q4-major/bin/
|
export PREFIX=/opt/gcc-arm-none-eabi-8-2018-q4-major/bin/
|
||||||
|
|
||||||
cd /solo/targets/stm32l432
|
cd /solo/targets/stm32l432
|
||||||
git fetch
|
git fetch --tags
|
||||||
git checkout ${version}
|
git checkout ${version}
|
||||||
version=$(git describe)
|
version=$(git describe)
|
||||||
|
|
||||||
|
@ -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)
|
||||||
@ -415,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
|
||||||
@ -465,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()
|
||||||
|
|
||||||
@ -511,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
|
||||||
@ -535,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")
|
||||||
@ -665,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))
|
||||||
|
|
||||||
@ -687,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)
|
||||||
@ -710,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))
|
||||||
|
|
||||||
@ -775,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))
|
||||||
|
|
||||||
@ -825,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()
|
||||||
|
Reference in New Issue
Block a user