|
| 1 | +from fido2.ctap import CtapError |
| 2 | +from fido2.ctap2.bio import BioEnrollment, FPBioEnrollment, CaptureError |
| 3 | +from fido2.ctap2.pin import ClientPin |
| 4 | +from fido2.server import Fido2Server |
| 5 | +from fido2.client import Fido2Client, ClientError |
| 6 | + |
| 7 | +from . import TEST_PIN, CliInteraction |
| 8 | +import pytest |
| 9 | + |
| 10 | + |
| 11 | +@pytest.fixture(autouse=True, scope="module") |
| 12 | +def preconditions(dev_manager): |
| 13 | + if not BioEnrollment.is_supported(dev_manager.info): |
| 14 | + pytest.skip("BioEnrollment not supported by authenticator") |
| 15 | + assert dev_manager.info.options["uv"] is False |
| 16 | + |
| 17 | + |
| 18 | +def get_bio(ctap2, pin_protocol=None, permissions=ClientPin.PERMISSION.BIO_ENROLL): |
| 19 | + if pin_protocol: |
| 20 | + token = ClientPin(ctap2, pin_protocol).get_pin_token(TEST_PIN, permissions) |
| 21 | + else: |
| 22 | + token = None |
| 23 | + return FPBioEnrollment(ctap2, pin_protocol, token) |
| 24 | + |
| 25 | + |
| 26 | +def test_get_sensor_info(ctap2): |
| 27 | + bio = get_bio(ctap2) |
| 28 | + info = bio.get_fingerprint_sensor_info() |
| 29 | + assert info.get(2) in (1, None) |
| 30 | + assert info.get(3, 1) > 0 |
| 31 | + assert info.get(8, 1) > 0 |
| 32 | + |
| 33 | + |
| 34 | +def test_enroll_use_delete(device, ctap2, pin_protocol, printer): |
| 35 | + bio = get_bio(ctap2, pin_protocol) |
| 36 | + assert len(bio.enumerate_enrollments()) == 0 |
| 37 | + |
| 38 | + context = bio.enroll() |
| 39 | + template_id = None |
| 40 | + while template_id is None: |
| 41 | + printer.print("Press your fingerprint against the sensor now...") |
| 42 | + try: |
| 43 | + template_id = context.capture() |
| 44 | + printer.print(f"{context.remaining} more scans needed.") |
| 45 | + except CaptureError as e: |
| 46 | + printer.print(e) |
| 47 | + |
| 48 | + enrollments = bio.enumerate_enrollments() |
| 49 | + assert len(enrollments) == 1 |
| 50 | + assert enrollments[template_id] in ("", None) |
| 51 | + |
| 52 | + # Test name/rename |
| 53 | + info = bio.get_fingerprint_sensor_info() |
| 54 | + fname = "Test 1" |
| 55 | + bio.set_name(template_id, fname) |
| 56 | + |
| 57 | + enrollments = bio.enumerate_enrollments() |
| 58 | + assert len(enrollments) == 1 |
| 59 | + assert enrollments[template_id] == fname |
| 60 | + |
| 61 | + fname = "Test".ljust(info.get(8, 0), "!") |
| 62 | + bio.set_name(template_id, fname) |
| 63 | + enrollments = bio.enumerate_enrollments() |
| 64 | + assert len(enrollments) == 1 |
| 65 | + assert enrollments[template_id] == fname |
| 66 | + |
| 67 | + # Create a credential using fingerprint |
| 68 | + rp = {"id": "example.com", "name": "Example RP"} |
| 69 | + server = Fido2Server(rp) |
| 70 | + user = {"id": b"user_id", "name": "A. User"} |
| 71 | + create_options, state = server.register_begin(user, user_verification="required") |
| 72 | + |
| 73 | + client = Fido2Client( |
| 74 | + device, |
| 75 | + "https://example.com", |
| 76 | + user_interaction=CliInteraction(printer, "WrongPin"), |
| 77 | + ) |
| 78 | + |
| 79 | + # Allow multiple attempts |
| 80 | + for _ in range(3): |
| 81 | + try: |
| 82 | + result = client.make_credential(create_options.public_key) |
| 83 | + break |
| 84 | + except ClientError as e: |
| 85 | + if e.cause.code == CtapError.ERR.UV_INVALID: |
| 86 | + continue |
| 87 | + raise |
| 88 | + |
| 89 | + server.register_complete(state, result) |
| 90 | + |
| 91 | + # Delete fingerprint |
| 92 | + bio = get_bio(ctap2, pin_protocol) |
| 93 | + bio.remove_enrollment(template_id) |
| 94 | + assert len(bio.enumerate_enrollments()) == 0 |
0 commit comments