From 1a5b68c2b3d47928cf1331e09b8e2a4546c95a69 Mon Sep 17 00:00:00 2001 From: Alexis Sellier Date: Tue, 29 Nov 2022 10:31:50 +0100 Subject: [PATCH] cob: Parametrize `Change` with single signature Signed-off-by: Alexis Sellier --- radicle-cob/src/backend/git/change.rs | 20 ++++++++++++++++---- radicle-cob/src/change.rs | 4 ++-- radicle-cob/src/change/store.rs | 17 +++++++++++++---- radicle-cob/src/change_graph.rs | 4 ++-- radicle-cob/src/lib.rs | 4 ++-- radicle-cob/src/signatures.rs | 6 ++++++ 6 files changed, 41 insertions(+), 14 deletions(-) diff --git a/radicle-cob/src/backend/git/change.rs b/radicle-cob/src/backend/git/change.rs index 1145e7ce..b9b476e8 100644 --- a/radicle-cob/src/backend/git/change.rs +++ b/radicle-cob/src/backend/git/change.rs @@ -64,6 +64,10 @@ pub mod error { NoChange(Oid), #[error("the 'change' found at '{0}' was not a blob")] ChangeNotBlob(Oid), + #[error("the 'change' found at '{0}' was not signed")] + ChangeNotSigned(Oid), + #[error("the 'change' found at '{0}' has more than one signature")] + TooManySignatures(Oid), #[error(transparent)] AuthorTrailer(#[from] trailers::error::InvalidAuthorTrailer), #[error(transparent)] @@ -82,7 +86,7 @@ impl change::Storage for git2::Repository { type ObjectId = Oid; type Author = Oid; type Resource = Oid; - type Signatures = Signatures; + type Signatures = Signature; fn create( &self, @@ -127,7 +131,7 @@ impl change::Storage for git2::Repository { Ok(Change { id, revision: revision.into(), - signatures: signature.into(), + signature, author, resource, manifest, @@ -138,7 +142,15 @@ impl change::Storage for git2::Repository { fn load(&self, id: Self::ObjectId) -> Result { let commit = Commit::read(self, id.into())?; let (author, resource) = parse_trailers(commit.trailers())?; - let signatures = Signatures::try_from(&commit)?; + let mut signatures = Signatures::try_from(&commit)? + .into_iter() + .collect::>(); + let Some(signature) = signatures.pop() else { + return Err(error::Load::ChangeNotSigned(id)); + }; + if !signatures.is_empty() { + return Err(error::Load::TooManySignatures(id)); + } let tree = self.find_tree(commit.tree())?; let manifest = load_manifest(self, &tree)?; @@ -147,7 +159,7 @@ impl change::Storage for git2::Repository { Ok(Change { id, revision: tree.id().into(), - signatures, + signature: signature.into(), author, resource, manifest, diff --git a/radicle-cob/src/change.rs b/radicle-cob/src/change.rs index 212f6198..e83e68ad 100644 --- a/radicle-cob/src/change.rs +++ b/radicle-cob/src/change.rs @@ -8,9 +8,9 @@ use git_ext::Oid; pub mod store; pub use store::{Create, Storage}; -use crate::signatures::Signatures; +use crate::signatures::Signature; /// A single change in the change graph. The layout of changes in the repository /// is specified in the RFC (docs/rfc/0662-collaborative-objects.adoc) /// under "Change Commits". -pub type Change = store::Change; +pub type Change = store::Change; diff --git a/radicle-cob/src/change/store.rs b/radicle-cob/src/change/store.rs index 0ae9daf6..1269c376 100644 --- a/radicle-cob/src/change/store.rs +++ b/radicle-cob/src/change/store.rs @@ -54,14 +54,14 @@ pub struct Create { } #[derive(Clone, Debug)] -pub struct Change { +pub struct Change { /// The content address of the `Change` itself. pub id: Id, /// The content address of the tree of the `Change`. pub revision: Id, - /// The cryptographic signatures and their public keys of the + /// The cryptographic signature(s) and their public keys of the /// authors. - pub signatures: Signatures, + pub signature: Signature, /// The author of this change. The `Author` is expected to be a /// content address to look up the identity of the author. pub author: Option, @@ -111,12 +111,21 @@ where Id: AsRef<[u8]>, { pub fn valid_signatures(&self) -> bool { - self.signatures + self.signature .iter() .all(|(key, sig)| key.verify(self.revision.as_ref(), sig).is_ok()) } } +impl Change +where + Id: AsRef<[u8]>, +{ + pub fn valid_signatures(&self) -> bool { + self.signature.verify(self.revision.as_ref()) + } +} + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Manifest { /// The name given to the type of collaborative object. diff --git a/radicle-cob/src/change_graph.rs b/radicle-cob/src/change_graph.rs index 4a30de24..d7d1b055 100644 --- a/radicle-cob/src/change_graph.rs +++ b/radicle-cob/src/change_graph.rs @@ -15,7 +15,7 @@ use petgraph::{ }; use crate::{ - change, object, signatures::Signatures, Change, CollaborativeObject, ObjectId, TypeName, + change, object, signatures::Signature, Change, CollaborativeObject, ObjectId, TypeName, }; mod evaluation; @@ -37,7 +37,7 @@ impl ChangeGraph { oid: &ObjectId, ) -> Option where - S: change::Storage, + S: change::Storage, { log::info!("loading object '{}' '{}'", typename, oid); let mut builder = GraphBuilder::default(); diff --git a/radicle-cob/src/lib.rs b/radicle-cob/src/lib.rs index 2b5d74e6..f4cce633 100644 --- a/radicle-cob/src/lib.rs +++ b/radicle-cob/src/lib.rs @@ -97,7 +97,7 @@ pub use history::{Contents, Entry, History, HistoryType}; mod pruning_fold; pub mod signatures; -use signatures::Signatures; +use signatures::Signature; pub mod type_name; pub use type_name::TypeName; @@ -134,7 +134,7 @@ where ObjectId = git_ext::Oid, Author = git_ext::Oid, Resource = git_ext::Oid, - Signatures = Signatures, + Signatures = Signature, >, { } diff --git a/radicle-cob/src/signatures.rs b/radicle-cob/src/signatures.rs index 574a8946..94a28493 100644 --- a/radicle-cob/src/signatures.rs +++ b/radicle-cob/src/signatures.rs @@ -24,6 +24,12 @@ pub struct Signature { sig: crypto::Signature, } +impl Signature { + pub fn verify(&self, payload: &[u8]) -> bool { + self.key.verify(payload, &self.sig).is_ok() + } +} + impl From for ExtendedSignature { fn from(sig: Signature) -> Self { Self::new(sig.key, sig.sig)