Skip to content

Commit

Permalink
First commit
Browse files Browse the repository at this point in the history
  • Loading branch information
conorpp committed Aug 1, 2019
1 parent ffe2d5e commit 59d44c1
Show file tree
Hide file tree
Showing 8 changed files with 457 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,5 @@ venv.bak/

# mypy
.mypy_cache/

*.swp
4 changes: 4 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fido2
solo-python
pytest
pytest-ordering
142 changes: 142 additions & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -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))
231 changes: 231 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -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)
7 changes: 7 additions & 0 deletions tests/connect_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import tests
import pytest


@pytest.mark.run(order=1)
def test_answer(device):
pass
Loading

0 comments on commit 59d44c1

Please sign in to comment.