From 0964ff69b7c2371f1f7b19deca81718a5cdc9685 Mon Sep 17 00:00:00 2001 From: Conor Patrick Date: Mon, 11 Mar 2019 14:28:08 -0400 Subject: [PATCH] refactor a bit --- tools/ctap_test.py | 1276 +++++++++++++++++++++----------------------- 1 file changed, 614 insertions(+), 662 deletions(-) diff --git a/tools/ctap_test.py b/tools/ctap_test.py index 7591301..1edbcdd 100755 --- a/tools/ctap_test.py +++ b/tools/ctap_test.py @@ -12,7 +12,7 @@ # Script for testing correctness of CTAP2/CTAP1 security token from __future__ import print_function, absolute_import, unicode_literals -import sys, os, time, math +import sys, os, time from random import randint from binascii import hexlify import array, struct, socket @@ -21,12 +21,12 @@ from fido2.hid import CtapHidDevice, CTAPHID from fido2.client import Fido2Client, ClientError from fido2.ctap import CtapError from fido2.ctap1 import CTAP1, ApduError, APDU -from fido2.ctap2 import ES256, PinProtocolV1 +from fido2.ctap2 import * +from fido2.cose import * from fido2.utils import Timeout, sha256, hmac_sha256 from fido2.attestation import Attestation from solo.fido2 import force_udp_backend -from solo.client import SoloClient # Set up a FIDO 2 client using the origin https://example.com @@ -57,17 +57,6 @@ class Packet(object): return Packet(data) -class Test: - def __init__(self, msg): - self.msg = msg - - def __enter__(self,): - print(self.msg) - - def __exit__(self, a, b, c): - print("Pass") - - class Tester: def __init__(self,): self.origin = "https://examplo.org" @@ -156,233 +145,272 @@ class Tester: def test_long_ping(self): amt = 1000 pingdata = os.urandom(amt) - with Test("Send %d byte ping" % amt): - try: - t1 = time.time() * 1000 - r = self.send_data(CTAPHID.PING, pingdata) - t2 = time.time() * 1000 - delt = t2 - t1 - # if (delt < 140 ): - # raise RuntimeError('Fob is too fast (%d ms)' % delt) - if delt > 555 * (amt / 1000): - raise RuntimeError("Fob is too slow (%d ms)" % delt) - if r != pingdata: - raise ValueError("Ping data not echo'd") - except CtapError as e: - raise RuntimeError("ping failed") - + try: + t1 = time.time() * 1000 + r = self.send_data(CTAPHID.PING, pingdata) + t2 = time.time() * 1000 + delt = t2 - t1 + # if (delt < 140 ): + # raise RuntimeError('Fob is too fast (%d ms)' % delt) + if delt > 555 * (amt / 1000): + raise RuntimeError("Fob is too slow (%d ms)" % delt) + if r != pingdata: + raise ValueError("Ping data not echo'd") + print("1000 byte ping time: %s ms" % delt) + except CtapError as e: + print("7609 byte Ping failed:", e) + raise RuntimeError("ping failed") + print("PASS: 7609 byte ping") + # sys.flush(sys.sto) sys.stdout.flush() def test_hid(self, check_timeouts=False): if check_timeouts: - with Test("idle"): - try: - cmd, resp = self.recv_raw() - except socket.timeout: - pass - - with Test("init"): - r = self.send_data(CTAPHID.INIT, "\x11\x11\x11\x11\x11\x11\x11\x11") - - with Test("100 byte ping"): - pingdata = os.urandom(100) + print("Test idle") try: - r = self.send_data(CTAPHID.PING, pingdata) - if r != pingdata: - raise ValueError("Ping data not echo'd") - except CtapError as e: - print("100 byte Ping failed:", e) - raise RuntimeError("ping failed") + cmd, resp = self.recv_raw() + except socket.timeout: + print("Pass: Idle") + + print("Test init") + r = self.send_data(CTAPHID.INIT, "\x11\x11\x11\x11\x11\x11\x11\x11") + + pingdata = os.urandom(100) + try: + r = self.send_data(CTAPHID.PING, pingdata) + if r != pingdata: + raise ValueError("Ping data not echo'd") + except CtapError as e: + print("100 byte Ping failed:", e) + raise RuntimeError("ping failed") + print("PASS: 100 byte ping") self.test_long_ping() - with Test("Wink"): + try: r = self.send_data(CTAPHID.WINK, "") + print(hexlify(r)) + # assert(len(r) == 0) + except CtapError as e: + print("wink failed:", e) + raise RuntimeError("wink failed") + print("PASS: wink") - with Test("CBOR msg with no data"): - try: - r = self.send_data(CTAPHID.CBOR, "") - if len(r) > 1 or r[0] == 0: - raise RuntimeError("Cbor is supposed to have payload") - except CtapError as e: - assert e.code == CtapError.ERR.INVALID_LENGTH + # try: + # r = self.send_data(CTAPHID.WINK, 'we9gofrei8g') + # raise RuntimeError('Wink is not supposed to have payload') + # except CtapError as e: + # assert(e.code == CtapError.ERR.INVALID_LENGTH) + # print('PASS: malformed wink') - with Test("No data in U2F msg"): - try: - r = self.send_data(CTAPHID.MSG, "") - print(hexlify(r)) - if len(r) > 2: - raise RuntimeError("MSG is supposed to have payload") - except CtapError as e: - assert e.code == CtapError.ERR.INVALID_LENGTH + try: + r = self.send_data(CTAPHID.CBOR, "") + if len(r) > 1 or r[0] == 0: + raise RuntimeError("Cbor is supposed to have payload") + except CtapError as e: + assert e.code == CtapError.ERR.INVALID_LENGTH + print("PASS: no data cbor") - with Test("Use init command to resync"): + try: + r = self.send_data(CTAPHID.MSG, "") + print(hexlify(r)) + if len(r) > 2: + raise RuntimeError("MSG is supposed to have payload") + except CtapError as e: + assert e.code == CtapError.ERR.INVALID_LENGTH + print("PASS: no data msg") + + try: r = self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88") + except CtapError as e: + raise RuntimeError("resync fail: ", e) + print("PASS: resync") - with Test("Invalid HID command"): - try: - r = self.send_data(0x66, "") - raise RuntimeError("Invalid command did not return error") - except CtapError as e: - assert e.code == CtapError.ERR.INVALID_COMMAND + try: + r = self.send_data(0x66, "") + raise RuntimeError("Invalid command did not return error") + except CtapError as e: + assert e.code == CtapError.ERR.INVALID_COMMAND + print("PASS: invalid HID command") - with Test("Sending packet with too large of a length."): - self.send_raw("\x81\x1d\xba\x00") - cmd, resp = self.recv_raw() - self.check_error(resp, CtapError.ERR.INVALID_LENGTH) + print("Sending packet with too large of a length.") + self.send_raw("\x81\x1d\xba\x00") + cmd, resp = self.recv_raw() + self.check_error(resp, CtapError.ERR.INVALID_LENGTH) + print("PASS: invalid length") r = self.send_data(CTAPHID.PING, "\x44" * 200) - with Test("Sending packets that skip a sequence number."): - self.send_raw("\x81\x04\x90") - self.send_raw("\x00") - self.send_raw("\x01") - # skip 2 - self.send_raw("\x03") + print("Sending packets that skip a sequence number.") + self.send_raw("\x81\x04\x90") + self.send_raw("\x00") + self.send_raw("\x01") + # skip 2 + self.send_raw("\x03") + cmd, resp = self.recv_raw() + self.check_error(resp, CtapError.ERR.INVALID_SEQ) + if check_timeouts: cmd, resp = self.recv_raw() - self.check_error(resp, CtapError.ERR.INVALID_SEQ) + assert cmd == 0xBF # timeout + print("PASS: invalid sequence") - with Test("Resync and send ping"): + print("Resync and send ping") + try: + r = self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88") + pingdata = os.urandom(100) + r = self.send_data(CTAPHID.PING, pingdata) + if r != pingdata: + raise ValueError("Ping data not echo'd") + except CtapError as e: + raise RuntimeError("resync fail: ", e) + print("PASS: resync and ping") + + print("Send ping and abort it") + self.send_raw("\x81\x04\x00") + self.send_raw("\x00") + self.send_raw("\x01") + try: + r = self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88") + except CtapError as e: + raise RuntimeError("resync fail: ", e) + print("PASS: interrupt ping with resync") + + print("Send ping and abort it with different cid, expect timeout") + oldcid = self.cid() + newcid = "\x11\x22\x33\x44" + self.send_raw("\x81\x10\x00") + self.send_raw("\x00") + self.send_raw("\x01") + self.set_cid(newcid) + self.send_raw( + "\x86\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88" + ) # init from different cid + print("wait for init response") + cmd, r = self.recv_raw() # init response + assert cmd == 0x86 + self.set_cid(oldcid) + if check_timeouts: + # print('wait for timeout') + cmd, r = self.recv_raw() # timeout response + assert cmd == 0xBF + + print("PASS: resync and timeout") + + print("Test timeout") + self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88") + t1 = time.time() * 1000 + self.send_raw("\x81\x04\x00") + self.send_raw("\x00") + self.send_raw("\x01") + cmd, r = self.recv_raw() # timeout response + t2 = time.time() * 1000 + delt = t2 - t1 + assert cmd == 0xBF + assert r[0] == CtapError.ERR.TIMEOUT + assert delt < 1000 and delt > 400 + print("Pass timeout") + + print("Test not cont") + self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88") + self.send_raw("\x81\x04\x00") + self.send_raw("\x00") + self.send_raw("\x01") + self.send_raw("\x81\x10\x00") # init packet + cmd, r = self.recv_raw() # timeout response + assert cmd == 0xBF + assert r[0] == CtapError.ERR.INVALID_SEQ + print("PASS: Test not cont") + + if check_timeouts: + print("Check random cont ignored") + self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88") + self.send_raw("\x01\x10\x00") try: - r = self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88") - pingdata = os.urandom(100) - r = self.send_data(CTAPHID.PING, pingdata) - if r != pingdata: - raise ValueError("Ping data not echo'd") - except CtapError as e: - raise RuntimeError("resync fail: ", e) - - with Test("Send ping and abort it"): - self.send_raw("\x81\x04\x00") - self.send_raw("\x00") - self.send_raw("\x01") - try: - r = self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88") - except CtapError as e: - raise RuntimeError("resync fail: ", e) - - with Test("Send ping and abort it with different cid, expect timeout"): - oldcid = self.cid() - newcid = "\x11\x22\x33\x44" - self.send_raw("\x81\x10\x00") - self.send_raw("\x00") - self.send_raw("\x01") - self.set_cid(newcid) - self.send_raw( - "\x86\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88" - ) # init from different cid - print("wait for init response") - cmd, r = self.recv_raw() # init response - assert cmd == 0x86 - self.set_cid(oldcid) - if check_timeouts: - # print('wait for timeout') cmd, r = self.recv_raw() # timeout response - assert cmd == 0xBF + except socket.timeout: + pass + print("PASS: random cont") - with Test("Test timeout"): - self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88") - t1 = time.time() * 1000 - self.send_raw("\x81\x04\x00") - self.send_raw("\x00") - self.send_raw("\x01") - cmd, r = self.recv_raw() # timeout response - t2 = time.time() * 1000 - delt = t2 - t1 - assert cmd == 0xBF - assert r[0] == CtapError.ERR.TIMEOUT - assert delt < 1000 and delt > 400 + print("Check busy") + t1 = time.time() * 1000 + self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88") + oldcid = self.cid() + newcid = "\x11\x22\x33\x44" + self.send_raw("\x81\x04\x00") + self.set_cid(newcid) + self.send_raw("\x81\x04\x00") + cmd, r = self.recv_raw() # busy response + t2 = time.time() * 1000 + assert t2 - t1 < 100 + assert cmd == 0xBF + assert r[0] == CtapError.ERR.CHANNEL_BUSY - with Test("Test not cont"): - self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88") - self.send_raw("\x81\x04\x00") - self.send_raw("\x00") - self.send_raw("\x01") - self.send_raw("\x81\x10\x00") # init packet - cmd, r = self.recv_raw() # timeout response - assert cmd == 0xBF - assert r[0] == CtapError.ERR.INVALID_SEQ + self.set_cid(oldcid) + cmd, r = self.recv_raw() # timeout response + assert cmd == 0xBF + assert r[0] == CtapError.ERR.TIMEOUT + print("PASS: busy") + + print("Check busy interleaved") + cid1 = "\x11\x22\x33\x44" + cid2 = "\x01\x22\x33\x44" + self.set_cid(cid2) + self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88") + self.set_cid(cid1) + self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88") + self.send_raw("\x81\x00\x63") # echo 99 bytes first channel + + self.set_cid(cid2) # send ping on 2nd channel + self.send_raw("\x81\x00\x63") + self.send_raw("\x00") + + cmd, r = self.recv_raw() # busy response + + self.set_cid(cid1) # finish 1st channel ping + self.send_raw("\x00") + + self.set_cid(cid2) + + assert cmd == 0xBF + assert r[0] == CtapError.ERR.CHANNEL_BUSY + + self.set_cid(cid1) + cmd, r = self.recv_raw() # ping response + assert cmd == 0x81 + assert len(r) == 0x63 if check_timeouts: - with Test("Check random cont ignored"): - self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88") - self.send_raw("\x01\x10\x00") - try: - cmd, r = self.recv_raw() # timeout response - except socket.timeout: - pass - - with Test("Check busy"): - t1 = time.time() * 1000 - self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88") - oldcid = self.cid() - newcid = "\x11\x22\x33\x44" - self.send_raw("\x81\x04\x00") - self.set_cid(newcid) - self.send_raw("\x81\x04\x00") - cmd, r = self.recv_raw() # busy response - t2 = time.time() * 1000 - assert t2 - t1 < 100 - assert cmd == 0xBF - assert r[0] == CtapError.ERR.CHANNEL_BUSY - - self.set_cid(oldcid) - cmd, r = self.recv_raw() # timeout response + cmd, r = self.recv_raw() # timeout assert cmd == 0xBF assert r[0] == CtapError.ERR.TIMEOUT - - with Test("Check busy interleaved"): - cid1 = "\x11\x22\x33\x44" - cid2 = "\x01\x22\x33\x44" - self.set_cid(cid2) - self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88") - self.set_cid(cid1) - self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88") - self.send_raw("\x81\x00\x63") # echo 99 bytes first channel - - self.set_cid(cid2) # send ping on 2nd channel - self.send_raw("\x81\x00\x63") - self.send_raw("\x00") - - cmd, r = self.recv_raw() # busy response - - self.set_cid(cid1) # finish 1st channel ping - self.send_raw("\x00") - - self.set_cid(cid2) - - assert cmd == 0xBF - assert r[0] == CtapError.ERR.CHANNEL_BUSY - - self.set_cid(cid1) - cmd, r = self.recv_raw() # ping response - assert cmd == 0x81 - assert len(r) == 0x63 + print("PASS: busy interleaved") if check_timeouts: - with Test("Test idle, wait for timeout"): - sys.stdout.flush() - try: - cmd, resp = self.recv_raw() - except socket.timeout: - pass + print("Test idle, wait for timeout") + sys.stdout.flush() + try: + cmd, resp = self.recv_raw() + except socket.timeout: + print("Pass: Idle") - with Test("Test cid 0 is invalid"): - self.set_cid("\x00\x00\x00\x00") - self.send_raw( - "\x86\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88", cid="\x00\x00\x00\x00" - ) - cmd, r = self.recv_raw() # timeout - assert cmd == 0xBF - assert r[0] == CtapError.ERR.INVALID_CHANNEL + print("Test cid 0 is invalid") + self.set_cid("\x00\x00\x00\x00") + self.send_raw( + "\x86\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88", cid="\x00\x00\x00\x00" + ) + cmd, r = self.recv_raw() # timeout + assert cmd == 0xBF + assert r[0] == CtapError.ERR.INVALID_CHANNEL + print("Pass: cid 0") - with Test("Test invalid broadcast cid use"): - self.set_cid("\xff\xff\xff\xff") - self.send_raw( - "\x81\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88", cid="\xff\xff\xff\xff" - ) - cmd, r = self.recv_raw() # timeout - assert cmd == 0xBF - assert r[0] == CtapError.ERR.INVALID_CHANNEL + print("Test invalid broadcast cid use") + self.set_cid("\xff\xff\xff\xff") + self.send_raw( + "\x81\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88", cid="\xff\xff\xff\xff" + ) + cmd, r = self.recv_raw() # timeout + assert cmd == 0xBF + assert r[0] == CtapError.ERR.INVALID_CHANNEL + print("Pass: cid broadcast") def test_u2f(self,): chal = sha256(b"AAA") @@ -391,89 +419,98 @@ class Tester: regs = [] - with Test("Check version"): - assert self.ctap1.get_version() == "U2F_V2" + print("Check version") + assert self.ctap1.get_version() == "U2F_V2" + print("Pass") - with Test("Check bad INS"): - try: - res = self.ctap1.send_apdu(0, 0, 0, 0, b"") - except ApduError as e: - assert e.code == 0x6D00 + 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") - with Test("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("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): - with Test( - "U2F reg + auth %d/%d (count: %02x)" % (i + 1, self.user_count, lastc) - ): - 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) + 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 - if lastc > 0x80000000: - print("WARNING: counter is unusually high: %04x" % lastc) - assert 0 + regs.append(reg) + # check endianness + if lastc: + assert (auth.counter - lastc) < 10 + lastc = auth.counter + if lastc > 0x80000000: + 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): - with Test( - "Checking previous registration %d/%d" % (i + 1, self.user_count) - ): - auth = self.ctap1.authenticate(chal, appid, regs[i].key_handle) - auth.verify(appid, chal, regs[i].public_key) + 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): - with Test("Check that previous credential %d is registered" % i): - 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 - - with Test("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 + auth = self.ctap1.authenticate( + chal, appid, regs[i].key_handle, check_only=True + ) except ApduError as e: - assert e.code == APDU.WRONG_DATA + # Indicates that key handle is registered + assert e.code == APDU.USE_NOT_SATISFIED - with Test("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("Check pass %d/%d" % (i + 1, self.user_count)) - with Test("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("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") - with Test("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("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 = [] @@ -489,13 +526,14 @@ class Tester: exclude_list.append({"id": fake_id1, "type": "public-key"}) exclude_list.append({"id": fake_id2, "type": "public-key"}) + print("MC") t1 = time.time() * 1000 attest, data = self.client.make_credential( rp, user, challenge, pin=PIN, exclude_list=[] ) t2 = time.time() * 1000 VerifyAttestation(attest, data) - print("Register time: %d ms" % (t2 - t1)) + print("Register valid (%d ms)" % (t2 - t1)) cred = attest.auth_data.credential_data creds.append(cred) @@ -508,7 +546,7 @@ class Tester: t2 = time.time() * 1000 assertions[0].verify(client_data.hash, creds[0].public_key) - print("Assertion time: %d ms" % (t2 - t1)) + print("Assertion valid (%d ms)" % (t2 - t1)) def test_fido2_brute_force(self): creds = [] @@ -528,10 +566,10 @@ class Tester: fake_id1 = array.array( "B", [randint(0, 255) for i in range(0, 150)] - ).tobytes() + ).tostring() fake_id2 = array.array( "B", [randint(0, 255) for i in range(0, 73)] - ).tobytes() + ).tostring() exclude_list.append({"id": fake_id1, "type": "public-key"}) exclude_list.append({"id": fake_id2, "type": "public-key"}) @@ -576,151 +614,160 @@ class Tester: fake_id1 = array.array( "B", [randint(0, 255) for i in range(0, 150)] - ).tobytes() + ).tostring() fake_id2 = array.array( "B", [randint(0, 255) for i in range(0, 73)] - ).tobytes() + ).tostring() exclude_list.append({"id": fake_id1, "type": "public-key"}) exclude_list.append({"id": fake_id2, "type": "public-key"}) # test make credential - with Test("make %d credentials" % self.user_count): - lastc = 0 - for i in range(0, self.user_count): - attest, data = self.client.make_credential( - rp, user, challenge, pin=PIN, exclude_list=[] - ) - VerifyAttestation(attest, data) - - # verify counter is correct - if lastc > 0: - assert attest.auth_data.counter - lastc < 10 - assert attest.auth_data.counter - lastc > 0 - assert attest.auth_data.counter < 0x10000 - lastc = attest.auth_data.counter - - cred = attest.auth_data.credential_data - creds.append(cred) - print(cred) - - if PIN is not None: - with Test("make credential with wrong pin code"): - try: - attest, data = self.client.make_credential( - rp, user, challenge, pin=PIN + " ", exclude_list=[] - ) - except CtapError as e: - assert e.code == CtapError.ERR.PIN_INVALID - except ClientError as e: - assert e.cause.code == CtapError.ERR.PIN_INVALID - - with Test("make credential with exclude list"): + print("make %d credentials" % self.user_count) + lastc = 0 + for i in range(0, self.user_count): attest, data = self.client.make_credential( - rp, user, challenge, pin=PIN, exclude_list=exclude_list + rp, user, challenge, pin=PIN, exclude_list=[] ) VerifyAttestation(attest, data) + + # verify counter is correct + if lastc > 0: + assert attest.auth_data.counter - lastc < 10 + assert attest.auth_data.counter - lastc > 0 + assert attest.auth_data.counter < 0x10000 + lastc = attest.auth_data.counter + cred = attest.auth_data.credential_data creds.append(cred) - - with Test("make credential with exclude list including real credential"): - real_excl = [{"id": cred.credential_id, "type": "public-key"}] - try: - attest, data = self.client.make_credential( - rp, - user, - challenge, - pin=PIN, - exclude_list=exclude_list + real_excl, - ) - raise RuntimeError("Exclude list did not return expected error") - except CtapError as e: - assert e.code == CtapError.ERR.CREDENTIAL_EXCLUDED - except ClientError as e: - assert e.cause.code == CtapError.ERR.CREDENTIAL_EXCLUDED - - for i, x in enumerate(creds): - with Test("get assertion %d" % i): - allow_list = [{"id": x.credential_id, "type": "public-key"}] - assertions, client_data = self.client.get_assertion( - rp["id"], challenge, allow_list, pin=PIN - ) - assertions[0].verify(client_data.hash, x.public_key) + print(cred) + print("PASS") if PIN is not None: - with Test("get assertion with wrong pin code"): - try: - assertions, client_data = self.client.get_assertion( - rp["id"], challenge, allow_list, pin=PIN + " " - ) - except CtapError as e: - assert e.code == CtapError.ERR.PIN_INVALID - except ClientError as e: - assert e.cause.code == CtapError.ERR.PIN_INVALID + print("make credential with wrong pin code") + try: + attest, data = self.client.make_credential( + rp, user, challenge, pin=PIN + " ", exclude_list=[] + ) + except CtapError as e: + assert e.code == CtapError.ERR.PIN_INVALID + except ClientError as e: + assert e.cause.code == CtapError.ERR.PIN_INVALID + print("PASS") - with Test("get multiple assertions"): - allow_list = [ - {"id": x.credential_id, "type": "public-key"} for x in creds - ] + print("make credential with exclude list") + attest, data = self.client.make_credential( + rp, user, challenge, pin=PIN, exclude_list=exclude_list + ) + VerifyAttestation(attest, data) + cred = attest.auth_data.credential_data + creds.append(cred) + print("PASS") + + print("make credential with exclude list including real credential") + real_excl = [{"id": cred.credential_id, "type": "public-key"}] + try: + attest, data = self.client.make_credential( + rp, user, challenge, pin=PIN, exclude_list=exclude_list + real_excl + ) + raise RuntimeError("Exclude list did not return expected error") + except CtapError as e: + assert e.code == CtapError.ERR.CREDENTIAL_EXCLUDED + except ClientError as e: + assert e.cause.code == CtapError.ERR.CREDENTIAL_EXCLUDED + print("PASS") + + for i, x in enumerate(creds): + print("get assertion %d" % i) + allow_list = [{"id": x.credential_id, "type": "public-key"}] assertions, client_data = self.client.get_assertion( rp["id"], challenge, allow_list, pin=PIN ) + assertions[0].verify(client_data.hash, x.public_key) + print("PASS") - for ass, cred in zip(assertions, creds): - i += 1 + if PIN is not None: + print("get assertion with wrong pin code") + try: + assertions, client_data = self.client.get_assertion( + rp["id"], challenge, allow_list, pin=PIN + " " + ) + except CtapError as e: + assert e.code == CtapError.ERR.PIN_INVALID + except ClientError as e: + assert e.cause.code == CtapError.ERR.PIN_INVALID + print("PASS") - ass.verify(client_data.hash, cred.public_key) - print("%d verified" % i) + print("get multiple assertions") + allow_list = [{"id": x.credential_id, "type": "public-key"} for x in creds] + assertions, client_data = self.client.get_assertion( + rp["id"], challenge, allow_list, pin=PIN + ) - with Test("Reset device"): - try: - self.ctap.reset() - except CtapError as e: - print("Warning, reset failed: ", e) - pass + for ass, cred in zip(assertions, creds): + i += 1 + + ass.verify(client_data.hash, cred.public_key) + print("%d verified" % i) + print("PASS") + + print("Reset device") + try: + self.ctap.reset() + except CtapError as e: + print("Warning, reset failed: ", e) + pass + print("PASS") test(self, None) - with Test("Set a pin code"): - PIN = "1122aabbwfg0h9g !@#==" + print("Set a pin code") + PIN = "1122aabbwfg0h9g !@#==" + self.client.pin_protocol.set_pin(PIN) + print("PASS") + + print("Illegally set pin code again") + try: self.client.pin_protocol.set_pin(PIN) + except CtapError as e: + assert e.code == CtapError.ERR.NOT_ALLOWED + print("PASS") - with Test("Illegally set pin code again"): - try: - self.client.pin_protocol.set_pin(PIN) - except CtapError as e: - assert e.code == CtapError.ERR.NOT_ALLOWED + print("Change pin code") + PIN2 = PIN + "_pin2" + self.client.pin_protocol.change_pin(PIN, PIN2) + PIN = PIN2 + print("PASS") - with Test("Change pin code"): - PIN2 = PIN + "_pin2" - self.client.pin_protocol.change_pin(PIN, PIN2) - PIN = PIN2 + print("Change pin code using wrong pin") + try: + self.client.pin_protocol.change_pin(PIN.replace("a", "b"), "1234") + except CtapError as e: + assert e.code == CtapError.ERR.PIN_INVALID + print("PASS") - with Test("Change pin code using wrong pin"): - try: - self.client.pin_protocol.change_pin(PIN.replace("a", "b"), "1234") - except CtapError as e: - assert e.code == CtapError.ERR.PIN_INVALID + print("MC using wrong pin") + try: + self.test_fido2_simple("abcd3") + except ClientError as e: + assert e.cause.code == CtapError.ERR.PIN_INVALID + print("PASS") - with Test("MC using wrong pin"): - try: - self.test_fido2_simple("abcd3") - except ClientError as e: - assert e.cause.code == CtapError.ERR.PIN_INVALID - - with Test("get info"): - inf = self.ctap.get_info() + print("get info") + inf = self.ctap.get_info() + print("PASS") self.test_fido2_simple(PIN) - with Test("Re-run make_credential and get_assertion tests with pin code"): - test(self, PIN) + print("Re-run make_credential and get_assertion tests with pin code") + test(self, PIN) - with Test("Reset device"): - try: - self.ctap.reset() - except CtapError as e: - print("Warning, reset failed: ", e) + print("Reset device") + try: + self.ctap.reset() + except CtapError as e: + print("Warning, reset failed: ", e) + print("PASS") def test_fido2_other(self,): @@ -738,25 +785,23 @@ class Tester: cdh = b"123456789abcdef0123456789abcdef0" def testFunc(func, test, *args, **kwargs): - with Test(test): - res = None - expectedError = kwargs.get("expectedError", None) - otherArgs = kwargs.get("other", {}) - try: - res = func(*args, **otherArgs) - if expectedError != CtapError.ERR.SUCCESS: + print(test) + res = None + expectedError = kwargs.get("expectedError", None) + otherArgs = kwargs.get("other", {}) + try: + res = func(*args, **otherArgs) + if expectedError != CtapError.ERR.SUCCESS: + raise RuntimeError("Expected error to occur for test: %s" % test) + except CtapError as e: + if expectedError is not None: + if e.code != expectedError: raise RuntimeError( - "Expected error to occur for test: %s" % test + "Got error code 0x%x, expected %x" % (e.code, expectedError) ) - except CtapError as e: - if expectedError is not None: - if e.code != expectedError: - raise RuntimeError( - "Got error code 0x%x, expected %x" - % (e.code, expectedError) - ) - else: - print(e) + else: + print(e) + print("Pass") return res def testReset(): @@ -789,19 +834,25 @@ class Tester: testReset() - with Test("Get info"): - info = self.ctap.get_info() + print("Get info") + info = self.ctap.get_info() - with Test("Check FIDO2 string is in VERSIONS field"): - assert "FIDO_2_0" in info.versions + print(info) + print("Pass") - with Test("Check pin protocols field"): - if len(info.pin_protocols): - assert sum(info.pin_protocols) > 0 + print("Check FIDO2 string is in VERSIONS field") + assert "FIDO_2_0" in info.versions + print("Pass") - with Test("Check options field"): - for x in info.options: - assert info.options[x] in [True, False] + print("Check pin protocols field") + if len(info.pin_protocols): + assert sum(info.pin_protocols) > 0 + print("Pass") + + print("Check options field") + for x in info.options: + assert info.options[x] in [True, False] + print("Pass") prev_reg = testMC( "Send MC request, expect success", @@ -812,11 +863,13 @@ class Tester: expectedError=CtapError.ERR.SUCCESS, ) - with Test("Check attestation format is correct"): - assert prev_reg.fmt in ["packed", "tpm", "android-key", "adroid-safetynet"] + print("Check attestation format is correct") + assert prev_reg.fmt in ["packed", "tpm", "android-key", "adroid-safetynet"] + print("Pass") - with Test("Check auth_data is at least 77 bytes"): - assert len(prev_reg.auth_data) >= 77 + print("Check auth_data is at least 77 bytes") + assert len(prev_reg.auth_data) >= 77 + print("Pass") allow_list = [ { @@ -833,39 +886,15 @@ class Tester: expectedError=CtapError.ERR.SUCCESS, ) - with Test("Test auth_data is 37 bytes"): - assert len(prev_auth.auth_data) == 37 + print("Test auth_data is 37 bytes") + assert len(prev_auth.auth_data) == 37 + print("pass") - with Test("Test that auth_data.rpIdHash is correct"): - assert sha256(rp["id"].encode()) == prev_auth.auth_data.rp_id_hash - - with Test("Check that AT flag is not set"): - assert (prev_auth.auth_data.flags & 0xF8) == 0 - - with Test("Test that user, credential and numberOfCredentials are not present"): - assert prev_auth.user == None - assert prev_auth.number_of_credentials == None - - testGA( - "Send GA request with empty allow_list, expect NO_CREDENTIALS", - rp["id"], - cdh, - [], - expectedError=CtapError.ERR.NO_CREDENTIALS, - ) - - # apply bit flip - badid = list(prev_reg.auth_data.credential_data.credential_id[:]) - badid[len(badid) // 2] = badid[len(badid) // 2] ^ 1 - badid = bytes(badid) - - testGA( - "Send GA request with corrupt credId in allow_list, expect NO_CREDENTIALS", - rp["id"], - cdh, - [{"id": badid, "type": "public-key"}], - expectedError=CtapError.ERR.NO_CREDENTIALS, - ) + print("Test that user, credential and numberOfCredentials are not present") + assert prev_auth.user == None + assert prev_auth.number_of_credentials == None + # assert prev_auth.credential == None # TODO double check this + print("Pass") testMC( "Send MC request with missing clientDataHash, expect error", @@ -1246,8 +1275,9 @@ class Tester: other={"options": {"uv": True}}, expectedError=CtapError.ERR.SUCCESS, ) - with Test("Check that UV flag is set in response"): - assert res.auth_data.flags & (1 << 2) + print("Check that UV flag is set in response") + assert res.auth_data.flags & (1 << 2) + print("Pass") if "up" in info.options: if info.options["up"]: res = testGA( @@ -1258,8 +1288,9 @@ class Tester: other={"options": {"up": True}}, expectedError=CtapError.ERR.SUCCESS, ) - with Test("Check that UP flag is set in response"): + print("Check that UP flag is set in response") assert res.auth_data.flags & 1 + print("Pass") testGA( "Send GA request with bogus type item in allow_list, expect SUCCESS", @@ -1310,10 +1341,11 @@ class Tester: pin_auth = None if pin_code: - with Test("Set pin code"): - self.client.pin_protocol.set_pin(pin_code) - pin_token = self.client.pin_protocol.get_pin_token(pin_code) - pin_auth = hmac_sha256(pin_token, cdh)[:16] + print("Setting pin code ...") + self.client.pin_protocol.set_pin(pin_code) + pin_token = self.client.pin_protocol.get_pin_token(pin_code) + pin_auth = hmac_sha256(pin_token, cdh)[:16] + print("Pass") testMC( "Send MC request with rk option set to true, expect SUCCESS", @@ -1349,31 +1381,35 @@ class Tester: expectedError=CtapError.ERR.SUCCESS, ) - with Test("Check that there are 3 credentials returned"): - assert auth1.number_of_credentials == 3 + print("Check that there are 3 credentials returned") + assert auth1.number_of_credentials == 3 + print("Pass") - with Test("Get the next 2 assertions"): - auth2 = self.ctap.get_next_assertion() - auth3 = self.ctap.get_next_assertion() + print("Get the next 2 assertions") + auth2 = self.ctap.get_next_assertion() + auth3 = self.ctap.get_next_assertion() + print("Pass") if not pin_code: - with Test("Check only the user ID was returned"): - assert "id" in auth1.user.keys() and len(auth1.user.keys()) == 1 - assert "id" in auth2.user.keys() and len(auth2.user.keys()) == 1 - assert "id" in auth3.user.keys() and len(auth3.user.keys()) == 1 + print("Check only the user ID was returned") + assert "id" in auth1.user.keys() and len(auth1.user.keys()) == 1 + assert "id" in auth2.user.keys() and len(auth2.user.keys()) == 1 + assert "id" in auth3.user.keys() and len(auth3.user.keys()) == 1 + print("Pass") else: - with Test("Check that all user info was returned"): - for x in (auth1, auth2, auth3): - for y in ("name", "icon", "displayName", "id"): - assert y in x.user.keys() - assert len(x.user.keys()) == 4 + print("Check that all user info was returned") + for x in (auth1, auth2, auth3): + for y in ("name", "icon", "displayName", "id"): + assert y in x.user.keys() + assert len(x.user.keys()) == 4 + print("Pass") - with Test("Send an extra getNextAssertion request, expect error"): - try: - auth4 = self.ctap.get_next_assertion() - assert 0 - except CtapError as e: - print(e) + print("Send an extra getNextAssertion request, expect error") + try: + auth4 = self.ctap.get_next_assertion() + except CtapError as e: + print(e) + print("Pass") testRk(None) # @@ -1389,20 +1425,23 @@ class Tester: expectedError=CtapError.ERR.SUCCESS, ) - with Test("Test getKeyAgreement has appropriate fields"): - key = res[1] - assert "Is public key" and key[1] == 2 - assert "Is P256" and key[-1] == 1 - assert "Is right alg" and key[3] == -7 - assert "Right key" and len(key[-3]) == 32 and type(key[-3]) == type(bytes()) + print("Test getKeyAgreement has appropriate fields") + key = res[1] + assert "Is public key" and key[1] == 2 + assert "Is P256" and key[-1] == 1 + assert "Is right alg" and key[3] == -7 + assert "Right key" and len(key[-3]) == 32 and type(key[-3]) == type(bytes()) + print("Pass") - with Test("Test setting a new pin"): - pin2 = "qwertyuiop\x11\x22\x33\x00123" - self.client.pin_protocol.change_pin(pin1, pin2) + print("Test setting a new pin") + pin2 = "qwertyuiop\x11\x22\x33\x00123" + self.client.pin_protocol.change_pin(pin1, pin2) + print("Pass") - with Test("Test getting new pin_auth"): - pin_token = self.client.pin_protocol.get_pin_token(pin2) - pin_auth = hmac_sha256(pin_token, cdh)[:16] + print("Test getting new pin_auth") + pin_token = self.client.pin_protocol.get_pin_token(pin2) + pin_auth = hmac_sha256(pin_token, cdh)[:16] + print("Pass") res_mc = testMC( "Send MC request with new pin auth", @@ -1414,8 +1453,9 @@ class Tester: expectedError=CtapError.ERR.SUCCESS, ) - with Test("Check UV flag is set"): - assert res_mc.auth_data.flags & (1 << 2) + print("Check UV flag is set") + assert res_mc.auth_data.flags & (1 << 2) + print("Pass") res_ga = testGA( "Send GA request with no allow_list, expect SUCCESS", @@ -1431,13 +1471,15 @@ class Tester: expectedError=CtapError.ERR.SUCCESS, ) - with Test("Check UV flag is set"): - assert res_ga.auth_data.flags & (1 << 2) + print("Check UV flag is set") + assert res_ga.auth_data.flags & (1 << 2) + print("Pass") testReset() - with Test("Setting pin code, expect SUCCESS"): - self.client.pin_protocol.set_pin(pin1) + print("Setting pin code, expect SUCCESS") + self.client.pin_protocol.set_pin(pin1) + print("Pass") testReset() @@ -1448,58 +1490,18 @@ class Tester: # assert e.code == CtapError.ERR.POLICY_VIOLATION # print("Pass") - with Test("Setting pin code >63 bytes, expect POLICY_VIOLATION "): - try: - self.client.pin_protocol.set_pin("A" * 64) - assert 0 - except CtapError as e: - assert e.code == CtapError.ERR.PIN_POLICY_VIOLATION + print("Setting pin code >63 bytes, expect POLICY_VIOLATION ") + try: + self.client.pin_protocol.set_pin("A" * 64) + except CtapError as e: + assert e.code == CtapError.ERR.PIN_POLICY_VIOLATION + print("Pass") - with Test("Get pin token when no pin is set, expect PIN_NOT_SET"): - try: - self.client.pin_protocol.get_pin_token(pin1) - assert 0 - except CtapError as e: - assert e.code == CtapError.ERR.PIN_NOT_SET - - with Test("Get change pin when no pin is set, expect PIN_NOT_SET"): - try: - self.client.pin_protocol.change_pin(pin1, "1234") - assert 0 - except CtapError as e: - assert e.code == CtapError.ERR.PIN_NOT_SET - - with Test("Setting pin code and get pin_token, expect SUCCESS"): - self.client.pin_protocol.set_pin(pin1) - pin_token = self.client.pin_protocol.get_pin_token(pin1) - pin_auth = hmac_sha256(pin_token, cdh)[:16] - - with Test("Get info and assert that clientPin is set to true"): - info = self.ctap.get_info() - assert info.options["clientPin"] - - with Test("Test setting pin again fails"): - try: - self.client.pin_protocol.set_pin(pin1) - assert 0 - except CtapError as e: - print(e) - - res_mc = testMC( - "Send MC request with no pin_auth, expect PIN_REQUIRED", - cdh, - rp, - user, - key_params, - expectedError=CtapError.ERR.PIN_REQUIRED, - ) - - res_mc = testGA( - "Send GA request with no pin_auth, expect PIN_REQUIRED", - rp["id"], - cdh, - expectedError=CtapError.ERR.PIN_REQUIRED, - ) + print("Setting pin code and get pin_token, expect SUCCESS") + self.client.pin_protocol.set_pin(pin1) + pin_token = self.client.pin_protocol.get_pin_token(pin1) + pin_auth = hmac_sha256(pin_token, cdh)[:16] + print("Pass") res = testCP( "Test getRetries, expect SUCCESS", @@ -1508,8 +1510,9 @@ class Tester: expectedError=CtapError.ERR.SUCCESS, ) - with Test("Check there is 8 pin attempts left"): - assert res[3] == 8 + print("Check there is 8 pin attempts left") + assert res[3] == 8 + print("Pass") # Flip 1 bit pin_wrong = list(pin1) @@ -1538,9 +1541,10 @@ class Tester: reboot() - with Test("Get pin_token, expect SUCCESS"): - pin_token = self.client.pin_protocol.get_pin_token(pin1) - pin_auth = hmac_sha256(pin_token, cdh)[:16] + print("Get pin_token, expect SUCCESS") + pin_token = self.client.pin_protocol.get_pin_token(pin1) + pin_auth = hmac_sha256(pin_token, cdh)[:16] + print("Pass") res_mc = testMC( "Send MC request with correct pin_auth", @@ -1552,9 +1556,10 @@ class Tester: expectedError=CtapError.ERR.SUCCESS, ) - with Test("Test getRetries resets to 8"): - res = self.ctap.client_pin(pin_protocol, PinProtocolV1.CMD.GET_RETRIES) - assert res[3] == (8) + print("Test getRetries resets to 8") + res = self.ctap.client_pin(pin_protocol, PinProtocolV1.CMD.GET_RETRIES) + assert res[3] == (8) + print("Pass") for i in range(1, 10): err = CtapError.ERR.PIN_INVALID @@ -1572,9 +1577,11 @@ class Tester: if i > 8: attempts = 0 - with Test("Check there is %d pin attempts left" % attempts): - res = self.ctap.client_pin(pin_protocol, PinProtocolV1.CMD.GET_RETRIES) - assert res[3] == attempts + print("Check there is %d pin attempts left" % attempts) + res = self.ctap.client_pin(pin_protocol, PinProtocolV1.CMD.GET_RETRIES) + assert res[3] == attempts + + print("Pass") if err == CtapError.ERR.PIN_AUTH_BLOCKED: reboot() @@ -1611,36 +1618,43 @@ class Tester: ] challenge = "Y2hhbGxlbmdl" PIN = None + print("reset") self.ctap.reset() # if PIN: self.client.pin_protocol.set_pin(PIN) - with Test("registering 1 user with RK"): + print("registering 1 user with RK") + t1 = time.time() * 1000 + attest, data = self.client.make_credential( + rp, users[-1], challenge, pin=PIN, exclude_list=[], rk=True + ) + t2 = time.time() * 1000 + VerifyAttestation(attest, data) + creds.append(attest.auth_data.credential_data) + print("Register valid (%d ms)" % (t2 - t1)) + + print("1 assertion") + t1 = time.time() * 1000 + assertions, client_data = self.client.get_assertion( + rp["id"], challenge, pin=PIN + ) + t2 = time.time() * 1000 + assertions[0].verify(client_data.hash, creds[0].public_key) + print("Assertion valid (%d ms)" % (t2 - t1)) + + print(assertions[0], client_data) + + print("registering %d users with RK" % len(users)) + for i in range(0, len(users) - 1): t1 = time.time() * 1000 attest, data = self.client.make_credential( - rp, users[-1], challenge, pin=PIN, exclude_list=[], rk=True + rp, users[i], challenge, pin=PIN, exclude_list=[], rk=True ) t2 = time.time() * 1000 VerifyAttestation(attest, data) + print("Register valid (%d ms)" % (t2 - t1)) + creds.append(attest.auth_data.credential_data) - with Test("1 assertion"): - t1 = time.time() * 1000 - assertions, client_data = self.client.get_assertion( - rp["id"], challenge, pin=PIN - ) - t2 = time.time() * 1000 - assertions[0].verify(client_data.hash, creds[0].public_key) - - with Test("registering %d users with RK" % 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 - ) - t2 = time.time() * 1000 - VerifyAttestation(attest, data) - creds.append(attest.auth_data.credential_data) - t1 = time.time() * 1000 assertions, client_data = self.client.get_assertion( rp["id"], challenge, pin=PIN @@ -1655,80 +1669,28 @@ class Tester: print("Assertion(s) valid (%d ms)" % (t2 - t1)) - with Test("register a duplicate user "): - t1 = time.time() * 1000 - attest, data = self.client.make_credential( - rp, users[1], challenge, pin=PIN, exclude_list=[], rk=True - ) - t2 = time.time() * 1000 - VerifyAttestation(attest, data) - creds = creds[:2] + creds[3:] + [attest.auth_data.credential_data] + print("registering a duplicate user ") + + t1 = time.time() * 1000 + attest, data = self.client.make_credential( + rp, users[1], challenge, pin=PIN, exclude_list=[], rk=True + ) + t2 = time.time() * 1000 + VerifyAttestation(attest, data) + creds = creds[:2] + creds[3:] + [attest.auth_data.credential_data] + print("Register valid (%d ms)" % (t2 - t1)) t1 = time.time() * 1000 assertions, client_data = self.client.get_assertion( rp["id"], challenge, pin=PIN ) t2 = time.time() * 1000 - with Test("check %d assertions, %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) + 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) - def test_solo(self,): - """ - Solo specific tests - """ - # RNG command - sc = SoloClient() - sc.find_device(self.dev) - sc.use_u2f() - memmap = (0x08005000, 0x08005000 + 198 * 1024 - 8) - - total = 1024 * 16 - with Test("Gathering %d random bytes..." % total): - entropy = b"" - while len(entropy) < total: - entropy += sc.get_rng() - total = len(entropy) - - with Test("Test entropy is close to perfect"): - sum = 0.0 - for x in range(0, 256): - freq = entropy.count(x) - p = freq / total - sum -= p * math.log2(p) - assert sum > 7.98 - print("Entropy is %.5f bits per byte." % sum) - - with Test("Test Solo version command"): - assert len(sc.solo_version()) == 3 - - with Test("Test bootloader is not active"): - try: - sc.write_flash(memmap[0], b"1234") - except ApduError: - pass - - def test_bootloader(self,): - sc = SoloClient() - sc.find_device(self.dev) - sc.use_u2f() - - memmap = (0x08005000, 0x08005000 + 198 * 1024 - 8) - data = b"A" * 64 - - with Test("Test version command"): - assert len(sc.bootloader_version()) == 3 - - with Test("Test write command"): - sc.write_flash(memmap[0], data) - - for addr in (memmap[0] - 8, memmap[0] - 4, memmap[1], memmap[1] - 8): - with Test("Test out of bounds write command at 0x%04x" % addr): - try: - sc.write_flash(addr, data) - except CtapError as e: - assert e.code == CtapError.ERR.NOT_ALLOWED + print("Assertion(s) valid (%d ms)" % (t2 - t1)) def test_responses(self,): PIN = "1234" @@ -1845,9 +1807,6 @@ if __name__ == "__main__": t.find_device() - if "solo" in sys.argv: - t.test_solo() - if "u2f" in sys.argv: t.test_u2f() @@ -1863,14 +1822,7 @@ if __name__ == "__main__": # hid tests are a bit invasive and should be done last if "hid" in sys.argv: - t.test_hid(check_timeouts=t.is_sim) - - if "bootloader" in sys.argv: - if t.is_sim: - raise RuntimeError("Cannot test bootloader in simulation yet.") - print("Put device in bootloader mode and then hit enter") - input() - t.test_bootloader() + t.test_hid() # t.test_responses() # test_find_brute_force()