More test fixes.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
This commit is contained in:
Pol Henarejos
2022-10-03 01:24:17 +02:00
parent 6e91694253
commit 6f226001df
4 changed files with 124 additions and 116 deletions

View File

@@ -4,7 +4,7 @@ from fido2.client import Fido2Client, WindowsClient, UserInteraction, ClientErro
from fido2.ctap2.pin import ClientPin from fido2.ctap2.pin import ClientPin
from fido2.server import Fido2Server from fido2.server import Fido2Server
from fido2.ctap import CtapError from fido2.ctap import CtapError
from fido2.webauthn import CollectedClientData from fido2.webauthn import CollectedClientData, AttestedCredentialData
from getpass import getpass from getpass import getpass
import sys import sys
import pytest import pytest
@@ -106,11 +106,15 @@ class Device():
self.__set_server(rp=self.__rp, attestation=self.__attestation) self.__set_server(rp=self.__rp, attestation=self.__attestation)
def MC(self, client_data_hash=Ellipsis, rp=Ellipsis, user=Ellipsis, key_params=Ellipsis, exclude_list=None, extensions=None, options=None, pin_uv_param=None, pin_uv_protocol=None, enterprise_attestation=None): def MC(self, client_data_hash=Ellipsis, rp=Ellipsis, user=Ellipsis, key_params=Ellipsis, exclude_list=None, extensions=None, options=None, pin_uv_param=None, pin_uv_protocol=None, enterprise_attestation=None):
client_data_hash = client_data_hash if client_data_hash is not Ellipsis else os.urandom(32)
rp = rp if rp is not Ellipsis else self.__rp
user = user if user is not Ellipsis else self.user()
key_params = key_params if key_params is not Ellipsis else self.__server.allowed_algorithms
att_obj = self.__client._backend.ctap2.make_credential( att_obj = self.__client._backend.ctap2.make_credential(
client_data_hash=client_data_hash if client_data_hash is not Ellipsis else os.urandom(32), client_data_hash=client_data_hash,
rp=rp if rp is not Ellipsis else self.__rp, rp=rp,
user=user if user is not Ellipsis else self.user(), user=user,
key_params=key_params if key_params is not Ellipsis else self.__server.allowed_algorithms, key_params=key_params,
exclude_list=exclude_list, exclude_list=exclude_list,
extensions=extensions, extensions=extensions,
options=options, options=options,
@@ -118,17 +122,23 @@ class Device():
pin_uv_protocol=pin_uv_protocol, pin_uv_protocol=pin_uv_protocol,
enterprise_attestation=enterprise_attestation enterprise_attestation=enterprise_attestation
) )
return att_obj return {'res':att_obj,'req':{'client_data_hash':client_data_hash,
'rp':rp,
'user':user,
'key_params':key_params}}
def doMC(self, client_data=Ellipsis, rp=Ellipsis, user=Ellipsis, key_params=Ellipsis, exclude_list=None, extensions=None, rk=None, user_verification=None, enterprise_attestation=None, event=None): def doMC(self, client_data=Ellipsis, rp=Ellipsis, user=Ellipsis, key_params=Ellipsis, exclude_list=None, extensions=None, rk=None, user_verification=None, enterprise_attestation=None, event=None):
client_data = client_data if client_data is not Ellipsis else CollectedClientData.create(
result = self.__client._backend.do_make_credential(
client_data=client_data if client_data is not Ellipsis else CollectedClientData.create(
type=CollectedClientData.TYPE.CREATE, origin=self.__origin, challenge=os.urandom(32) type=CollectedClientData.TYPE.CREATE, origin=self.__origin, challenge=os.urandom(32)
), )
rp=rp if rp is not Ellipsis else self.__rp, rp = rp if rp is not Ellipsis else self.__rp
user=user if user is not Ellipsis else self.user(), user = user if user is not Ellipsis else self.user()
key_params=key_params if key_params is not Ellipsis else self.__server.allowed_algorithms, key_params = key_params if key_params is not Ellipsis else self.__server.allowed_algorithms
result = self.__client._backend.do_make_credential(
client_data=client_data,
rp=rp,
user=user,
key_params=key_params,
exclude_list=exclude_list, exclude_list=exclude_list,
extensions=extensions, extensions=extensions,
rk=rk, rk=rk,
@@ -136,7 +146,10 @@ class Device():
enterprise_attestation=enterprise_attestation, enterprise_attestation=enterprise_attestation,
event=event event=event
) )
return result return {'res':result,'req':{'client_data':client_data,
'rp':rp,
'user':user,
'key_params':key_params}}
def try_make_credential(self, options=None): def try_make_credential(self, options=None):
if (options is None): if (options is None):
@@ -202,32 +215,38 @@ class Device():
print("AUTH DATA:", result.authenticator_data) print("AUTH DATA:", result.authenticator_data)
def GA(self, rp_id=Ellipsis, client_data_hash=Ellipsis, allow_list=None, extensions=None, options=None, pin_uv_param=None, pin_uv_protocol=None): def GA(self, rp_id=Ellipsis, client_data_hash=Ellipsis, allow_list=None, extensions=None, options=None, pin_uv_param=None, pin_uv_protocol=None):
rp_id = rp_id if rp_id is not Ellipsis else self.__rp['id']
client_data_hash = client_data_hash if client_data_hash is not Ellipsis else os.urandom(32)
att_obj = self.__client._backend.ctap2.get_assertion( att_obj = self.__client._backend.ctap2.get_assertion(
rp_id=rp_id if rp_id is not Ellipsis else self.__rp['id'], rp_id=rp_id,
client_data_hash=client_data_hash if client_data_hash is not Ellipsis else os.urandom(32), client_data_hash=client_data_hash,
allow_list=allow_list, allow_list=allow_list,
extensions=extensions, extensions=extensions,
options=options, options=options,
pin_uv_param=pin_uv_param, pin_uv_param=pin_uv_param,
pin_uv_protocol=pin_uv_protocol pin_uv_protocol=pin_uv_protocol
) )
return att_obj return {'res':att_obj,'req':{'rp_id':rp_id,
'client_data_hash':client_data_hash}}
def GNA(self): def GNA(self):
return self.__client._backend.ctap2.get_next_assertion() 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): def doGA(self, client_data=Ellipsis, rp_id=Ellipsis, allow_list=None, extensions=None, user_verification=None, event=None):
result = self.__client._backend.do_get_assertion( client_data = client_data if client_data is not Ellipsis else CollectedClientData.create(
client_data=client_data if client_data is not Ellipsis else CollectedClientData.create(
type=CollectedClientData.TYPE.CREATE, origin=self.__origin, challenge=os.urandom(32) type=CollectedClientData.TYPE.CREATE, origin=self.__origin, challenge=os.urandom(32)
), )
rp_id=rp_id if rp_id is not Ellipsis else self.__rp['id'], rp_id = rp_id if rp_id is not Ellipsis else self.__rp['id']
result = self.__client._backend.do_get_assertion(
client_data=client_data,
rp_id=rp_id,
allow_list=allow_list, allow_list=allow_list,
extensions=extensions, extensions=extensions,
user_verification=user_verification, user_verification=user_verification,
event=event event=event
) )
return result return {'res':result,'req':{'client_data':client_data,
'rp_id':rp_id}}
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
@@ -241,7 +260,7 @@ def info(device):
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
def MCRes(device, *args): def MCRes(device, *args):
return device.doMC(*args).attestation_object return device.doMC(*args)
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
def resetdevice(device): def resetdevice(device):
@@ -250,18 +269,24 @@ def resetdevice(device):
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
def GARes(device, MCRes, *args): def GARes(device, MCRes, *args):
r = device.doGA(allow_list=[ res = device.doGA(allow_list=[
{"id": MCRes.auth_data.credential_data.credential_id, "type": "public-key"} {"id": MCRes['res'].attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
], *args) ], *args)
return r credential_data = AttestedCredentialData(MCRes['res'].attestation_object.auth_data.credential_data)
assertions = res['res'].get_assertions()
for a in assertions:
a.verify(res['req']['client_data'].hash, credential_data.public_key)
return res
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
def MCRes_DC(device, *args): def MCRes_DC(device, *args):
return device.doMC(rk=True, *args).attestation_object return device.doMC(rk=True, *args)
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
def GARes_DC(device, MCRes_DC, *args): def GARes_DC(device, MCRes_DC, *args):
r = device.GA(allow_list=[ res = device.GA(allow_list=[
{"id": MCRes_DC.auth_data.credential_data.credential_id, "type": "public-key"} {"id": MCRes_DC['res'].attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
], *args) ], *args)
return r credential_data = AttestedCredentialData(MCRes_DC['res'].attestation_object.auth_data.credential_data)
res['res'].verify(res['req']['client_data_hash'], credential_data.public_key)
return res

View File

@@ -10,17 +10,17 @@ def test_authenticate(device):
AUTRes = device.authenticate(credentials) AUTRes = device.authenticate(credentials)
def test_assertion_auth_data(GARes): def test_assertion_auth_data(GARes):
assert len(GARes.get_response(0).authenticator_data) == 37 assert len(GARes['res'].get_response(0).authenticator_data) == 37
def test_Check_that_AT_flag_is_not_set(GARes): def test_Check_that_AT_flag_is_not_set(GARes):
assert (GARes.get_response(0).authenticator_data.flags & 0xF8) == 0 assert (GARes['res'].get_response(0).authenticator_data.flags & 0xF8) == 0
def test_that_user_credential_and_numberOfCredentials_are_not_present(device, MCRes): def test_that_user_credential_and_numberOfCredentials_are_not_present(device, MCRes):
res = device.GA(allow_list=[ res = device.GA(allow_list=[
{"id": MCRes.auth_data.credential_data.credential_id, "type": "public-key"} {"id": MCRes['res'].attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
]) ])
assert res.user == None assert res['res'].user == None
assert res.number_of_credentials == None assert res['res'].number_of_credentials == None
def test_empty_allowList(device): def test_empty_allowList(device):
with pytest.raises(CtapError) as e: with pytest.raises(CtapError) as e:
@@ -41,7 +41,7 @@ def test_get_assertion_allow_list_filtering_and_buffering(device):
l1 = 4 l1 = 4
for i in range(0, l1): for i in range(0, l1):
res = device.doMC(rp=rp1).attestation_object res = device.doMC(rp=rp1)['res'].attestation_object
rp1_registrations.append(res) rp1_registrations.append(res)
allow_list.append({ allow_list.append({
"id": res.auth_data.credential_data.credential_id[:], "id": res.auth_data.credential_data.credential_id[:],
@@ -50,7 +50,7 @@ def test_get_assertion_allow_list_filtering_and_buffering(device):
l2 = 6 l2 = 6
for i in range(0, l2): for i in range(0, l2):
res = device.doMC(rp=rp2).attestation_object res = device.doMC(rp=rp2)['res'].attestation_object
rp2_registrations.append(res) rp2_registrations.append(res)
allow_list.append({ allow_list.append({
"id": res.auth_data.credential_data.credential_id[:], "id": res.auth_data.credential_data.credential_id[:],
@@ -66,10 +66,10 @@ def test_get_assertion_allow_list_filtering_and_buffering(device):
# cached. # cached.
# Should authenticate to all credentials matching rp1 # Should authenticate to all credentials matching rp1
rp1_assertions = device.doGA(rp_id=rp1['id'], allow_list=allow_list).get_assertions() rp1_assertions = device.doGA(rp_id=rp1['id'], allow_list=allow_list)['res'].get_assertions()
# Should authenticate to all credentials matching rp2 # Should authenticate to all credentials matching rp2
rp2_assertions = device.doGA(rp_id=rp2['id'], allow_list=allow_list).get_assertions() rp2_assertions = device.doGA(rp_id=rp2['id'], allow_list=allow_list)['res'].get_assertions()
counts = ( counts = (
len(rp1_assertions), len(rp1_assertions),
@@ -80,14 +80,14 @@ def test_get_assertion_allow_list_filtering_and_buffering(device):
def test_corrupt_credId(device, MCRes): def test_corrupt_credId(device, MCRes):
# apply bit flip # apply bit flip
badid = list(MCRes.auth_data.credential_data.credential_id[:]) badid = list(MCRes['res'].attestation_object.auth_data.credential_data.credential_id[:])
badid[len(badid) // 2] = badid[len(badid) // 2] ^ 1 badid[len(badid) // 2] = badid[len(badid) // 2] ^ 1
badid = bytes(badid) badid = bytes(badid)
allow_list = [{"id": badid, "type": "public-key"}] allow_list = [{"id": badid, "type": "public-key"}]
with pytest.raises(CtapError) as e: with pytest.raises(CtapError) as e:
device.doGA(allow_list=allow_list) device.doGA(allow_list=allow_list)['res']
assert e.value.code == CtapError.ERR.NO_CREDENTIALS assert e.value.code == CtapError.ERR.NO_CREDENTIALS
def test_mismatched_rp(device, GARes): def test_mismatched_rp(device, GARes):
@@ -124,38 +124,38 @@ def test_bad_allow_list(device):
def test_bad_allow_list_item(device, MCRes): def test_bad_allow_list_item(device, MCRes):
with pytest.raises(CtapError) as e: with pytest.raises(CtapError) as e:
device.doGA(allow_list=["wrong"] + [ device.doGA(allow_list=["wrong"] + [
{"id": MCRes.auth_data.credential_data.credential_id, "type": "public-key"} {"id": MCRes['res'].attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
] ]
) )
def test_unknown_option(device, MCRes): def test_unknown_option(device, MCRes):
device.GA(options={"unknown": True}, allow_list=[ device.GA(options={"unknown": True}, allow_list=[
{"id": MCRes.auth_data.credential_data.credential_id, "type": "public-key"} {"id": MCRes['res'].attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
]) ])
def test_option_uv(device, info, GARes): def test_option_uv(device, info, GARes):
if "uv" in info.options: if "uv" in info.options:
if info.options["uv"]: if info.options["uv"]:
res = device.doGA(options={"uv": True}) res = device.doGA(options={"uv": True})['res']
assert res.auth_data.flags & (1 << 2) assert res.auth_data.flags & (1 << 2)
def test_option_up(device, info, GARes): def test_option_up(device, info, GARes):
if "up" in info.options: if "up" in info.options:
if info.options["up"]: if info.options["up"]:
res = device.doGA(options={"up": True}) res = device.doGA(options={"up": True})['res']
assert res.auth_data.flags & (1 << 0) assert res.auth_data.flags & (1 << 0)
def test_allow_list_fake_item(device, MCRes): def test_allow_list_fake_item(device, MCRes):
device.doGA(allow_list=[{"type": "rot13", "id": b"1234"}] device.doGA(allow_list=[{"type": "rot13", "id": b"1234"}]
+ [ + [
{"id": MCRes.auth_data.credential_data.credential_id, "type": "public-key"} {"id": MCRes['res'].attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
], ],
) )
def test_allow_list_missing_field(device, MCRes): def test_allow_list_missing_field(device, MCRes):
with pytest.raises(CtapError) as e: with pytest.raises(CtapError) as e:
device.doGA(allow_list=[{"id": b"1234"}] + [ device.doGA(allow_list=[{"id": b"1234"}] + [
{"id": MCRes.auth_data.credential_data.credential_id, "type": "public-key"} {"id": MCRes['res'].attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
] ]
) )
@@ -163,7 +163,7 @@ def test_allow_list_field_wrong_type(device, MCRes):
with pytest.raises(CtapError) as e: with pytest.raises(CtapError) as e:
device.doGA(allow_list=[{"type": b"public-key", "id": b"1234"}] device.doGA(allow_list=[{"type": b"public-key", "id": b"1234"}]
+ [ + [
{"id": MCRes.auth_data.credential_data.credential_id, "type": "public-key"} {"id": MCRes['res'].attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
] ]
) )
@@ -171,20 +171,20 @@ def test_allow_list_id_wrong_type(device, MCRes):
with pytest.raises(CtapError) as e: with pytest.raises(CtapError) as e:
device.doGA(allow_list=[{"type": "public-key", "id": 42}] device.doGA(allow_list=[{"type": "public-key", "id": 42}]
+ [ + [
{"id": MCRes.auth_data.credential_data.credential_id, "type": "public-key"} {"id": MCRes['res'].attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
] ]
) )
def test_allow_list_missing_id(device, MCRes): def test_allow_list_missing_id(device, MCRes):
with pytest.raises(CtapError) as e: with pytest.raises(CtapError) as e:
device.doGA(allow_list=[{"type": "public-key"}] + [ device.doGA(allow_list=[{"type": "public-key"}] + [
{"id": MCRes.auth_data.credential_data.credential_id, "type": "public-key"} {"id": MCRes['res'].attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
] ]
) )
def test_user_presence_option_false(device, MCRes): def test_user_presence_option_false(device, MCRes):
res = device.GA(options={"up": False}, allow_list=[ res = device.GA(options={"up": False}, allow_list=[
{"id": MCRes.auth_data.credential_data.credential_id, "type": "public-key"} {"id": MCRes['res'].attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
]) ])
def test_credential_resets(device, MCRes, GARes): def test_credential_resets(device, MCRes, GARes):

View File

@@ -46,11 +46,11 @@ def generate_user_maximum():
@pytest.mark.parametrize("do_reboot", [False, True]) @pytest.mark.parametrize("do_reboot", [False, True])
def test_user_info_returned_when_using_allowlist(device, MCRes_DC, GARes_DC, do_reboot): def test_user_info_returned_when_using_allowlist(device, MCRes_DC, GARes_DC, do_reboot):
assert "id" in GARes_DC.user.keys() assert "id" in GARes_DC['res'].user.keys()
allow_list = [ allow_list = [
{ {
"id": MCRes_DC.auth_data.credential_data.credential_id[:], "id": MCRes_DC['res'].attestation_object.auth_data.credential_data.credential_id[:],
"type": "public-key", "type": "public-key",
} }
] ]
@@ -58,23 +58,23 @@ def test_user_info_returned_when_using_allowlist(device, MCRes_DC, GARes_DC, do_
if do_reboot: if do_reboot:
device.reboot() device.reboot()
ga_res = device.GA(allow_list=allow_list) ga_res = device.GA(allow_list=allow_list)['res']
assert device.user()["id"] == ga_res.user["id"] assert MCRes_DC["req"]["user"]["id"] == ga_res.user["id"]
def test_with_allow_list_after_reset(device, MCRes_DC, GARes_DC): def test_with_allow_list_after_reset(device, MCRes_DC, GARes_DC):
assert "id" in GARes_DC.user.keys() assert "id" in GARes_DC['res'].user.keys()
allow_list = [ allow_list = [
{ {
"id": MCRes_DC.auth_data.credential_data.credential_id[:], "id": MCRes_DC['res'].attestation_object.auth_data.credential_data.credential_id[:],
"type": "public-key", "type": "public-key",
} }
] ]
ga_res = device.GA(allow_list=allow_list) ga_res = device.GA(allow_list=allow_list)['res']
assert device.user()["id"] == ga_res.user["id"] assert MCRes_DC["req"]["user"]["id"] == ga_res.user["id"]
device.reset() device.reset()
@@ -91,16 +91,16 @@ def test_resident_key_auth(MCRes_DC, GARes_DC):
pass pass
def test_user_info_returned(device, MCRes_DC, GARes_DC): def test_user_info_returned(device, MCRes_DC, GARes_DC):
assert "id" in GARes_DC.user.keys() assert "id" in GARes_DC['res'].user.keys()
assert ( assert (
MCRes_DC.auth_data.credential_data.credential_id MCRes_DC['res'].attestation_object.auth_data.credential_data.credential_id
== GARes_DC.credential["id"] == GARes_DC['res'].credential["id"]
) )
assert device.user()["id"] == GARes_DC.user["id"] assert MCRes_DC["req"]["user"]["id"] == GARes_DC['res'].user["id"]
if not GARes_DC.number_of_credentials: if not GARes_DC['res'].number_of_credentials:
assert "id" in GARes_DC.user.keys() and len(GARes_DC.user.keys()) == 1 assert "id" in GARes_DC['res'].user.keys() and len(GARes_DC['res'].user.keys()) == 1
else: else:
assert device.user() == GARes_DC.user assert MCRes_DC["req"]["user"] == GARes_DC['res'].user
def test_multiple_rk_nodisplay(device, MCRes_DC): def test_multiple_rk_nodisplay(device, MCRes_DC):
@@ -113,7 +113,7 @@ def test_multiple_rk_nodisplay(device, MCRes_DC):
regs.append(res) regs.append(res)
# time.sleep(2) # time.sleep(2)
res = device.doGA(rp_id=rp['id']) res = device.doGA(rp_id=rp['id'])['res']
auths = res.get_assertions() auths = res.get_assertions()
assert len(regs) == 3 assert len(regs) == 3
@@ -125,21 +125,20 @@ def test_multiple_rk_nodisplay(device, MCRes_DC):
print("FAIL: %s was not in user: " % y, x.user) print("FAIL: %s was not in user: " % y, x.user)
def test_rk_maximum_size_nodisplay(device, MCRes_DC): def test_rk_maximum_size_nodisplay(device):
""" """
Check the lengths of the fields according to the FIDO2 spec Check the lengths of the fields according to the FIDO2 spec
https://github.com/solokeys/solo/issues/158#issue-426613303 https://github.com/solokeys/solo/issues/158#issue-426613303
https://www.w3.org/TR/webauthn/#dom-publickeycredentialuserentity-displayname https://www.w3.org/TR/webauthn/#dom-publickeycredentialuserentity-displayname
""" """
auths = [] device.reset()
user_max = generate_user_maximum() user_max = generate_user_maximum()
print(user_max) resMC = device.doMC(user=user_max, rk=True)
resMC = device.doMC(user=user_max) resGA = device.doGA()['res']
resGA = device.doGA()
auths = resGA.get_assertions() auths = resGA.get_assertions()
user_max_GA = auths[0] user_max_GA = auths[0]
print(auths)
for y in ("name", "displayName", "id"): for y in ("name", "displayName", "id"):
assert user_max_GA.user[y] == user_max[y] assert user_max_GA.user[y] == user_max[y]
@@ -172,20 +171,18 @@ def test_rk_maximum_list_capacity_per_rp_nodisplay(info, device, MCRes_DC):
regs = [MCRes_DC] regs = [MCRes_DC]
RK_to_generate = RK_CAPACITY_PER_RP - current_credentials_count RK_to_generate = RK_CAPACITY_PER_RP - current_credentials_count
for i in range(RK_to_generate): for i in range(RK_to_generate):
req = FidoRequest(MCRes_DC, user=get_user(), rp = rp) res = device.doMC(user=get_user(), rp=rp, rk=True)['res'].attestation_object
res = device.sendMC(*req.toMC())
regs.append(res) regs.append(res)
req = FidoRequest(MCRes_DC, options=None, user=generate_user_maximum(), rp = rp) res = device.GA(rp_id = rp['id'])['res']
res = device.sendGA(*req.toGA())
assert res.number_of_credentials == RK_CAPACITY_PER_RP assert res.number_of_credentials == RK_CAPACITY_PER_RP
auths.append(res) auths.append(res)
for i in range(RK_CAPACITY_PER_RP - 1): for i in range(RK_CAPACITY_PER_RP - 1):
auths.append(device.ctap2.get_next_assertion()) auths.append(device.GNA())
with pytest.raises(CtapError) as e: with pytest.raises(CtapError) as e:
device.ctap2.get_next_assertion() device.GNA()
auths = auths[::-1][-RK_to_generate:] auths = auths[::-1][-RK_to_generate:]
regs = regs[-RK_to_generate:] regs = regs[-RK_to_generate:]
@@ -193,29 +190,24 @@ def test_rk_maximum_list_capacity_per_rp_nodisplay(info, device, MCRes_DC):
assert len(auths) == len(users) assert len(auths) == len(users)
if MCRes_DC.request.pin_protocol: for x, u in zip(auths, users):
for x, u in zip(auths, users): for y in ("name", "displayName", "id"):
for y in ("name", "icon", "displayName", "id"): assert y in x.user.keys()
assert y in x.user.keys() assert x.user[y] == u[y]
assert x.user[y] == u[y]
assert len(auths) == len(regs) assert len(auths) == len(regs)
for x, y in zip(regs, auths):
verify(x, y, req.cdh)
def test_rk_with_allowlist_of_different_rp(resetDevice): def test_rk_with_allowlist_of_different_rp(resetdevice):
""" """
Test that a rk credential is not found when using an allowList item for a different RP Test that a rk credential is not found when using an allowList item for a different RP
""" """
rk_rp = {"id": "rk-cred.org", "name": "Example"} rk_rp = {"id": "rk-cred.org", "name": "Example"}
rk_req = FidoRequest(rp = rk_rp, options={"rk": True}) rk_res = resetdevice.doMC(rp = rk_rp, rk=True)['res'].attestation_object
rk_res = resetDevice.sendMC(*rk_req.toMC())
server_rp = {"id": "server-cred.com", "name": "Example"} server_rp = {"id": "server-cred.com", "name": "Example"}
server_req = FidoRequest(rp = server_rp) server_res = resetdevice.doMC(rp = server_rp, rk=True)['res'].attestation_object
server_res = resetDevice.sendMC(*server_req.toMC())
allow_list_with_different_rp_cred = [ allow_list_with_different_rp_cred = [
{ {
@@ -224,43 +216,39 @@ def test_rk_with_allowlist_of_different_rp(resetDevice):
} }
] ]
test_req = FidoRequest(rp = rk_rp, allow_list = allow_list_with_different_rp_cred)
with pytest.raises(CtapError) as e: with pytest.raises(CtapError) as e:
res = resetDevice.sendGA(*test_req.toGA()) res = resetdevice.doGA(rp_id = rk_rp['id'], allow_list = allow_list_with_different_rp_cred)
assert e.value.code == CtapError.ERR.NO_CREDENTIALS assert e.value.code == CtapError.ERR.NO_CREDENTIALS
def test_same_userId_overwrites_rk(resetDevice): def test_same_userId_overwrites_rk(resetdevice):
""" """
A make credential request with a UserId & Rp that is the same as an existing one should overwrite. A make credential request with a UserId & Rp that is the same as an existing one should overwrite.
""" """
rp = {"id": "overwrite.org", "name": "Example"} rp = {"id": "overwrite.org", "name": "Example"}
user = generate_user() user = generate_random_user()
req = FidoRequest(rp = rp, options={"rk": True}, user = user) mc_res1 = resetdevice.doMC(rp = rp, rk=True, user = user)
mc_res1 = resetDevice.sendMC(*req.toMC())
# Should overwrite the first credential. # Should overwrite the first credential.
mc_res2 = resetDevice.sendMC(*req.toMC()) mc_res2 = resetdevice.doMC(rp = rp, rk=True, user = user)
ga_res = resetDevice.sendGA(*req.toGA()) ga_res = resetdevice.GA(rp_id=rp['id'])['res']
# If there's only one credential, this is None # If there's only one credential, this is None
assert ga_res.number_of_credentials == None assert ga_res.number_of_credentials == None
verify(mc_res2, ga_res, req.cdh)
def test_larger_icon_than_128(device): def test_larger_icon_than_128(device):
""" """
Test it works if we give an icon value larger than 128 bytes Test it works if we give an icon value larger than 128 bytes
""" """
rp = {"id": "overwrite.org", "name": "Example"} rp = {"id": "overwrite.org", "name": "Example"}
user = generate_user() user = generate_random_user()
user['icon'] = 'https://www.w3.org/TR/webauthn/?icon=' + ("A" * 128) user['icon'] = 'https://www.w3.org/TR/webauthn/?icon=' + ("A" * 128)
req = FidoRequest(rp = rp, options={"rk": True}, user = user) device.doMC(rp = rp, rk=True, user = user)
device.sendMC(*req.toMC())
def test_returned_credential(device): def test_returned_credential(device):
@@ -269,29 +257,24 @@ def test_returned_credential(device):
only 1 will get returned. only 1 will get returned.
""" """
device.reset() device.reset()
pin = '12345'
device.client.pin_protocol.set_pin(pin)
req = FidoRequest(pin = pin, options={"rk": True})
regs = [] regs = []
allow_list = [] allow_list = []
for i in range(0, 2): for i in range(0, 2):
req = FidoRequest(req, user = { res = device.doMC(rk=True, user = {
"id": b'123456' + bytes([i]), "name": f'Test User {i}', "displayName": f'Test User display {i}' "id": b'123456' + bytes([i]), "name": f'Test User {i}', "displayName": f'Test User display {i}'
}) })['res'].attestation_object
res = device.sendMC(*req.toMC())
setattr(res, "request", req)
regs.append(res) regs.append(res)
allow_list.append({"id": res.auth_data.credential_data.credential_id[:], "type": "public-key"}) allow_list.append({"id": res.auth_data.credential_data.credential_id[:], "type": "public-key"})
print('allow_list: ' , allow_list) print('allow_list: ' , allow_list)
ga_req = FidoRequest(pin = pin, allow_list=allow_list) ga_res = device.GA(allow_list=allow_list)['res']
ga_res = device.sendGA(*ga_req.toGA()) print(ga_res)
# No other credentials should be returned # No other credentials should be returned
with pytest.raises(CtapError) as e: with pytest.raises(CtapError) as e:
device.ctap2.get_next_assertion() device.GNA()
# the returned credential should have user id in it # the returned credential should have user id in it
print(ga_res) print(ga_res)

View File

@@ -11,10 +11,10 @@ def test_make_credential():
pass pass
def test_attestation_format(MCRes): def test_attestation_format(MCRes):
assert MCRes.fmt in ["packed", "tpm", "android-key", "adroid-safetynet"] assert MCRes['res'].attestation_object.fmt in ["packed", "tpm", "android-key", "adroid-safetynet"]
def test_authdata_length(MCRes): def test_authdata_length(MCRes):
assert len(MCRes.auth_data) >= 77 assert len(MCRes['res'].attestation_object.auth_data) >= 77
def test_missing_cdh(device): def test_missing_cdh(device):
with pytest.raises(CtapError) as e: with pytest.raises(CtapError) as e:
@@ -152,7 +152,7 @@ def test_bad_type_exclude_list_type(device):
device.doMC(exclude_list=[{"type": b"public-key", "id": b"1234"}]) device.doMC(exclude_list=[{"type": b"public-key", "id": b"1234"}])
def test_exclude_list_excluded(device): def test_exclude_list_excluded(device):
res = device.doMC().attestation_object res = device.doMC()['res'].attestation_object
with pytest.raises(CtapError) as e: with pytest.raises(CtapError) as e:
device.doMC(exclude_list=[ device.doMC(exclude_list=[
{"id": res.auth_data.credential_data.credential_id, "type": "public-key"} {"id": res.auth_data.credential_data.credential_id, "type": "public-key"}