import pytest from cryptography.exceptions import InvalidTag from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature from cryptography.hazmat.primitives.ciphers.aead import AESGCM from aliro_harness.reader.auth1_response import ( decrypt_auth1_response, verify_ud_signature, ) def _gen_ud_keypair_and_pub_uncompressed(): priv = ec.generate_private_key(ec.SECP256R1()) pub_uncompressed = priv.public_key().public_bytes( encoding=serialization.Encoding.X962, format=serialization.PublicFormat.UncompressedPoint, ) return priv, pub_uncompressed def _sign_raw(priv, message: bytes) -> bytes: der = priv.sign(message, ec.ECDSA(hashes.SHA256())) r, s = decode_dss_signature(der) return r.to_bytes(32, "big") + s.to_bytes(32, "big") def test_decrypt_auth1_response_roundtrips_with_device_counter_1(): sk = b"\x42" * 32 iv = b"\x00" * 7 + b"\x01" + b"\x00\x00\x00\x01" plaintext = b"\xAB" * 17 ct_and_tag = AESGCM(sk).encrypt(iv, plaintext, associated_data=None) assert decrypt_auth1_response(sk, ct_and_tag) == plaintext def test_decrypt_auth1_response_fails_on_tampered_ciphertext(): sk = b"\x42" * 32 iv = b"\x00" * 7 + b"\x01" + b"\x00\x00\x00\x01" plaintext = b"\xAB" * 17 ct_and_tag = bytearray(AESGCM(sk).encrypt(iv, plaintext, associated_data=None)) ct_and_tag[0] ^= 0x01 with pytest.raises(InvalidTag): decrypt_auth1_response(sk, bytes(ct_and_tag)) def test_verify_ud_signature_round_trip_returns_true(): priv, pub_uncompressed = _gen_ud_keypair_and_pub_uncompressed() table_813 = b"\x00\x01\x02\x03" + b"table-8-13-test-bytes" * 3 raw_sig = _sign_raw(priv, table_813) assert len(pub_uncompressed) == 65 assert verify_ud_signature(pub_uncompressed, table_813, raw_sig) is True def test_verify_ud_signature_returns_false_on_tampered_table(): priv, pub_uncompressed = _gen_ud_keypair_and_pub_uncompressed() table_813 = b"table-8-13-original-bytes-here!!" raw_sig = _sign_raw(priv, table_813) tampered = bytearray(table_813) tampered[0] ^= 0x01 assert verify_ud_signature(pub_uncompressed, bytes(tampered), raw_sig) is False def test_verify_ud_signature_returns_false_on_tampered_sig(): priv, pub_uncompressed = _gen_ud_keypair_and_pub_uncompressed() table_813 = b"table-8-13-original-bytes-here!!" raw_sig = bytearray(_sign_raw(priv, table_813)) raw_sig[0] ^= 0x01 assert verify_ud_signature(pub_uncompressed, table_813, bytes(raw_sig)) is False def test_verify_ud_signature_rejects_wrong_sig_length(): _, pub_uncompressed = _gen_ud_keypair_and_pub_uncompressed() with pytest.raises(ValueError): verify_ud_signature(pub_uncompressed, b"data", b"\x00" * 63) def test_decrypt_auth1_response_fails_with_wrong_counter(): """Pins the hardcoded counter=1 behavior. If someone "fixes" the counter to 0 or 2 without thinking, this test fails loudly.""" sk = b"\x42" * 32 wrong_iv = b"\x00" * 7 + b"\x01" + b"\x00\x00\x00\x02" # counter=2 ct = AESGCM(sk).encrypt(wrong_iv, b"hello", associated_data=None) with pytest.raises(InvalidTag): decrypt_auth1_response(sk, ct) def test_verify_ud_signature_rejects_malformed_pubkey(): """A short or off-curve pubkey blob must raise (not return False). Guards against a silent ``cryptography`` behavior change.""" with pytest.raises(ValueError): verify_ud_signature(b"\x04" + b"\x00" * 10, b"data", b"\x00" * 64)