From 1fa5360a210b4b820441fdbc6f5dfbbb9eaf14f3 Mon Sep 17 00:00:00 2001 From: tv Date: Sun, 8 Nov 2026 21:46:41 +0000 Subject: screen: signatures required as the policy a change came under says Review 3, R1: what a node received was vouched for only while its own document's policy required signatures. A joiner's first sync, and every node's message bringing `required`, went in by the rules alone: a malicious first peer could plant changes under other members' actors at their next seqs, their real ones then failing as duplicates for good. The signatures a message carries are always offered now, and the screen always has them: a change waits for one where the policy at the heads it is judged at requires it (rules::Reads::required, read once per heads), whatever the receiving document holds. Changes written under a policy not requiring signatures wait for none, as on every other node; the check for forged changes runs always. Changes waiting for a signature are judged again whenever signatures come. The test of signatures required now takes the history into an empty document; a new one takes changes written under a policy not requiring them without any. --- src/swarm/signatures.rs | 51 ++++++++++++++++++++++--------------------------- 1 file changed, 23 insertions(+), 28 deletions(-) (limited to 'src/swarm/signatures.rs') diff --git a/src/swarm/signatures.rs b/src/swarm/signatures.rs index 9ea404e..111b3da 100644 --- a/src/swarm/signatures.rs +++ b/src/swarm/signatures.rs @@ -75,35 +75,30 @@ pub(crate) async fn received(state: &AppState, message: SignaturesMessage) { if replica.epoch != message.epoch { return 0; } - { - let (epoch, epoch_seq) = (replica.epoch, replica.epoch_seq); - let replica = &mut *replica; - replica.signatures.offer(&replica.doc, signatures); - // With signatures required, changes waiting for them judged again - // (`access::screen`). - if crate::access::policy::of(&replica.doc).required() { - let applied = replica.logged_signed(|doc, known, log| { - let applied = crate::access::screen::apply_bytes( - doc, - epoch, - epoch_seq, - &[], - &screening, - Some(known), - log, - ); - (applied, true) - }); - replica.refresh_heads(); - return match applied { - Ok(outcome) => outcome.applied, - Err(error) => { - tracing::warn!(error = %format!("{error:#}"), "Couldn't judge the changes waiting for signatures"); - 0 - } - }; + let (epoch, epoch_seq) = (replica.epoch, replica.epoch_seq); + let replica = &mut *replica; + replica.signatures.offer(&replica.doc, signatures); + // Changes waiting for them judged again (`access::screen`): those the policy they + // were written under requires signed. + let applied = replica.logged_signed(|doc, known, log| { + let applied = crate::access::screen::apply_bytes( + doc, + epoch, + epoch_seq, + &[], + &screening, + Some(known), + log, + ); + (applied, true) + }); + replica.refresh_heads(); + match applied { + Ok(outcome) => outcome.applied, + Err(error) => { + tracing::warn!(error = %format!("{error:#}"), "Couldn't judge the changes waiting for signatures"); + 0 } - 0 } }) .await -- cgit v1.3.1