import pytest from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric.utils import encode_dss_signature from aliro_harness.reader.auth1 import ( build_auth1_apdu, build_auth1_data, build_table_812, build_table_813, sign_table_812_raw, ) def test_table_812_layout_per_spec_table_8_12(): t = build_table_812( reader_id_32=b"\x4d" * 32, credential_ephem_pub_x=b"\x86" * 32, reader_ephem_pub_x=b"\x87" * 32, transaction_id=b"\x4c" * 16, ) assert t[0:2] == bytes([0x4D, 0x20]) assert t[2:34] == b"\x4d" * 32 assert t[34:36] == bytes([0x86, 0x20]) assert t[36:68] == b"\x86" * 32 assert t[68:70] == bytes([0x87, 0x20]) assert t[70:102] == b"\x87" * 32 assert t[102:104] == bytes([0x4C, 0x10]) assert t[104:120] == b"\x4c" * 16 assert t[120:122] == bytes([0x93, 0x04]) assert t[122:126] == bytes.fromhex("415D9569") # reader sig usage # 5 TLVs: 34+34+34+18+6 = 126 bytes (plan's "128" was off-by-2; byte # offsets above are self-consistent and match ReaderSide.buildTable812). assert len(t) == 126 def test_table_813_uses_ud_usage_tag(): """Same TLV positions as 8-12, but the 0x93 value = UD usage tag.""" kwargs = dict( reader_id_32=b"\x4d" * 32, credential_ephem_pub_x=b"\x86" * 32, reader_ephem_pub_x=b"\x87" * 32, transaction_id=b"\x4c" * 16, ) t = build_table_813(**kwargs) t12 = build_table_812(**kwargs) # Everything up through the 0x93 0x04 header is identical... assert t[0:122] == t12[0:122] assert t[120:122] == bytes([0x93, 0x04]) # ...but the usage tag differs. assert t[122:126] == bytes.fromhex("4E887B4C") assert t12[122:126] == bytes.fromhex("415D9569") assert len(t) == 126 def test_sign_then_verify_round_trip(): """Sign Table 8-12 with a fresh P-256 key, verify with public half. Raw 64B r||s must re-assemble into a DER signature that verifies.""" priv = ec.generate_private_key(ec.SECP256R1()) table_812 = build_table_812( reader_id_32=b"\x01" * 32, credential_ephem_pub_x=b"\x02" * 32, reader_ephem_pub_x=b"\x03" * 32, transaction_id=b"\x04" * 16, ) raw_sig = sign_table_812_raw(table_812, priv) assert len(raw_sig) == 64 r = int.from_bytes(raw_sig[:32], "big") s = int.from_bytes(raw_sig[32:], "big") der = encode_dss_signature(r, s) # Should verify -- raises InvalidSignature if not. priv.public_key().verify(der, table_812, ec.ECDSA(hashes.SHA256())) def test_build_auth1_data_with_cmd_params_zero(): """cmd_params=0 ('key_slot' path) still emits the 0x41/0x9E TLV shell.""" sig = b"\xaa" * 64 data = build_auth1_data(sig, command_parameters=0x00) assert data[:5] == bytes([0x41, 0x01, 0x00, 0x9E, 0x40]) assert data[5:69] == sig assert len(data) == 69 def test_build_auth1_data_rejects_wrong_sig_length(): with pytest.raises(ValueError, match="64B raw"): build_auth1_data(b"\x00" * 63) def test_build_auth1_apdu_short_form_wrap(): """CLA=0x80, INS=0x81, P1=P2=0, Lc=len(data), then data.""" data = bytes([0x41, 0x01, 0x01]) + bytes([0x9E, 0x40]) + b"\xcc" * 64 apdu = build_auth1_apdu(data) assert apdu[0] == 0x80 # CLA assert apdu[1] == 0x81 # INS_AUTH1 assert apdu[2] == 0x00 # P1 assert apdu[3] == 0x00 # P2 assert apdu[4] == len(data) # Lc (short form) assert apdu[5:] == data assert len(apdu) == 5 + len(data)