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);
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;
}
GA->creds[count].type = PUB_KEY_CRED_PUB_KEY;

View File

@ -22,6 +22,11 @@
#include "log.h"
#include "ctaphid.h"
#define RK_NUM 50
struct ResidentKeyStore {
CTAP_residentKey rks[RK_NUM];
} RK_STORE;
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 * backup_file = "authenticator_state2.bin";
const char * rk_file = "resident_keys.bin";
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()
{
uint8_t header[16];
@ -393,6 +417,22 @@ void authenticator_initialize()
perror("fwrite");
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
{
@ -431,6 +471,12 @@ void authenticator_initialize()
exit(1);
}
// resident_keys
memset(&RK_STORE,0xff,sizeof(RK_STORE));
sync_rk();
}
}
@ -439,26 +485,52 @@ void device_manage()
}
void ctap_reset_rk()
{
memset(&RK_STORE,0xff,sizeof(RK_STORE));
sync_rk();
}
uint32_t ctap_rk_size()
{
printf("Warning: rk not implemented\n");
return 0;
return RK_NUM;
}
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()

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));
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
{

View File

@ -20,7 +20,7 @@ import array, struct, socket
from fido2.hid import CtapHidDevice, CTAPHID
from fido2.client import Fido2Client, ClientError
from fido2.ctap import CtapError
from fido2.ctap1 import CTAP1
from fido2.ctap1 import CTAP1, ApduError, APDU
from fido2.ctap2 import *
from fido2.cose import *
from fido2.utils import Timeout, sha256
@ -61,6 +61,7 @@ class Tester:
def __init__(self,):
self.origin = "https://examplo.org"
self.host = "examplo.org"
self.user_count = 10
def find_device(self,):
print(list(CtapHidDevice.list_devices()))
@ -75,6 +76,9 @@ class Tester:
# consume timeout error
# cmd,resp = self.recv_raw()
def set_user_count(self, count):
self.user_count = count
def send_data(self, cmd, data):
if type(data) != type(b""):
data = struct.pack("%dB" % len(data), *[ord(x) for x in data])
@ -393,16 +397,101 @@ class Tester:
chal = sha256(b"AAA")
appid = sha256(b"BBB")
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.verify(appid, chal)
auth = self.ctap1.authenticate(chal, appid, reg.key_handle)
auth.verify(appid, chal, reg.public_key)
regs.append(reg)
# check endianness
if lastc:
assert (auth.counter - lastc) < 10
lastc = auth.counter
print(hex(lastc))
print("U2F reg + auth pass %d/5" % (i + 1))
if lastc > 0x100000:
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):
creds = []
@ -515,8 +604,8 @@ class Tester:
exclude_list.append({"id": fake_id2, "type": "public-key"})
# test make credential
print("make 3 credentials")
for i in range(0, 3):
print("make %d credentials" % self.user_count)
for i in range(0, self.user_count):
attest, data = self.client.make_credential(
rp, user, challenge, pin=PIN, exclude_list=[]
)
@ -657,10 +746,10 @@ class Tester:
def test_rk(self,):
creds = []
rp = {"id": self.host, "name": "ExaRP"}
user0 = {"id": b"first one", "name": "single User"}
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"
PIN = None
@ -671,7 +760,7 @@ class Tester:
print("registering 1 user with RK")
t1 = time.time() * 1000
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
VerifyAttestation(attest, data)
@ -690,7 +779,7 @@ class Tester:
print(assertions[0], client_data)
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
attest, data = self.client.make_credential(
rp, users[i], challenge, pin=PIN, exclude_list=[], rk=True
@ -707,6 +796,9 @@ class Tester:
)
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):
x.verify(client_data.hash, y.public_key)
@ -728,7 +820,8 @@ class Tester:
rp["id"], challenge, pin=PIN
)
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):
x.verify(client_data.hash, y.public_key)
@ -834,18 +927,35 @@ def test_find_brute_force():
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.")
force_udp_backend()
t = Tester()
t.find_device()
# t.test_hid()
# t.test_long_ping()
# t.test_fido2()
t.test_u2f()
# t.test_rk()
t.set_user_count(15)
if "u2f" in sys.argv:
t.test_u2f()
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()
# test_find_brute_force()
# t.test_fido2_simple()
# t.test_fido2_brute_force()