"""Tests for the DeviceResponse builder used in the step-up phase.""" import cbor2 import pytest from cryptography.hazmat.primitives.asymmetric import ec from aliro_harness.issuer.access_document import build_access_document from aliro_harness.issuer.cose import verify_cose_sign1 from aliro_harness.issuer.device_response import build_device_response @pytest.fixture def issuer_key(): return ec.generate_private_key(ec.SECP256R1()) @pytest.fixture def access_doc(issuer_key): return build_access_document( issuer_private_key=issuer_key, access_credential_public_key=ec.generate_private_key(ec.SECP256R1()).public_key(), ) def test_device_response_has_aliro_remapped_top_level_keys(access_doc): decoded = cbor2.loads(build_device_response(access_doc)) assert set(decoded.keys()) == {"1", "2", "3"}, ( "top-level keys must be Aliro Table 8-22 aliases (version, documents, status)" ) def test_device_response_version_is_1_0(access_doc): decoded = cbor2.loads(build_device_response(access_doc)) assert decoded["1"] == "1.0" def test_device_response_status_is_ok(access_doc): decoded = cbor2.loads(build_device_response(access_doc)) assert decoded["3"] == 0 def test_device_response_has_exactly_one_document(access_doc): decoded = cbor2.loads(build_device_response(access_doc)) assert isinstance(decoded["2"], list) assert len(decoded["2"]) == 1 def test_document_doctype_is_aliro_a(access_doc): decoded = cbor2.loads(build_device_response(access_doc)) assert decoded["2"][0]["5"] == "aliro-a" def test_issuer_signed_has_empty_namespaces(access_doc): decoded = cbor2.loads(build_device_response(access_doc)) issuer_signed = decoded["2"][0]["1"] assert issuer_signed["1"] == {}, "v1 returns no Access Data Elements" def test_issuer_auth_is_the_original_cose_sign1(access_doc, issuer_key): """IssuerAuth embedded in DeviceResponse must still verify as a valid COSE_Sign1 under the issuer's public key — i.e. the AD bytes round-trip.""" decoded = cbor2.loads(build_device_response(access_doc)) issuer_auth = decoded["2"][0]["1"]["2"] assert isinstance(issuer_auth, list) and len(issuer_auth) == 4, ( "IssuerAuth is a 4-element COSE_Sign1 array" ) # Re-serialize and verify. reserialized = cbor2.dumps(issuer_auth, canonical=True) payload = verify_cose_sign1(reserialized, issuer_key.public_key()) assert isinstance(payload, bytes) and len(payload) > 0 def test_forbidden_fields_absent(access_doc): """Per Aliro §8.4.2, documentErrors / errors / deviceSigned / readerAuth SHALL NOT be present.""" decoded = cbor2.loads(build_device_response(access_doc)) doc = decoded["2"][0] assert "documentErrors" not in decoded assert "errors" not in decoded assert "documentErrors" not in doc assert "errors" not in doc assert "deviceSigned" not in doc def test_device_response_is_deterministic(access_doc): """Same inputs must produce byte-identical CBOR.""" a = build_device_response(access_doc) b = build_device_response(access_doc) assert a == b, "DeviceResponse must be deterministic CBOR for a given AD" def test_device_response_fits_within_realistic_apdu_budget(access_doc): """Sanity-check that encrypted response size stays under the step-up APDU chain budget (ENVELOPE/GET RESPONSE supports >2000 bytes with extended-length support, but we want to stay comfortably small).""" encoded = build_device_response(access_doc) assert len(encoded) < 2000, f"DeviceResponse is {len(encoded)} bytes"