cob: Parametrize `Change` with single signature

Signed-off-by: Alexis Sellier <alexis@radicle.xyz>
This commit is contained in:
Alexis Sellier 2022-11-29 10:31:50 +01:00
parent 2230c8ddc4
commit 1a5b68c2b3
No known key found for this signature in database
6 changed files with 41 additions and 14 deletions

View File

@ -64,6 +64,10 @@ pub mod error {
NoChange(Oid), NoChange(Oid),
#[error("the 'change' found at '{0}' was not a blob")] #[error("the 'change' found at '{0}' was not a blob")]
ChangeNotBlob(Oid), 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)] #[error(transparent)]
AuthorTrailer(#[from] trailers::error::InvalidAuthorTrailer), AuthorTrailer(#[from] trailers::error::InvalidAuthorTrailer),
#[error(transparent)] #[error(transparent)]
@ -82,7 +86,7 @@ impl change::Storage for git2::Repository {
type ObjectId = Oid; type ObjectId = Oid;
type Author = Oid; type Author = Oid;
type Resource = Oid; type Resource = Oid;
type Signatures = Signatures; type Signatures = Signature;
fn create<Signer>( fn create<Signer>(
&self, &self,
@ -127,7 +131,7 @@ impl change::Storage for git2::Repository {
Ok(Change { Ok(Change {
id, id,
revision: revision.into(), revision: revision.into(),
signatures: signature.into(), signature,
author, author,
resource, resource,
manifest, manifest,
@ -138,7 +142,15 @@ impl change::Storage for git2::Repository {
fn load(&self, id: Self::ObjectId) -> Result<Change, Self::LoadError> { fn load(&self, id: Self::ObjectId) -> Result<Change, Self::LoadError> {
let commit = Commit::read(self, id.into())?; let commit = Commit::read(self, id.into())?;
let (author, resource) = parse_trailers(commit.trailers())?; let (author, resource) = parse_trailers(commit.trailers())?;
let signatures = Signatures::try_from(&commit)?; let mut signatures = Signatures::try_from(&commit)?
.into_iter()
.collect::<Vec<_>>();
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 tree = self.find_tree(commit.tree())?;
let manifest = load_manifest(self, &tree)?; let manifest = load_manifest(self, &tree)?;
@ -147,7 +159,7 @@ impl change::Storage for git2::Repository {
Ok(Change { Ok(Change {
id, id,
revision: tree.id().into(), revision: tree.id().into(),
signatures, signature: signature.into(),
author, author,
resource, resource,
manifest, manifest,

View File

@ -8,9 +8,9 @@ use git_ext::Oid;
pub mod store; pub mod store;
pub use store::{Create, Storage}; 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 /// 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) /// is specified in the RFC (docs/rfc/0662-collaborative-objects.adoc)
/// under "Change Commits". /// under "Change Commits".
pub type Change = store::Change<Oid, Oid, Oid, Signatures>; pub type Change = store::Change<Oid, Oid, Oid, Signature>;

View File

@ -54,14 +54,14 @@ pub struct Create<Id> {
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Change<Author, Resource, Id, Signatures> { pub struct Change<Author, Resource, Id, Signature> {
/// The content address of the `Change` itself. /// The content address of the `Change` itself.
pub id: Id, pub id: Id,
/// The content address of the tree of the `Change`. /// The content address of the tree of the `Change`.
pub revision: Id, pub revision: Id,
/// The cryptographic signatures and their public keys of the /// The cryptographic signature(s) and their public keys of the
/// authors. /// authors.
pub signatures: Signatures, pub signature: Signature,
/// The author of this change. The `Author` is expected to be a /// The author of this change. The `Author` is expected to be a
/// content address to look up the identity of the author. /// content address to look up the identity of the author.
pub author: Option<Author>, pub author: Option<Author>,
@ -111,12 +111,21 @@ where
Id: AsRef<[u8]>, Id: AsRef<[u8]>,
{ {
pub fn valid_signatures(&self) -> bool { pub fn valid_signatures(&self) -> bool {
self.signatures self.signature
.iter() .iter()
.all(|(key, sig)| key.verify(self.revision.as_ref(), sig).is_ok()) .all(|(key, sig)| key.verify(self.revision.as_ref(), sig).is_ok())
} }
} }
impl<A, R, Id> Change<A, R, Id, signatures::Signature>
where
Id: AsRef<[u8]>,
{
pub fn valid_signatures(&self) -> bool {
self.signature.verify(self.revision.as_ref())
}
}
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Manifest { pub struct Manifest {
/// The name given to the type of collaborative object. /// The name given to the type of collaborative object.

View File

@ -15,7 +15,7 @@ use petgraph::{
}; };
use crate::{ use crate::{
change, object, signatures::Signatures, Change, CollaborativeObject, ObjectId, TypeName, change, object, signatures::Signature, Change, CollaborativeObject, ObjectId, TypeName,
}; };
mod evaluation; mod evaluation;
@ -37,7 +37,7 @@ impl ChangeGraph {
oid: &ObjectId, oid: &ObjectId,
) -> Option<ChangeGraph> ) -> Option<ChangeGraph>
where where
S: change::Storage<ObjectId = Oid, Author = Oid, Resource = Oid, Signatures = Signatures>, S: change::Storage<ObjectId = Oid, Author = Oid, Resource = Oid, Signatures = Signature>,
{ {
log::info!("loading object '{}' '{}'", typename, oid); log::info!("loading object '{}' '{}'", typename, oid);
let mut builder = GraphBuilder::default(); let mut builder = GraphBuilder::default();

View File

@ -97,7 +97,7 @@ pub use history::{Contents, Entry, History, HistoryType};
mod pruning_fold; mod pruning_fold;
pub mod signatures; pub mod signatures;
use signatures::Signatures; use signatures::Signature;
pub mod type_name; pub mod type_name;
pub use type_name::TypeName; pub use type_name::TypeName;
@ -134,7 +134,7 @@ where
ObjectId = git_ext::Oid, ObjectId = git_ext::Oid,
Author = git_ext::Oid, Author = git_ext::Oid,
Resource = git_ext::Oid, Resource = git_ext::Oid,
Signatures = Signatures, Signatures = Signature,
>, >,
{ {
} }

View File

@ -24,6 +24,12 @@ pub struct Signature {
sig: crypto::Signature, sig: crypto::Signature,
} }
impl Signature {
pub fn verify(&self, payload: &[u8]) -> bool {
self.key.verify(payload, &self.sig).is_ok()
}
}
impl From<Signature> for ExtendedSignature { impl From<Signature> for ExtendedSignature {
fn from(sig: Signature) -> Self { fn from(sig: Signature) -> Self {
Self::new(sig.key, sig.sig) Self::new(sig.key, sig.sig)