gabriel / muse public
test_public_key_fingerprint.py python
156 lines 6.6 KB
Raw
sha256:b636f72dcba9e190afb980bece906fa5b717fbde014b76ef023df8cb96e01eb9 docs: expand cache plan with all seven testing tiers and do… Sonnet 4.6 135 days ago
1 """Tests for the canonical public_key_fingerprint function.
2
3 All public key fingerprints in Muse must use the sha256: prefix — the same
4 convention as every other content-addressed value. These tests drive the
5 single canonical implementation in muse.core._types.
6 """
7 from __future__ import annotations
8
9 import hashlib
10
11 import pytest
12
13
14 class TestPublicKeyFingerprintCanonical:
15 """The canonical function lives in muse.core._types."""
16
17 def test_returns_sha256_prefixed_string(self) -> None:
18 from muse.core._types import public_key_fingerprint
19 fp = public_key_fingerprint(b"\x00" * 32)
20 assert fp.startswith("sha256:")
21
22 def test_hex_portion_is_64_chars(self) -> None:
23 from muse.core._types import public_key_fingerprint
24 fp = public_key_fingerprint(b"\x00" * 32)
25 _, hex_part = fp.split(":", 1)
26 assert len(hex_part) == 64
27
28 def test_hex_portion_is_lowercase_hex(self) -> None:
29 from muse.core._types import public_key_fingerprint
30 fp = public_key_fingerprint(b"\xff" * 32)
31 _, hex_part = fp.split(":", 1)
32 assert all(c in "0123456789abcdef" for c in hex_part)
33
34 def test_correct_sha256_of_input(self) -> None:
35 from muse.core._types import public_key_fingerprint
36 data = b"test public key bytes"
37 expected = "sha256:" + hashlib.sha256(data).hexdigest()
38 assert public_key_fingerprint(data) == expected
39
40 def test_known_zero_key(self) -> None:
41 from muse.core._types import public_key_fingerprint
42 data = b"\x00" * 32
43 expected = "sha256:" + hashlib.sha256(data).hexdigest()
44 assert public_key_fingerprint(data) == expected
45
46 def test_deterministic(self) -> None:
47 from muse.core._types import public_key_fingerprint
48 data = b"deterministic input"
49 assert public_key_fingerprint(data) == public_key_fingerprint(data)
50
51 def test_different_inputs_produce_different_fingerprints(self) -> None:
52 from muse.core._types import public_key_fingerprint
53 assert public_key_fingerprint(b"aaa") != public_key_fingerprint(b"bbb")
54
55 def test_empty_bytes(self) -> None:
56 from muse.core._types import public_key_fingerprint
57 fp = public_key_fingerprint(b"")
58 assert fp == "sha256:" + hashlib.sha256(b"").hexdigest()
59
60 def test_total_length_is_71(self) -> None:
61 # "sha256:" (7) + 64 hex chars = 71
62 from muse.core._types import public_key_fingerprint
63 assert len(public_key_fingerprint(b"x" * 32)) == 71
64
65
66 class TestPublicKeyFingerprintKeypairModule:
67 """keypair.py::public_key_fingerprint must delegate to _types and return prefixed value."""
68
69 def test_returns_sha256_prefixed_string(self) -> None:
70 from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
71 from muse.core.keypair import public_key_fingerprint
72 private_key = Ed25519PrivateKey.generate()
73 public_key = private_key.public_key()
74 fp = public_key_fingerprint(public_key)
75 assert fp.startswith("sha256:")
76
77 def test_hex_portion_is_64_chars(self) -> None:
78 from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
79 from muse.core.keypair import public_key_fingerprint
80 private_key = Ed25519PrivateKey.generate()
81 fp = public_key_fingerprint(private_key.public_key())
82 _, hex_part = fp.split(":", 1)
83 assert len(hex_part) == 64
84
85 def test_consistent_with_types_module(self) -> None:
86 from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
87 from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
88 from muse.core._types import public_key_fingerprint as canonical_fp
89 from muse.core.keypair import public_key_fingerprint as keypair_fp
90 private_key = Ed25519PrivateKey.generate()
91 public_key = private_key.public_key()
92 raw = public_key.public_bytes(Encoding.Raw, PublicFormat.Raw)
93 assert keypair_fp(public_key) == canonical_fp(raw)
94
95
96 class TestNoBareFingerprintsAnywhere:
97 """The old bare-hex implementations must no longer exist."""
98
99 def test_agent_fingerprint_function_is_gone(self) -> None:
100 import importlib
101 import inspect
102 mod = importlib.import_module("muse.cli.commands.agent")
103 # _fingerprint should not exist as a standalone function anymore
104 assert not hasattr(mod, "_fingerprint"), (
105 "_fingerprint still exists in agent.py — delete it and route callers "
106 "through muse.core._types.public_key_fingerprint"
107 )
108
109 def test_provenance_fingerprint_function_is_gone(self) -> None:
110 import importlib
111 mod = importlib.import_module("muse.core.provenance")
112 assert not hasattr(mod, "public_key_fingerprint"), (
113 "public_key_fingerprint still exists in provenance.py — delete it and "
114 "route callers through muse.core._types.public_key_fingerprint"
115 )
116
117
118 class TestFingerprintInIdentityEntry:
119 """Fingerprints written to identity.toml must carry the sha256: prefix."""
120
121 def test_derive_hd_public_info_returns_prefixed_fingerprint(self) -> None:
122 from muse.core.keypair import derive_hd_public_info
123 seed = b"\x00" * 64
124 _, fingerprint = derive_hd_public_info(seed)
125 assert fingerprint.startswith("sha256:"), (
126 f"derive_hd_public_info returned bare fingerprint {fingerprint!r} — "
127 "must be sha256:-prefixed"
128 )
129
130 def test_fingerprint_field_in_identity_entry_is_prefixed(self, tmp_path) -> None:
131 """Round-trip: save an identity, load it back, fingerprint has prefix."""
132 from muse.core.identity import save_identity, load_identity
133 from muse.core._types import public_key_fingerprint
134 import secrets
135
136 fingerprint = public_key_fingerprint(b"fake-public-key-bytes")
137 entry = {
138 "type": "human",
139 "handle": "gabriel",
140 "key_path": str(tmp_path / "key.pem"),
141 "algorithm": "ed25519",
142 "fingerprint": fingerprint,
143 }
144 # Monkeypatch the identity file location
145 import muse.core.identity as id_mod
146 orig = id_mod._IDENTITY_FILE
147 id_mod._IDENTITY_FILE = tmp_path / "identity.toml"
148 try:
149 save_identity("localhost:1337", entry) # type: ignore[arg-type]
150 loaded = load_identity("localhost:1337")
151 assert loaded is not None
152 assert loaded.get("fingerprint", "").startswith("sha256:"), (
153 f"Loaded fingerprint {loaded.get('fingerprint')!r} lacks sha256: prefix"
154 )
155 finally:
156 id_mod._IDENTITY_FILE = orig
File History 1 commit
sha256:b636f72dcba9e190afb980bece906fa5b717fbde014b76ef023df8cb96e01eb9 docs: expand cache plan with all seven testing tiers and do… Sonnet 4.6 135 days ago