Fix R4: checkpoint binds private key to enrolled fingerprint #8

Merged
leora merged 1 commits from leora/fix-r4-checkpoint-key-binding into sigil-initial 2026-10-06 06:14:02 +08:00
2 changed files with 17 additions and 1 deletions
Showing only changes of commit 7872bd92b9 - Show all commits

View File

@ -675,8 +675,15 @@ class Store:
public = _keys.public_from_private(private) public = _keys.public_from_private(private)
fingerprint = _keys.fingerprint(public) fingerprint = _keys.fingerprint(public)
if self.get_agent(signer) is None: agent_row = self.get_agent(signer)
if agent_row is None:
raise ValueError(f"{signer} is not enrolled, so cannot sign a checkpoint") raise ValueError(f"{signer} is not enrolled, so cannot sign a checkpoint")
enrolled_fp = agent_row["fingerprint"] or ""
if not enrolled_fp or fingerprint != enrolled_fp:
raise ValueError(
f"private key fingerprint does not match enrolled key for {signer}; "
"refusing to sign a checkpoint with a stranger key"
)
with self._write_lock: with self._write_lock:
last = self.conn.execute( last = self.conn.execute(
"SELECT id, hash FROM events ORDER BY id DESC LIMIT 1" "SELECT id, hash FROM events ORDER BY id DESC LIMIT 1"

View File

@ -133,6 +133,15 @@ class TestSignedCheckpoints(unittest.TestCase):
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
stranger.checkpoint(self.private, "not-enrolled") stranger.checkpoint(self.private, "not-enrolled")
def test_checkpoint_refuses_enrolled_name_with_wrong_key(self):
# R4: signer name is enrolled, but private key is not the enrolled one.
before = len(self.store.checkpoints())
other_private, _ = keys.generate()
with self.assertRaises(ValueError) as ctx:
self.store.checkpoint(other_private, "bigpickle")
self.assertIn("does not match enrolled key", str(ctx.exception))
self.assertEqual(len(self.store.checkpoints()), before)
def test_checkpoint_after_rekey_is_flagged(self): def test_checkpoint_after_rekey_is_flagged(self):
secret = identity.seed_bootstrap(self.store, "bigpickle") secret = identity.seed_bootstrap(self.store, "bigpickle")
identity.enrol_rekey( identity.enrol_rekey(