You've already forked fido2-tests
mirror of
https://github.com/trussed-dev/fido2-tests.git
synced 2026-06-20 04:16:30 -07:00
First commit
This commit is contained in:
@@ -102,3 +102,5 @@ venv.bak/
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
|
||||
*.swp
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
fido2
|
||||
solo-python
|
||||
pytest
|
||||
pytest-ordering
|
||||
@@ -0,0 +1,142 @@
|
||||
import struct, time
|
||||
|
||||
import pytest
|
||||
|
||||
from fido2.hid import CtapHidDevice
|
||||
from fido2.client import Fido2Client
|
||||
from fido2.utils import Timeout
|
||||
|
||||
from solo.fido2 import force_udp_backend
|
||||
|
||||
__device = None
|
||||
|
||||
origin = "https://solokeys.com"
|
||||
|
||||
|
||||
class Packet(object):
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
|
||||
def ToWireFormat(self,):
|
||||
return self.data
|
||||
|
||||
@staticmethod
|
||||
def FromWireFormat(pkt_size, data):
|
||||
return Packet(data)
|
||||
|
||||
|
||||
def cid():
|
||||
return __device._dev.cid
|
||||
|
||||
|
||||
def set_cid(cid):
|
||||
if not isinstance(cid, (bytes, bytearray)):
|
||||
cid = struct.pack("%dB" % len(cid), *[ord(x) for x in cid])
|
||||
__device._dev.cid = cid
|
||||
|
||||
|
||||
def recv_raw():
|
||||
with Timeout(1.0):
|
||||
cmd, payload = __device._dev.InternalRecv()
|
||||
return cmd, payload
|
||||
|
||||
|
||||
def send_data(cmd, data):
|
||||
if not isinstance(data, bytes):
|
||||
data = struct.pack("%dB" % len(data), *[ord(x) for x in data])
|
||||
with Timeout(1.0) as event:
|
||||
return __device.call(cmd, data, event)
|
||||
|
||||
|
||||
def send_raw(data, cid=None):
|
||||
if cid is None:
|
||||
cid = __device._dev.cid
|
||||
elif not isinstance(cid, bytes):
|
||||
cid = struct.pack("%dB" % len(cid), *[ord(x) for x in cid])
|
||||
if not isinstance(data, bytes):
|
||||
data = struct.pack("%dB" % len(data), *[ord(x) for x in data])
|
||||
data = cid + data
|
||||
l = len(data)
|
||||
if l != 64:
|
||||
pad = "\x00" * (64 - l)
|
||||
pad = struct.pack("%dB" % len(pad), *[ord(x) for x in pad])
|
||||
data = data + pad
|
||||
data = list(data)
|
||||
assert len(data) == 64
|
||||
__device._dev.InternalSendPacket(Packet(data))
|
||||
|
||||
|
||||
def send_magic_reboot():
|
||||
"""
|
||||
For use in simulation and testing. Random bytes that authentictor should detect
|
||||
and then restart itself.
|
||||
"""
|
||||
magic_cmd = (
|
||||
b"\xac\x10\x52\xca\x95\xe5\x69\xde\x69\xe0\x2e\xbf"
|
||||
+ b"\xf3\x33\x48\x5f\x13\xf9\xb2\xda\x34\xc5\xa8\xa3"
|
||||
+ b"\x40\x52\x66\x97\xa9\xab\x2e\x0b\x39\x4d\x8d\x04"
|
||||
+ b"\x97\x3c\x13\x40\x05\xbe\x1a\x01\x40\xbf\xf6\x04"
|
||||
+ b"\x5b\xb2\x6e\xb7\x7a\x73\xea\xa4\x78\x13\xf6\xb4"
|
||||
+ b"\x9a\x72\x50\xdc"
|
||||
)
|
||||
__device.dev._dev.InternalSendPacket(Packet(magic_cmd))
|
||||
|
||||
|
||||
def reboot():
|
||||
if is_simulation:
|
||||
print("Sending restart command...")
|
||||
self.send_magic_reboot()
|
||||
Tester.delay(0.25)
|
||||
else:
|
||||
print("Please reboot authentictor and hit enter")
|
||||
input()
|
||||
self.find_device(self.nfc_interface_only)
|
||||
|
||||
|
||||
def find_device(nfcInterfaceOnly=False):
|
||||
print(is_simulation)
|
||||
if is_simulation:
|
||||
print("FORCE UDP")
|
||||
force_udp_backend()
|
||||
dev = None
|
||||
nfcInterfaceOnly
|
||||
if not nfcInterfaceOnly:
|
||||
print("--- HID ---")
|
||||
print(list(CtapHidDevice.list_devices()))
|
||||
dev = next(CtapHidDevice.list_devices(), None)
|
||||
|
||||
if not dev:
|
||||
from fido2.pcsc import CtapPcscDevice
|
||||
|
||||
print("--- NFC ---")
|
||||
print(list(CtapPcscDevice.list_devices()))
|
||||
dev = next(CtapPcscDevice.list_devices(), None)
|
||||
|
||||
if not dev:
|
||||
raise RuntimeError("No FIDO device found")
|
||||
|
||||
return Fido2Client(dev, origin)
|
||||
|
||||
|
||||
def set_device(dev):
|
||||
__device = dev
|
||||
|
||||
|
||||
def _get_device(refresh=False):
|
||||
|
||||
print(0)
|
||||
dev = find_device()
|
||||
print(1, dev)
|
||||
yield dev
|
||||
|
||||
while True:
|
||||
if refresh:
|
||||
print("REFRESH")
|
||||
dev = find_device()
|
||||
print(2, dev)
|
||||
yield dev
|
||||
|
||||
|
||||
def get_device(*args):
|
||||
time.sleep(0.5)
|
||||
return next(_get_device(*args))
|
||||
@@ -0,0 +1,231 @@
|
||||
import pytest
|
||||
|
||||
import time
|
||||
import struct
|
||||
|
||||
from fido2.hid import CtapHidDevice
|
||||
from fido2.client import Fido2Client
|
||||
from fido2.attestation import Attestation
|
||||
from fido2.ctap1 import CTAP1
|
||||
from fido2.utils import Timeout
|
||||
from fido2.ctap import CtapError
|
||||
from fido2.ctap2 import ES256, PinProtocolV1, AttestedCredentialData
|
||||
from fido2.utils import sha256, hmac_sha256
|
||||
|
||||
|
||||
from solo.fido2 import force_udp_backend
|
||||
|
||||
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption("--sim", action="store_true")
|
||||
parser.addoption("--nfc", action="store_true")
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def is_simulation(pytestconfig):
|
||||
return pytestconfig.getoption("sim")
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def is_nfc(pytestconfig):
|
||||
return pytestconfig.getoption("nfc")
|
||||
|
||||
@pytest.fixture()
|
||||
def MCParams():
|
||||
rp = {"id": "examplo.org", "name": "ExaRP"}
|
||||
rp2 = {"id": "solokeys.com", "name": "ExaRP"}
|
||||
user = {"id": b"usee_od", "name": "AB User"}
|
||||
user1 = {"id": b"1234567890", "name": "Conor Patrick"}
|
||||
user2 = {"id": b"oiewhfoi", "name": "Han Solo"}
|
||||
user3 = {"id": b"23ohfpjwo@@", "name": "John Smith"}
|
||||
challenge = "Y2hhbGxlbmdl"
|
||||
pin_protocol = 1
|
||||
key_params = [{"type": "public-key", "alg": ES256.ALGORITHM}]
|
||||
cdh = b"123456789abcdef0123456789abcdef0"
|
||||
return [cdh, rp, user, key_params]
|
||||
|
||||
def GAParams():
|
||||
rp = {"id": "examplo.org", "name": "ExaRP"}
|
||||
rp2 = {"id": "solokeys.com", "name": "ExaRP"}
|
||||
user = {"id": b"usee_od", "name": "AB User"}
|
||||
user1 = {"id": b"1234567890", "name": "Conor Patrick"}
|
||||
user2 = {"id": b"oiewhfoi", "name": "Han Solo"}
|
||||
user3 = {"id": b"23ohfpjwo@@", "name": "John Smith"}
|
||||
challenge = "Y2hhbGxlbmdl"
|
||||
pin_protocol = 1
|
||||
key_params = [{"type": "public-key", "alg": ES256.ALGORITHM}]
|
||||
cdh = b"123456789abcdef0123456789abcdef0"
|
||||
pass
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def device(pytestconfig):
|
||||
if pytestconfig.getoption("sim"):
|
||||
print("FORCE UDP")
|
||||
force_udp_backend()
|
||||
|
||||
dev = TestDevice()
|
||||
dev.set_sim(pytestconfig.getoption("sim"))
|
||||
|
||||
dev.find_device(pytestconfig.getoption("nfc"))
|
||||
|
||||
return dev
|
||||
|
||||
|
||||
class TestDevice:
|
||||
def __init__(self, tester=None):
|
||||
self.origin = "https://examplo.org"
|
||||
self.host = "examplo.org"
|
||||
self.user_count = 10
|
||||
self.is_sim = False
|
||||
self.nfc_interface_only = False
|
||||
if tester:
|
||||
self.initFromTester(tester)
|
||||
|
||||
def initFromTester(self, tester):
|
||||
self.user_count = tester.user_count
|
||||
self.is_sim = tester.is_sim
|
||||
self.dev = tester.dev
|
||||
self.ctap2 = tester.ctap2
|
||||
self.ctap1 = tester.ctap1
|
||||
self.client = tester.client
|
||||
self.nfc_interface_only = tester.nfc_interface_only
|
||||
|
||||
def find_device(self, nfcInterfaceOnly=False):
|
||||
dev = None
|
||||
self.nfc_interface_only = nfcInterfaceOnly
|
||||
if not nfcInterfaceOnly:
|
||||
print("--- HID ---")
|
||||
print(list(CtapHidDevice.list_devices()))
|
||||
dev = next(CtapHidDevice.list_devices(), None)
|
||||
|
||||
if not dev:
|
||||
from fido2.pcsc import CtapPcscDevice
|
||||
|
||||
print("--- NFC ---")
|
||||
print(list(CtapPcscDevice.list_devices()))
|
||||
dev = next(CtapPcscDevice.list_devices(), None)
|
||||
|
||||
if not dev:
|
||||
raise RuntimeError("No FIDO device found")
|
||||
self.dev = dev
|
||||
self.client = Fido2Client(dev, self.origin)
|
||||
self.ctap2 = self.client.ctap2
|
||||
self.ctap1 = CTAP1(dev)
|
||||
|
||||
# consume timeout error
|
||||
# cmd,resp = self.recv_raw()
|
||||
|
||||
def set_user_count(self, count):
|
||||
self.user_count = count
|
||||
|
||||
def set_sim(self, b):
|
||||
self.is_sim = b
|
||||
|
||||
def reboot(self,):
|
||||
if self.is_sim:
|
||||
print("Sending restart command...")
|
||||
self.send_magic_reboot()
|
||||
Tester.delay(0.25)
|
||||
else:
|
||||
print("Please reboot authentictor and hit enter")
|
||||
input()
|
||||
self.find_device(self.nfc_interface_only)
|
||||
|
||||
def send_data(self, cmd, data):
|
||||
if not isinstance(data, bytes):
|
||||
data = struct.pack("%dB" % len(data), *[ord(x) for x in data])
|
||||
with Timeout(1.0) as event:
|
||||
return self.dev.call(cmd, data, event)
|
||||
|
||||
def send_raw(self, data, cid=None):
|
||||
if cid is None:
|
||||
cid = self.dev._dev.cid
|
||||
elif not isinstance(cid, bytes):
|
||||
cid = struct.pack("%dB" % len(cid), *[ord(x) for x in cid])
|
||||
if not isinstance(data, bytes):
|
||||
data = struct.pack("%dB" % len(data), *[ord(x) for x in data])
|
||||
data = cid + data
|
||||
l = len(data)
|
||||
if l != 64:
|
||||
pad = "\x00" * (64 - l)
|
||||
pad = struct.pack("%dB" % len(pad), *[ord(x) for x in pad])
|
||||
data = data + pad
|
||||
data = list(data)
|
||||
assert len(data) == 64
|
||||
self.dev._dev.InternalSendPacket(Packet(data))
|
||||
|
||||
def send_magic_reboot(self,):
|
||||
"""
|
||||
For use in simulation and testing. Random bytes that authentictor should detect
|
||||
and then restart itself.
|
||||
"""
|
||||
magic_cmd = (
|
||||
b"\xac\x10\x52\xca\x95\xe5\x69\xde\x69\xe0\x2e\xbf"
|
||||
+ b"\xf3\x33\x48\x5f\x13\xf9\xb2\xda\x34\xc5\xa8\xa3"
|
||||
+ b"\x40\x52\x66\x97\xa9\xab\x2e\x0b\x39\x4d\x8d\x04"
|
||||
+ b"\x97\x3c\x13\x40\x05\xbe\x1a\x01\x40\xbf\xf6\x04"
|
||||
+ b"\x5b\xb2\x6e\xb7\x7a\x73\xea\xa4\x78\x13\xf6\xb4"
|
||||
+ b"\x9a\x72\x50\xdc"
|
||||
)
|
||||
self.dev._dev.InternalSendPacket(Packet(magic_cmd))
|
||||
|
||||
def cid(self,):
|
||||
return self.dev._dev.cid
|
||||
|
||||
def set_cid(self, cid):
|
||||
if not isinstance(cid, (bytes, bytearray)):
|
||||
cid = struct.pack("%dB" % len(cid), *[ord(x) for x in cid])
|
||||
self.dev._dev.cid = cid
|
||||
|
||||
def recv_raw(self,):
|
||||
with Timeout(1.0):
|
||||
cmd, payload = self.dev._dev.InternalRecv()
|
||||
return cmd, payload
|
||||
|
||||
def check_error(data, err=None):
|
||||
assert len(data) == 1
|
||||
if err is None:
|
||||
if data[0] != 0:
|
||||
raise CtapError(data[0])
|
||||
elif data[0] != err:
|
||||
raise ValueError("Unexpected error: %02x" % data[0])
|
||||
|
||||
def reset(self,):
|
||||
print("Resetting Authenticator...")
|
||||
try:
|
||||
self.ctap2.reset()
|
||||
except CtapError:
|
||||
# Some authenticators need a power cycle
|
||||
print("You must power cycle authentictor. Hit enter when done.")
|
||||
input()
|
||||
time.sleep(0.2)
|
||||
self.find_device(self.nfc_interface_only)
|
||||
self.ctap2.reset()
|
||||
|
||||
def sendMC(self, *args, **kwargs):
|
||||
attestation_object = self.ctap2.make_credential(
|
||||
*args, **kwargs
|
||||
)
|
||||
if attestation_object:
|
||||
verifier = Attestation.for_type(attestation_object.fmt)
|
||||
client_data = args[0]
|
||||
verifier().verify(
|
||||
attestation_object.att_statement,
|
||||
attestation_object.auth_data,
|
||||
client_data,
|
||||
)
|
||||
return attestation_object
|
||||
|
||||
def sendGA(self, *args, **kwargs):
|
||||
return self.ctap2.get_assertion(*args, **kwargs)
|
||||
|
||||
def sendCP(self, *args, **kwargs):
|
||||
return self.ctap2.client_pin(*args, **kwargs)
|
||||
|
||||
def sendPP(self, *args, **kwargs):
|
||||
return self.client.pin_protocol.get_pin_token(test, *args, **kwargs)
|
||||
|
||||
|
||||
def delay(secs):
|
||||
time.sleep(secs)
|
||||
@@ -0,0 +1,7 @@
|
||||
import tests
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.run(order=1)
|
||||
def test_answer(device):
|
||||
pass
|
||||
@@ -0,0 +1,17 @@
|
||||
import time
|
||||
|
||||
import tests
|
||||
|
||||
|
||||
def reset():
|
||||
print("Resetting Authenticator...")
|
||||
dev = tests.get_device()
|
||||
try:
|
||||
dev.ctap2.reset()
|
||||
except CtapError:
|
||||
# Some authenticators need a power cycle
|
||||
print("You must power cycle authentictor. Hit enter when done.")
|
||||
input()
|
||||
time.sleep(0.2)
|
||||
dev = tests.get_device(refresh=True)
|
||||
dev.ctap2.reset()
|
||||
@@ -0,0 +1,47 @@
|
||||
import pytest
|
||||
|
||||
from fido2 import cbor
|
||||
from fido2.ctap import CtapError
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def info(device):
|
||||
info = device.ctap2.get_info()
|
||||
print("data:", bytes(info))
|
||||
print("decoded:", cbor.decode_from(bytes(info)))
|
||||
return info
|
||||
|
||||
|
||||
def test_get_info(info):
|
||||
pass
|
||||
|
||||
|
||||
def test_get_info_version(info):
|
||||
assert "FIDO_2_0" in info.versions
|
||||
|
||||
|
||||
def test_Check_pin_protocols_field(info):
|
||||
if len(info.pin_protocols):
|
||||
assert sum(info.pin_protocols) > 0
|
||||
|
||||
def test_Check_options_field(info):
|
||||
for x in info.options:
|
||||
assert info.options[x] in [True, False]
|
||||
|
||||
def test_Check_uv_option(device, info, MCParams):
|
||||
if "uv" in info.options:
|
||||
if info.options["uv"]:
|
||||
device.sendMC(
|
||||
*MCParams,
|
||||
options = {"uv": True},
|
||||
)
|
||||
|
||||
def test_Check_up_option(device,info,MCParams):
|
||||
if "up" in info.options:
|
||||
if info.options["up"]:
|
||||
with pytest.raises(CtapError) as e:
|
||||
device.sendMC(
|
||||
*MCParams,
|
||||
options = {"up": True},
|
||||
)
|
||||
assert e.value.code == CtapError.ERR.INVALID_OPTION
|
||||
@@ -0,0 +1,7 @@
|
||||
import tests
|
||||
|
||||
from fido2.ctap import CtapError
|
||||
|
||||
|
||||
def test_reset(device):
|
||||
device.reset()
|
||||
Reference in New Issue
Block a user