gabriel / muse public
test_provenance.py python
208 lines 7.6 KB
Raw
sha256:88ac91129873e6a496e9189515aa690eb893ae25d69c8f72af141a2be5068eb3 docs: docstring sprint contract→find-symbol — idiomatic run… Sonnet 4.6 patch 142 days ago
1 """Tests for muse.core.provenance — AgentIdentity, Ed25519 signing."""
2
3 import datetime
4
5 import pytest
6 from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
7 from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
8
9 from muse.core._types import decode_pubkey, decode_sig, encode_sig
10 from muse.core.provenance import (
11 AgentIdentity,
12 encode_public_key,
13 make_agent_identity,
14 provenance_payload,
15 public_key_fingerprint,
16 sign_commit_ed25519,
17 sign_commit_record,
18 verify_commit_ed25519,
19 )
20 from muse.core.store import CommitRecord
21
22
23 def _gen_key() -> Ed25519PrivateKey:
24 return Ed25519PrivateKey.generate()
25
26
27 def _pub_bytes(key: Ed25519PrivateKey) -> bytes:
28 return key.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw)
29
30
31 # ---------------------------------------------------------------------------
32 # AgentIdentity factory
33 # ---------------------------------------------------------------------------
34
35
36 class TestMakeAgentIdentity:
37 def test_required_fields_present(self) -> None:
38 identity = make_agent_identity(
39 agent_id="test-agent",
40 model_id="gpt-5",
41 toolchain_id="muse-v2",
42 )
43 assert identity["agent_id"] == "test-agent"
44 assert identity.get("model_id") == "gpt-5"
45 assert identity.get("toolchain_id") == "muse-v2"
46
47 def test_prompt_hash_is_hex(self) -> None:
48 identity = make_agent_identity(
49 agent_id="a",
50 model_id="m",
51 toolchain_id="t",
52 prompt="system: you are a music agent",
53 )
54 prompt_hash = identity.get("prompt_hash", "")
55 assert isinstance(prompt_hash, str)
56 assert len(prompt_hash) == 64
57 assert all(c in "0123456789abcdef" for c in prompt_hash)
58
59 def test_no_prompt_gives_no_hash_key(self) -> None:
60 identity = make_agent_identity(agent_id="a", model_id="m", toolchain_id="t")
61 assert identity.get("prompt_hash", "") == ""
62
63 def test_execution_context_hash_populated(self) -> None:
64 identity = make_agent_identity(
65 agent_id="a",
66 model_id="m",
67 toolchain_id="t",
68 execution_context='{"env": "ci", "version": "1.2.3"}',
69 )
70 ec_hash = identity.get("execution_context_hash", "")
71 assert isinstance(ec_hash, str)
72 assert len(ec_hash) == 64
73
74
75 # ---------------------------------------------------------------------------
76 # Ed25519 signing / verification
77 # ---------------------------------------------------------------------------
78
79
80 class TestEd25519Signing:
81 def test_sign_and_verify_succeed(self) -> None:
82 key = _gen_key()
83 payload = provenance_payload("abc123def456" * 4)
84 sig = sign_commit_ed25519(payload, key)
85 pub_bytes = _pub_bytes(key)
86 assert verify_commit_ed25519(payload, sig, pub_bytes)
87
88 def test_wrong_key_fails(self) -> None:
89 key1 = _gen_key()
90 key2 = _gen_key()
91 payload = provenance_payload("abc123")
92 sig = sign_commit_ed25519(payload, key1)
93 assert not verify_commit_ed25519(payload, sig, _pub_bytes(key2))
94
95 def test_wrong_payload_fails(self) -> None:
96 key = _gen_key()
97 sig = sign_commit_ed25519(provenance_payload("commit-a"), key)
98 assert not verify_commit_ed25519(provenance_payload("commit-b"), sig, _pub_bytes(key))
99
100 def test_tampered_signature_fails(self) -> None:
101 key = _gen_key()
102 payload = provenance_payload("abc")
103 sig = sign_commit_ed25519(payload, key)
104 # Flip a byte in the middle of the raw signature.
105 _, raw = decode_sig(sig)
106 sig_bytes = bytearray(raw)
107 sig_bytes[32] ^= 0xFF
108 tampered = encode_sig("ed25519", bytes(sig_bytes))
109 assert not verify_commit_ed25519(payload, tampered, _pub_bytes(key))
110
111 def test_signature_is_prefixed_base64url_string(self) -> None:
112 key = _gen_key()
113 sig = sign_commit_ed25519(provenance_payload("test-commit"), key)
114 assert isinstance(sig, str)
115 assert sig.startswith("ed25519:")
116 # Ed25519 signature is 64 bytes → 86 base64url chars + 8-char prefix
117 assert len(sig) == len("ed25519:") + 86
118
119 def test_empty_signature_fails(self) -> None:
120 key = _gen_key()
121 assert not verify_commit_ed25519(provenance_payload("x"), "", _pub_bytes(key))
122
123 def test_garbage_signature_fails(self) -> None:
124 key = _gen_key()
125 assert not verify_commit_ed25519(provenance_payload("x"), "!!not-base64!!", _pub_bytes(key))
126
127 def test_truncated_signature_fails(self) -> None:
128 key = _gen_key()
129 payload = provenance_payload("commit-x")
130 sig = sign_commit_ed25519(payload, key)
131 assert not verify_commit_ed25519(payload, sig[:40], _pub_bytes(key))
132
133 def test_different_keys_produce_different_sigs(self) -> None:
134 key1 = _gen_key()
135 key2 = _gen_key()
136 payload = provenance_payload("same-commit")
137 assert sign_commit_ed25519(payload, key1) != sign_commit_ed25519(payload, key2)
138
139
140 # ---------------------------------------------------------------------------
141 # Public key helpers
142 # ---------------------------------------------------------------------------
143
144
145 class TestPublicKeyHelpers:
146 def test_fingerprint_is_16_hex_chars(self) -> None:
147 key = _gen_key()
148 fp = public_key_fingerprint(_pub_bytes(key))
149 assert isinstance(fp, str)
150 assert len(fp) == 16
151 assert all(c in "0123456789abcdef" for c in fp)
152
153 def test_fingerprint_is_deterministic(self) -> None:
154 key = _gen_key()
155 pub = _pub_bytes(key)
156 assert public_key_fingerprint(pub) == public_key_fingerprint(pub)
157
158 def test_different_keys_different_fingerprints(self) -> None:
159 k1, k2 = _gen_key(), _gen_key()
160 assert public_key_fingerprint(_pub_bytes(k1)) != public_key_fingerprint(_pub_bytes(k2))
161
162 def test_encode_public_key_returns_32_bytes_and_prefixed_b64(self) -> None:
163 key = _gen_key()
164 raw_bytes, b64 = encode_public_key(key)
165 assert len(raw_bytes) == 32
166 assert isinstance(b64, str)
167 assert b64.startswith("ed25519:")
168 # No padding in the base64 part
169 assert "=" not in b64
170 # Stripping prefix + decoding returns the raw bytes
171 _, decoded = decode_pubkey(b64)
172 assert decoded == raw_bytes
173
174
175 # ---------------------------------------------------------------------------
176 # sign_commit_record
177 # ---------------------------------------------------------------------------
178
179
180 class TestSignCommitRecord:
181 def test_sign_commit_record_returns_three_tuple(self) -> None:
182 key = _gen_key()
183 commit_id = "deadbeef" * 8
184 result = sign_commit_record(commit_id, "test-agent", key)
185 assert result is not None
186 sig, pub_b64, fprint = result
187 assert sig != ""
188 assert pub_b64 != ""
189 assert len(fprint) == 16
190
191 def test_sign_commit_record_verifiable(self) -> None:
192 key = _gen_key()
193 commit_id = "cafebabe" * 8
194 agent_id = "verify-agent"
195 result = sign_commit_record(commit_id, agent_id, key, model_id="claude-sonnet-4-6")
196 assert result is not None
197 sig, pub_b64, _ = result
198 _, pub_bytes = decode_pubkey(pub_b64)
199 payload = provenance_payload(commit_id, agent_id=agent_id, model_id="claude-sonnet-4-6")
200 assert verify_commit_ed25519(payload, sig, pub_bytes)
201
202 def test_sign_commit_record_public_key_matches_private(self) -> None:
203 key = _gen_key()
204 result = sign_commit_record("aabbccdd" * 8, "agent", key)
205 assert result is not None
206 _, pub_b64, _ = result
207 raw, expected_b64 = encode_public_key(key)
208 assert pub_b64 == expected_b64
File History 2 commits
sha256:88ac91129873e6a496e9189515aa690eb893ae25d69c8f72af141a2be5068eb3 docs: docstring sprint contract→find-symbol — idiomatic run… Sonnet 4.6 patch 142 days ago
sha256:a09b1b4f6838754495547f200aa0ce88e2f56ffc5b20b900f6f0cff2c3cdede9 fix(cursorignore): remove git-ism (.git/worktrees) Human minor 145 days ago