File: test_clientpin.py

package info (click to toggle)
python-fido2 2.0.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 1,456 kB
  • sloc: python: 11,423; javascript: 181; sh: 21; makefile: 9
file content (81 lines) | stat: -rw-r--r-- 2,743 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import pytest

from fido2.ctap import CtapError
from fido2.ctap2.pin import ClientPin

from . import TEST_PIN


@pytest.fixture(autouse=True, scope="module")
def preconditions(dev_manager):
    if not ClientPin.is_supported(dev_manager.info):
        pytest.skip("ClientPin not supported by authenticator")


@pytest.fixture
def client_pin(ctap2, pin_protocol):
    return ClientPin(ctap2, pin_protocol)


def test_pin_validation(dev_manager, client_pin):
    assert dev_manager.ctap2.get_info().options["clientPin"] is True
    assert client_pin.get_pin_retries()[0] == 8

    # Wrong PIN decreases the retries remaining
    for retries in range(7, 4, -1):
        # Third attempt uses AUTH_BLOCKED
        with pytest.raises(CtapError, match="PIN_(INVALID|AUTH_BLOCKED)"):
            client_pin.get_pin_token("123456")
        assert client_pin.get_pin_retries()[0] == retries

    # Now soft-locked, does not decrement or unlock with any PIN
    for pin in (TEST_PIN, "123456"):
        with pytest.raises(CtapError, match="PIN_AUTH_BLOCKED"):
            client_pin.get_pin_token(pin)
    assert client_pin.get_pin_retries()[0] == retries

    dev_manager.reconnect()
    client_pin = ClientPin(dev_manager.ctap2, client_pin.protocol)

    # Wrong PIN decreases the retries remaining again
    with pytest.raises(CtapError, match="PIN_INVALID"):
        client_pin.get_pin_token("123456")
    assert client_pin.get_pin_retries()[0] == retries - 1

    # Unlocks with correct PIN
    token = client_pin.get_pin_token(TEST_PIN)
    assert client_pin.get_pin_retries()[0] == 8
    assert token


def test_change_pin(client_pin):
    client_pin.get_pin_token(TEST_PIN)

    new_pin = TEST_PIN[::-1]

    client_pin.change_pin(TEST_PIN, new_pin)
    with pytest.raises(CtapError, match="PIN_INVALID"):
        client_pin.get_pin_token(TEST_PIN)

    client_pin.get_pin_token(new_pin)

    client_pin.change_pin(new_pin, TEST_PIN)
    client_pin.get_pin_token(TEST_PIN)


def test_set_and_reset(dev_manager, client_pin, factory_reset):
    assert dev_manager.ctap2.get_info().options["clientPin"] is True
    assert client_pin.get_pin_retries()[0] == 8

    factory_reset()
    client_pin = ClientPin(dev_manager.ctap2, client_pin.protocol)
    # Factory reset clears the PIN
    assert dev_manager.ctap2.get_info().options["clientPin"] is False
    with pytest.raises(CtapError, match="PIN_NOT_SET"):
        client_pin.get_pin_retries()

    # Setup includes setting the default PIN. More correct would be to just set
    # the PIN ourselves here and test that, but then we need another factory reset
    dev_manager.setup()
    assert dev_manager.ctap2.get_info().options["clientPin"] is True
    assert client_pin.get_pin_retries()[0] == 8