Compare commits

...

6 Commits
1.1.0 ... 1.1.1

9 changed files with 57 additions and 23 deletions

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

View File

@ -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
View File

@ -0,0 +1 @@
1.1.1

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

@ -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;

View File

@ -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;
} }

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

View File

@ -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)

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)
@ -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()