Merge pull request #127 from solokeys/testing

Testing
This commit is contained in:
Conor Patrick 2019-03-02 01:12:48 -05:00 committed by GitHub
commit 9b356076c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 210 additions and 28 deletions

View File

@ -820,7 +820,7 @@ int ctap_filter_invalid_credentials(CTAP_getAssertion * GA)
printf1(TAG_GA, "RK %d is a rpId match!\r\n", i); printf1(TAG_GA, "RK %d is a rpId match!\r\n", i);
if (count == ALLOW_LIST_MAX_SIZE-1) if (count == ALLOW_LIST_MAX_SIZE-1)
{ {
printf2(TAG_ERR, "not enough ram allocated for matching RK's (%d)\r\n", count); printf2(TAG_ERR, "not enough ram allocated for matching RK's (%d). Skipping.\r\n", count);
break; break;
} }
GA->creds[count].type = PUB_KEY_CRED_PUB_KEY; GA->creds[count].type = PUB_KEY_CRED_PUB_KEY;

View File

@ -22,6 +22,11 @@
#include "log.h" #include "log.h"
#include "ctaphid.h" #include "ctaphid.h"
#define RK_NUM 50
struct ResidentKeyStore {
CTAP_residentKey rks[RK_NUM];
} RK_STORE;
void authenticator_initialize(); void authenticator_initialize();
@ -251,6 +256,7 @@ int ctap_generate_rng(uint8_t * dst, size_t num)
const char * state_file = "authenticator_state.bin"; const char * state_file = "authenticator_state.bin";
const char * backup_file = "authenticator_state2.bin"; const char * backup_file = "authenticator_state2.bin";
const char * rk_file = "resident_keys.bin";
void authenticator_read_state(AuthenticatorState * state) void authenticator_read_state(AuthenticatorState * state)
{ {
@ -370,6 +376,24 @@ int authenticator_is_backup_initialized()
/*}*/ /*}*/
static void sync_rk()
{
FILE * f = fopen(rk_file, "wb+");
if (f== NULL)
{
perror("fopen");
exit(1);
}
int ret = fwrite(&RK_STORE, 1, sizeof(RK_STORE), f);
fclose(f);
if (ret != sizeof(RK_STORE))
{
perror("fwrite");
exit(1);
}
}
void authenticator_initialize() void authenticator_initialize()
{ {
uint8_t header[16]; uint8_t header[16];
@ -393,6 +417,22 @@ void authenticator_initialize()
perror("fwrite"); perror("fwrite");
exit(1); exit(1);
} }
// resident_keys
f = fopen(rk_file, "rb");
if (f== NULL)
{
perror("fopen");
exit(1);
}
ret = fread(&RK_STORE, 1, sizeof(RK_STORE), f);
fclose(f);
if(ret != sizeof(RK_STORE))
{
perror("fwrite");
exit(1);
}
} }
else else
{ {
@ -431,6 +471,12 @@ void authenticator_initialize()
exit(1); exit(1);
} }
// resident_keys
memset(&RK_STORE,0xff,sizeof(RK_STORE));
sync_rk();
} }
} }
@ -439,26 +485,52 @@ void device_manage()
} }
void ctap_reset_rk() void ctap_reset_rk()
{ {
memset(&RK_STORE,0xff,sizeof(RK_STORE));
sync_rk();
} }
uint32_t ctap_rk_size() uint32_t ctap_rk_size()
{ {
printf("Warning: rk not implemented\n"); return RK_NUM;
return 0;
} }
void ctap_store_rk(int index,CTAP_residentKey * rk)
void ctap_store_rk(int index, CTAP_residentKey * rk)
{ {
printf("Warning: rk not implemented\n"); if (index < RK_NUM)
{
memmove(RK_STORE.rks + index, rk, sizeof(CTAP_residentKey));
sync_rk();
}
else
{
printf1(TAG_ERR,"Out of bounds for store_rk\r\n");
}
} }
void ctap_load_rk(int index,CTAP_residentKey * rk)
void ctap_load_rk(int index, CTAP_residentKey * rk)
{ {
printf("Warning: rk not implemented\n"); memmove(rk, RK_STORE.rks + index, sizeof(CTAP_residentKey));
} }
void ctap_overwrite_rk(int index,CTAP_residentKey * rk)
void ctap_overwrite_rk(int index, CTAP_residentKey * rk)
{ {
printf("Warning: rk not implemented\n"); if (index < RK_NUM)
{
memmove(RK_STORE.rks + index, rk, sizeof(CTAP_residentKey));
sync_rk();
}
else
{
printf1(TAG_ERR,"Out of bounds for store_rk\r\n");
}
} }
void device_wink() void device_wink()

View File

@ -596,7 +596,7 @@ void ctap_overwrite_rk(int index,CTAP_residentKey * rk)
memmove(tmppage + (sizeof(CTAP_residentKey) * index) % PAGE_SIZE, rk, sizeof(CTAP_residentKey)); memmove(tmppage + (sizeof(CTAP_residentKey) * index) % PAGE_SIZE, rk, sizeof(CTAP_residentKey));
flash_erase_page(page); flash_erase_page(page);
flash_write(flash_addr(page), tmppage, ((sizeof(CTAP_residentKey) * (index + 1)) % PAGE_SIZE) ); flash_write(flash_addr(page), tmppage, PAGE_SIZE);
} }
else else
{ {

View File

@ -20,7 +20,7 @@ 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
from fido2.ctap import CtapError from fido2.ctap import CtapError
from fido2.ctap1 import CTAP1 from fido2.ctap1 import CTAP1, ApduError, APDU
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
@ -61,6 +61,7 @@ class Tester:
def __init__(self,): def __init__(self,):
self.origin = "https://examplo.org" self.origin = "https://examplo.org"
self.host = "examplo.org" self.host = "examplo.org"
self.user_count = 10
def find_device(self,): def find_device(self,):
print(list(CtapHidDevice.list_devices())) print(list(CtapHidDevice.list_devices()))
@ -75,6 +76,9 @@ class Tester:
# consume timeout error # consume timeout error
# cmd,resp = self.recv_raw() # cmd,resp = self.recv_raw()
def set_user_count(self, count):
self.user_count = count
def send_data(self, cmd, data): def send_data(self, cmd, data):
if type(data) != type(b""): if type(data) != type(b""):
data = struct.pack("%dB" % len(data), *[ord(x) for x in data]) data = struct.pack("%dB" % len(data), *[ord(x) for x in data])
@ -393,16 +397,101 @@ class Tester:
chal = sha256(b"AAA") chal = sha256(b"AAA")
appid = sha256(b"BBB") appid = sha256(b"BBB")
lastc = 0 lastc = 0
for i in range(0, 5):
regs = []
print("Check version")
assert self.ctap1.get_version() == "U2F_V2"
print("Pass")
print("Check bad INS")
try:
res = self.ctap1.send_apdu(0, 0, 0, 0, b"")
except ApduError as e:
assert e.code == 0x6D00
print("Pass")
print("Check bad CLA")
try:
res = self.ctap1.send_apdu(1, CTAP1.INS.VERSION, 0, 0, b"abc")
except ApduError as e:
assert e.code == 0x6E00
print("Pass")
for i in range(0, self.user_count):
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)
auth.verify(appid, chal, reg.public_key)
regs.append(reg)
# check endianness # check endianness
if lastc: if lastc:
assert (auth.counter - lastc) < 10 assert (auth.counter - lastc) < 10
lastc = auth.counter lastc = auth.counter
print(hex(lastc)) if lastc > 0x100000:
print("U2F reg + auth pass %d/5" % (i + 1)) print("WARNING: counter is unusually high: %04x" % lastc)
assert 0
print(
"U2F reg + auth pass %d/%d (count: %02x)"
% (i + 1, self.user_count, lastc)
)
print("Checking previous registrations...")
for i in range(0, self.user_count):
auth = self.ctap1.authenticate(chal, appid, regs[i].key_handle)
auth.verify(appid, chal, regs[i].public_key)
print("Auth pass %d/%d" % (i + 1, self.user_count))
print("Check that all previous credentials are registered...")
for i in range(0, self.user_count):
try:
auth = self.ctap1.authenticate(
chal, appid, regs[i].key_handle, check_only=True
)
except ApduError as e:
# Indicates that key handle is registered
assert e.code == APDU.USE_NOT_SATISFIED
print("Check pass %d/%d" % (i + 1, self.user_count))
print("Check an incorrect key handle is not registered")
kh = bytearray(regs[0].key_handle)
kh[0] = kh[0] ^ (0x40)
try:
self.ctap1.authenticate(chal, appid, kh, check_only=True)
assert 0
except ApduError as e:
assert e.code == APDU.WRONG_DATA
print("Pass")
print("Try to sign with incorrect key handle")
try:
self.ctap1.authenticate(chal, appid, kh)
assert 0
except ApduError as e:
assert e.code == APDU.WRONG_DATA
print("Pass")
print("Try to sign using an incorrect keyhandle length")
try:
kh = regs[0].key_handle
self.ctap1.authenticate(chal, appid, kh[: len(kh) // 2])
assert 0
except ApduError as e:
assert e.code == APDU.WRONG_DATA
print("Pass")
print("Try to sign using an incorrect appid")
badid = bytearray(appid)
badid[0] = badid[0] ^ (0x40)
try:
auth = self.ctap1.authenticate(chal, badid, regs[0].key_handle)
assert 0
except ApduError as e:
assert e.code == APDU.WRONG_DATA
print("Pass")
def test_fido2_simple(self, pin_token=None): def test_fido2_simple(self, pin_token=None):
creds = [] creds = []
@ -515,8 +604,8 @@ class Tester:
exclude_list.append({"id": fake_id2, "type": "public-key"}) exclude_list.append({"id": fake_id2, "type": "public-key"})
# test make credential # test make credential
print("make 3 credentials") print("make %d credentials" % self.user_count)
for i in range(0, 3): for i in range(0, self.user_count):
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=[]
) )
@ -657,10 +746,10 @@ class Tester:
def test_rk(self,): def test_rk(self,):
creds = [] creds = []
rp = {"id": self.host, "name": "ExaRP"} rp = {"id": self.host, "name": "ExaRP"}
user0 = {"id": b"first one", "name": "single User"}
users = [ users = [
{"id": b"user" + os.urandom(16), "name": "AB User"} for i in range(0, 2) {"id": b"user" + os.urandom(16), "name": "Username%d" % i}
for i in range(0, self.user_count)
] ]
challenge = "Y2hhbGxlbmdl" challenge = "Y2hhbGxlbmdl"
PIN = None PIN = None
@ -671,7 +760,7 @@ class Tester:
print("registering 1 user with RK") print("registering 1 user with RK")
t1 = time.time() * 1000 t1 = time.time() * 1000
attest, data = self.client.make_credential( attest, data = self.client.make_credential(
rp, user0, 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
VerifyAttestation(attest, data) VerifyAttestation(attest, data)
@ -690,7 +779,7 @@ class Tester:
print(assertions[0], client_data) print(assertions[0], client_data)
print("registering %d users with RK" % len(users)) print("registering %d users with RK" % len(users))
for i in range(0, len(users)): for i in range(0, len(users) - 1):
t1 = time.time() * 1000 t1 = time.time() * 1000
attest, data = self.client.make_credential( attest, data = self.client.make_credential(
rp, users[i], challenge, pin=PIN, exclude_list=[], rk=True rp, users[i], challenge, pin=PIN, exclude_list=[], rk=True
@ -707,6 +796,9 @@ class Tester:
) )
t2 = time.time() * 1000 t2 = time.time() * 1000
print("Got %d assertions for %d users" % (len(assertions), len(users)))
assert len(assertions) == len(users)
for x, y in zip(assertions, creds): for x, y in zip(assertions, creds):
x.verify(client_data.hash, y.public_key) x.verify(client_data.hash, y.public_key)
@ -728,7 +820,8 @@ class Tester:
rp["id"], challenge, pin=PIN rp["id"], challenge, pin=PIN
) )
t2 = time.time() * 1000 t2 = time.time() * 1000
assert len(assertions) == len(users) + 1 print("Assertions: %d, users: %d" % (len(assertions), len(users)))
assert len(assertions) == len(users)
for x, y in zip(assertions, creds): for x, y in zip(assertions, creds):
x.verify(client_data.hash, y.public_key) x.verify(client_data.hash, y.public_key)
@ -834,18 +927,35 @@ def test_find_brute_force():
if __name__ == "__main__": if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "sim": if len(sys.argv) < 2:
print("Usage: %s [sim] <[u2f]|[fido2]|[rk]|[hid]|[ping]>")
sys.exit(0)
if "sim" in sys.argv:
print("Using UDP backend.") print("Using UDP backend.")
force_udp_backend() force_udp_backend()
t = Tester() t = Tester()
t.find_device() t.find_device()
# t.test_hid() t.set_user_count(15)
# t.test_long_ping()
# t.test_fido2() if "u2f" in sys.argv:
t.test_u2f() t.test_u2f()
# t.test_rk()
if "fido2" in sys.argv:
t.test_fido2()
t.test_fido2_simple()
if "rk" in sys.argv:
t.test_rk()
if "ping" in sys.argv:
t.test_long_ping()
# hid tests are a bit invasive and should be done last
if "hid" in sys.argv:
t.test_hid()
# t.test_responses() # t.test_responses()
# test_find_brute_force() # test_find_brute_force()
# t.test_fido2_simple()
# t.test_fido2_brute_force() # t.test_fido2_brute_force()