Skip to content

Commit 6acccd7

Browse files
committed
Add tests for CredentialManagement
1 parent b2b7d3d commit 6acccd7

File tree

1 file changed

+132
-0
lines changed

1 file changed

+132
-0
lines changed

tests/device/test_credman.py

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
from fido2.ctap import CtapError
2+
from fido2.ctap2.pin import ClientPin, PinProtocolV1, PinProtocolV2
3+
from fido2.ctap2.credman import CredentialManagement
4+
from fido2.server import Fido2Server
5+
from . import TEST_PIN
6+
7+
import pytest
8+
9+
10+
@pytest.fixture(autouse=True, scope="module")
11+
def preconditions(dev_manager):
12+
if not CredentialManagement.is_supported(dev_manager.info):
13+
pytest.skip("CredentialManagement not supported by authenticator")
14+
15+
16+
@pytest.fixture(params=[PinProtocolV1, PinProtocolV2])
17+
def pin_protocol(request, info):
18+
proto = request.param
19+
if proto.VERSION not in info.pin_uv_protocols:
20+
pytest.skip(f"PIN/UV protocol {proto.VERSION} not supported")
21+
22+
return proto()
23+
24+
25+
def get_credman(ctap2, pin_protocol, permissions=ClientPin.PERMISSION.CREDENTIAL_MGMT):
26+
token = ClientPin(ctap2, pin_protocol).get_pin_token(TEST_PIN, permissions)
27+
return CredentialManagement(ctap2, pin_protocol, token)
28+
29+
30+
def test_list_and_delete(client, ctap2, pin_protocol):
31+
# Ensure no credentials exist initially
32+
credman = get_credman(ctap2, pin_protocol)
33+
metadata = credman.get_metadata()
34+
assert metadata[CredentialManagement.RESULT.EXISTING_CRED_COUNT] == 0
35+
remaining = metadata[CredentialManagement.RESULT.MAX_REMAINING_COUNT]
36+
assert remaining > 0
37+
38+
rp = {"id": "example.com", "name": "Example RP"}
39+
server = Fido2Server(rp)
40+
user = {"id": b"user_id", "name": "A. User"}
41+
42+
create_options, state = server.register_begin(
43+
user,
44+
resident_key_requirement="required",
45+
)
46+
47+
# Create a credential
48+
result = client.make_credential(
49+
{
50+
**create_options["publicKey"],
51+
"extensions": {"credProps": True},
52+
}
53+
)
54+
55+
# Need new PIN token as old one is expired by make_credential
56+
credman = get_credman(ctap2, pin_protocol)
57+
58+
metadata = credman.get_metadata()
59+
assert metadata[CredentialManagement.RESULT.EXISTING_CRED_COUNT] == 1
60+
assert metadata[CredentialManagement.RESULT.MAX_REMAINING_COUNT] < remaining
61+
62+
# Complete registration
63+
auth_data = server.register_complete(state, result)
64+
cred = auth_data.credential_data
65+
66+
rps = credman.enumerate_rps()
67+
assert len(rps) == 1
68+
69+
# Not all keys are required in response, but those that are should match
70+
for k, v in rps[0][3].items():
71+
assert rp[k] == v
72+
73+
rp_id_hash = rps[0][4]
74+
creds = credman.enumerate_creds(rp_id_hash)
75+
assert len(creds) == 1
76+
assert creds[0][6] == user
77+
assert creds[0][7]["id"] == cred.credential_id
78+
assert creds[0][8] == cred.public_key
79+
80+
credman.delete_cred(creds[0][7])
81+
metadata = credman.get_metadata()
82+
assert metadata[CredentialManagement.RESULT.EXISTING_CRED_COUNT] == 0
83+
assert metadata[CredentialManagement.RESULT.MAX_REMAINING_COUNT] == remaining
84+
85+
86+
def test_update(client, ctap2, pin_protocol):
87+
if not CredentialManagement.is_update_supported(ctap2.info):
88+
pytest.skip("ClientPin update not supported")
89+
90+
rp = {"id": "example.com", "name": "Example RP"}
91+
server = Fido2Server(rp)
92+
user = {"id": b"user_id", "name": "A. User"}
93+
94+
create_options, state = server.register_begin(
95+
user,
96+
resident_key_requirement="required",
97+
)
98+
99+
# Create a credential
100+
result = client.make_credential(
101+
{
102+
**create_options["publicKey"],
103+
"extensions": {"credProps": True},
104+
}
105+
)
106+
auth_data = server.register_complete(state, result)
107+
cred_id = {"id": auth_data.credential_data.credential_id, "type": "public-key"}
108+
109+
# Update user data
110+
credman = get_credman(ctap2, pin_protocol)
111+
user2 = {"id": b"user_id", "name": "A. User 2"}
112+
credman.update_user_info(cred_id, user2)
113+
114+
rps = credman.enumerate_rps()
115+
rp_id_hash = rps[0][4]
116+
creds = credman.enumerate_creds(rp_id_hash)
117+
assert len(creds) == 1
118+
assert creds[0][6] == user2
119+
assert creds[0][7] == cred_id
120+
121+
# Clean up
122+
credman.delete_cred(cred_id)
123+
124+
125+
def test_missing_permissions(ctap2, pin_protocol):
126+
if not ClientPin.is_token_supported(ctap2.info):
127+
pytest.skip("Permissions not supported")
128+
129+
credman = get_credman(ctap2, pin_protocol, ClientPin.PERMISSION(0))
130+
131+
with pytest.raises(CtapError, match="PIN_AUTH_INVALID"):
132+
credman.get_metadata()

0 commit comments

Comments
 (0)