Add tests to demonstrate broken user presence timeouts (#520)
* Add tests to demonstrate broken user presence timeouts * fix up some formatting * apply auto-formatting
This commit is contained in:
@@ -1,7 +1,12 @@
|
|||||||
"""These tests verify the functionality of the VendorHID interface."""
|
"""These tests verify the functionality of the VendorHID interface."""
|
||||||
from typing import Dict, Iterable
|
from fido2 import ctap
|
||||||
|
from fido2.hid import CtapHidDevice
|
||||||
|
from fido2.hid.base import CtapHidConnection
|
||||||
|
from fido2.client import Fido2Client, UserInteraction, ClientError
|
||||||
|
from fido2.server import Fido2Server
|
||||||
import hid
|
import hid
|
||||||
import time
|
import time
|
||||||
|
from typing import Dict, Iterable
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
_OPENSK_VID = 0x1915
|
_OPENSK_VID = 0x1915
|
||||||
@@ -12,6 +17,7 @@ _PACKETS = 4
|
|||||||
_PACKET_SIZE = 64
|
_PACKET_SIZE = 64
|
||||||
_SEND_DATA_SIZE = _PACKET_SIZE + 1
|
_SEND_DATA_SIZE = _PACKET_SIZE + 1
|
||||||
_BROADCAST_CID = bytes([0xFF, 0xFF, 0xFF, 0xFF])
|
_BROADCAST_CID = bytes([0xFF, 0xFF, 0xFF, 0xFF])
|
||||||
|
_TEST_USER = {'id': b'user_id', 'name': 'Foo User'}
|
||||||
|
|
||||||
|
|
||||||
def sleep():
|
def sleep():
|
||||||
@@ -265,5 +271,87 @@ class HidInterfaces(unittest.TestCase):
|
|||||||
self.fido_hid)
|
self.fido_hid)
|
||||||
|
|
||||||
|
|
||||||
|
def get_fido_device() -> CtapHidDevice:
|
||||||
|
for d in CtapHidDevice.list_devices():
|
||||||
|
if d.descriptor.vid == _OPENSK_VID and d.descriptor.pid == _OPENSK_PID:
|
||||||
|
return d
|
||||||
|
raise Exception('Unable to find Fido device')
|
||||||
|
|
||||||
|
|
||||||
|
class CliInteraction(UserInteraction):
|
||||||
|
"""Sends cancel messages while prompting user."""
|
||||||
|
|
||||||
|
def __init__(self, cid=None, connection: CtapHidConnection = None):
|
||||||
|
super(CliInteraction).__init__()
|
||||||
|
self.cid = cid
|
||||||
|
self.connection = connection
|
||||||
|
|
||||||
|
def prompt_up(self) -> None:
|
||||||
|
print('\n Don\'t touch your authenticator device now...\n')
|
||||||
|
# Send cancel messages to the specified device.
|
||||||
|
if self.connection and self.cid:
|
||||||
|
cancel_packet = (
|
||||||
|
self.cid.to_bytes(4, byteorder='big') + b'\x91' +
|
||||||
|
b''.join([b'\x00'] * 59))
|
||||||
|
assert len(cancel_packet) == _PACKET_SIZE, (
|
||||||
|
f'Expected packet to be {_PACKET_SIZE} '
|
||||||
|
'but was {len(cancel_packet)}')
|
||||||
|
|
||||||
|
self.connection.write_packet(cancel_packet)
|
||||||
|
|
||||||
|
|
||||||
|
class CancelTests(unittest.TestCase):
|
||||||
|
"""Tests for the canceling while waiting for user touch."""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
cls.fido = get_fido_device()
|
||||||
|
|
||||||
|
def setUp(self) -> None:
|
||||||
|
super().setUp()
|
||||||
|
server = Fido2Server({
|
||||||
|
'id': 'example.com',
|
||||||
|
'name': 'Example RP'
|
||||||
|
},
|
||||||
|
attestation='direct')
|
||||||
|
self.create_options, _ = server.register_begin(
|
||||||
|
_TEST_USER,
|
||||||
|
user_verification='foo',
|
||||||
|
authenticator_attachment='cross-platform')
|
||||||
|
|
||||||
|
def test_cancel_works(self):
|
||||||
|
cid = self.fido._channel_id # pylint: disable=protected-access
|
||||||
|
connection = self.fido._connection # pylint: disable=protected-access
|
||||||
|
client = Fido2Client(
|
||||||
|
self.fido,
|
||||||
|
'https://example.com',
|
||||||
|
user_interaction=CliInteraction(cid, connection))
|
||||||
|
|
||||||
|
with self.assertRaises(ClientError) as context:
|
||||||
|
client.make_credential(self.create_options['publicKey'])
|
||||||
|
|
||||||
|
self.assertEqual(context.exception.code, ClientError.ERR.TIMEOUT)
|
||||||
|
self.assertEqual(context.exception.cause.code,
|
||||||
|
ctap.CtapError.ERR.KEEPALIVE_CANCEL)
|
||||||
|
|
||||||
|
def test_cancel_ignores_wrong_cid(self):
|
||||||
|
cid = self.fido._channel_id # pylint: disable=protected-access
|
||||||
|
connection = self.fido._connection # pylint: disable=protected-access
|
||||||
|
client = Fido2Client(
|
||||||
|
self.fido,
|
||||||
|
'https://example.com',
|
||||||
|
user_interaction=CliInteraction(cid + 1, connection))
|
||||||
|
# TODO(https://github.com/google/OpenSK/issues/519): This should be an
|
||||||
|
# exception
|
||||||
|
client.make_credential(self.create_options['publicKey'])
|
||||||
|
|
||||||
|
def test_timeout(self):
|
||||||
|
client = Fido2Client(
|
||||||
|
self.fido, 'https://example.com', user_interaction=CliInteraction())
|
||||||
|
# TODO(https://github.com/google/OpenSK/issues/519): This should be an
|
||||||
|
# exception
|
||||||
|
client.make_credential(self.create_options['publicKey'])
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user