tests/test_signer.py
2.7 KB · 72 lines · python Raw
1 """Tests for MBOMSigner and MBOMVerifier."""
2
3 from __future__ import annotations
4
5 import pytest
6 from quantumshield.identity.agent import AgentIdentity
7
8 from pqc_mbom import MBOM, MBOMSigner, MBOMVerifier
9 from pqc_mbom.errors import SignatureVerificationError
10
11
12 def test_sign_populates_fields(sample_mbom: MBOM, creator_identity: AgentIdentity) -> None:
13 signer = MBOMSigner(creator_identity)
14 signer.sign(sample_mbom)
15 assert sample_mbom.signer_did == creator_identity.did
16 assert sample_mbom.algorithm
17 assert sample_mbom.signature
18 assert sample_mbom.public_key
19 assert sample_mbom.signed_at
20
21
22 def test_verify_success(sample_mbom: MBOM, creator_identity: AgentIdentity) -> None:
23 MBOMSigner(creator_identity).sign(sample_mbom)
24 result = MBOMVerifier.verify(sample_mbom)
25 assert result.valid
26 assert result.signature_valid
27 assert result.root_hash_valid
28 assert result.error is None
29 assert result.signer_did == creator_identity.did
30
31
32 def test_tamper_detection(sample_mbom: MBOM, creator_identity: AgentIdentity) -> None:
33 MBOMSigner(creator_identity).sign(sample_mbom)
34 # Tamper: change a component's name AFTER signing; the canonical bytes diverge.
35 sample_mbom.components[0].name = "Evil-Replacement"
36 result = MBOMVerifier.verify(sample_mbom)
37 assert not result.valid
38 # Either the signature OR the root check fails (both will, in fact).
39 assert not (result.signature_valid and result.root_hash_valid)
40
41
42 def test_root_hash_mismatch_detection(sample_mbom: MBOM, creator_identity: AgentIdentity) -> None:
43 MBOMSigner(creator_identity).sign(sample_mbom)
44 # Overwrite stored root without recomputing - signature still matches the
45 # canonical bytes (which include the bad root), but the recomputed root
46 # will disagree.
47 sample_mbom.components_root_hash = "f" * 64
48 # re-sign with the wrong root baked in
49 MBOMSigner(creator_identity).sign(sample_mbom)
50 # Actually the signer always recomputes, so to force a mismatch we
51 # mutate after signing without recompute:
52 sample_mbom.components_root_hash = "0" * 64
53 result = MBOMVerifier.verify(sample_mbom)
54 assert not result.valid
55 assert not result.root_hash_valid
56
57
58 def test_verify_or_raise(sample_mbom: MBOM, creator_identity: AgentIdentity) -> None:
59 # Unsigned MBOM should raise
60 with pytest.raises(SignatureVerificationError):
61 MBOMVerifier.verify_or_raise(sample_mbom)
62
63 MBOMSigner(creator_identity).sign(sample_mbom)
64 # Signed valid MBOM returns result
65 result = MBOMVerifier.verify_or_raise(sample_mbom)
66 assert result.valid
67
68 # Tamper then verify_or_raise raises
69 sample_mbom.components[0].supplier = "Attacker"
70 with pytest.raises(SignatureVerificationError):
71 MBOMVerifier.verify_or_raise(sample_mbom)
72