From 4f93b984cd89bf14d1eec524aa68ccc48b168599 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Tue, 4 Oct 2022 19:38:07 +0200 Subject: [PATCH] Adding U2F tests. Signed-off-by: Pol Henarejos --- tests/conftest.py | 3 +- tests/pico-fido/test_u2f.py | 110 ++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 tests/pico-fido/test_u2f.py diff --git a/tests/conftest.py b/tests/conftest.py index 56841a9..3dab4f2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -72,6 +72,7 @@ class Device(): self.__client1 = Fido2Client(self.__dev, self.__origin, user_interaction=self.__user_interaction) self.__client1._backend = _Ctap1ClientBackend(self.__dev, user_interaction=self.__user_interaction) + self.ctap1 = self.__client1._backend.ctap1 def __set_server(self, rp, attestation): self.__rp = rp @@ -242,7 +243,7 @@ class Device(): def GNA(self): return self.__client._backend.ctap2.get_next_assertion() - def doGA(self, client_data=Ellipsis, rp_id=Ellipsis, allow_list=None, extensions=None, user_verification=None, event=None, ctap1=False): + def doGA(self, client_data=Ellipsis, rp_id=Ellipsis, allow_list=None, extensions=None, user_verification=None, event=None, ctap1=False, check_only=False): client_data = client_data if client_data is not Ellipsis else CollectedClientData.create( type=CollectedClientData.TYPE.CREATE, origin=self.__origin, challenge=os.urandom(32) ) diff --git a/tests/pico-fido/test_u2f.py b/tests/pico-fido/test_u2f.py new file mode 100644 index 0000000..aa350c7 --- /dev/null +++ b/tests/pico-fido/test_u2f.py @@ -0,0 +1,110 @@ +import pytest +import os +from fido2.ctap1 import APDU, ApduError, Ctap1 +from fido2.webauthn import CollectedClientData +from fido2.utils import sha256 + +def test_u2f_reg(RegRes): + pass + +def test_u2f_auth(RegRes, AuthRes): + pass + +def test_u2f_auth_check_only(device, RegRes): + with pytest.raises(ApduError) as e: + device.ctap1.authenticate( + RegRes['req']['client_data'].hash, + RegRes['res'].attestation_object.auth_data.rp_id_hash, + RegRes['res'].attestation_object.auth_data.credential_data.credential_id, + check_only=True, + ) + assert e.value.code == APDU.USE_NOT_SATISFIED + +def test_version(device): + assert device.ctap1.get_version() == "U2F_V2" + +def test_bad_ins(device): + with pytest.raises(ApduError) as e: + device.ctap1.send_apdu(0, 0, 0, 0, b"") + assert e.value.code == 0x6D00 + +def test_bad_cla(device): + with pytest.raises(ApduError) as e: + device.ctap1.send_apdu(1, Ctap1.INS.VERSION, 0, 0, b"abc") + assert e.value.code == 0x6E00 + +@pytest.mark.parametrize("iterations", (5,)) +def test_u2f_it(device, iterations): + lastc = 0 + + regs = [] + + cd = CollectedClientData.create( + type=CollectedClientData.TYPE.CREATE, origin=None, challenge=os.urandom(32) + ) + cdh = cd.hash + rih = sha256(device.rp()['id'].encode()) + + for i in range(0, iterations): + reg = device.ctap1.register(cdh, rih) + auth = device.ctap1.authenticate(cdh, rih, reg.key_handle) + auth.verify(rih, cdh, reg.public_key) + + regs.append(reg) + # check endianness + if lastc: + assert (auth.counter - lastc) < 256 + lastc = auth.counter + if lastc > 0x80000000: + print("WARNING: counter is unusually high: %04x" % lastc) + assert 0 + + for reg in regs: + auth = device.ctap1.authenticate(cdh, rih, reg.key_handle) + + device.reboot() + + for reg in regs: + auth = device.ctap1.authenticate(cdh, rih, reg.key_handle) + + for reg in regs: + with pytest.raises(ApduError) as e: + auth = device.ctap1.authenticate( + cdh, rih, reg.key_handle, check_only=True + ) + assert e.value.code == APDU.USE_NOT_SATISFIED + +def test_bad_key_handle(device, RegRes): + kh = bytearray(RegRes['res'].attestation_object.auth_data.credential_data.credential_id) + kh[0] = kh[0] ^ (0x40) + + with pytest.raises(ApduError) as e: + device.ctap1.authenticate( + RegRes['res'].client_data.hash, RegRes['res'].attestation_object.auth_data.rp_id_hash, kh, check_only=True + ) + assert e.value.code == APDU.WRONG_DATA + + with pytest.raises(ApduError) as e: + device.ctap1.authenticate( + RegRes['res'].client_data.hash, RegRes['res'].attestation_object.auth_data.rp_id_hash, kh + ) + assert e.value.code == APDU.WRONG_DATA + +def test_bad_key_handle_length(device, RegRes): + kh = bytearray(RegRes['res'].attestation_object.auth_data.credential_data.credential_id) + + with pytest.raises(ApduError) as e: + device.ctap1.authenticate( + RegRes['res'].client_data.hash, RegRes['res'].attestation_object.auth_data.rp_id_hash, kh[: len(kh) // 2] + ) + assert e.value.code == APDU.WRONG_DATA + +def test_incorrect_appid(device, RegRes): + + badid = bytearray(RegRes['res'].attestation_object.auth_data.rp_id_hash) + badid[0] = badid[0] ^ (0x40) + with pytest.raises(ApduError) as e: + device.ctap1.authenticate( + RegRes['res'].client_data.hash, badid, RegRes['res'].attestation_object.auth_data.credential_data.credential_id + ) + assert e.value.code == APDU.WRONG_DATA