Skip to content

Commit 262b1ad

Browse files
committed
fix(rust/catalyst-types): refactor sigining logic into builder
1 parent ee0657c commit 262b1ad

File tree

3 files changed

+68
-46
lines changed

3 files changed

+68
-46
lines changed

rust/signed_doc/examples/mk_signed_doc.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ impl Cli {
7777
let payload = serde_json::to_vec(&json_doc)?;
7878
// Start with no signatures.
7979
let signed_doc = Builder::new()
80-
.with_content(payload)
80+
.with_decoded_content(payload)
8181
.with_metadata(metadata)
8282
.build()?;
8383
save_signed_doc(signed_doc, &output)?;
@@ -87,7 +87,8 @@ impl Cli {
8787
.map_err(|e| anyhow::anyhow!("Failed to load SK FILE: {e}"))?;
8888
let cose_bytes = read_bytes_from_file(&doc)?;
8989
let signed_doc = signed_doc_from_bytes(cose_bytes.as_slice())?;
90-
let new_signed_doc = signed_doc.sign(sk.to_bytes(), kid)?;
90+
let builder = signed_doc.as_signed_doc_builder();
91+
let new_signed_doc = builder.add_signature(sk.to_bytes(), kid)?.build()?;
9192
save_signed_doc(new_signed_doc, &doc)?;
9293
},
9394
Self::Inspect { path } => {

rust/signed_doc/src/builder.rs

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
//! Catalyst Signed Document Builder.
2+
use catalyst_types::kid_uri::KidUri;
3+
use ed25519_dalek::{ed25519::signature::Signer, SecretKey};
4+
25
use crate::{CatalystSignedDocument, Content, InnerCatalystSignedDocument, Metadata, Signatures};
36

47
/// Catalyst Signed Document Builder.
5-
#[derive(Debug, Default)]
8+
#[derive(Debug, Default, Clone)]
69
pub struct Builder {
710
/// Document Metadata
8-
metadata: Option<Metadata>,
11+
pub(crate) metadata: Option<Metadata>,
912
/// Document Content
10-
content: Option<Vec<u8>>,
13+
pub(crate) content: Option<Vec<u8>>,
1114
/// Signatures
12-
signatures: Signatures,
15+
pub(crate) signatures: Signatures,
1316
}
1417

1518
impl Builder {
@@ -26,13 +29,58 @@ impl Builder {
2629
self
2730
}
2831

29-
/// Set document content
32+
/// Set decoded (original) document content bytes
3033
#[must_use]
31-
pub fn with_content(mut self, content: Vec<u8>) -> Self {
34+
pub fn with_decoded_content(mut self, content: Vec<u8>) -> Self {
3235
self.content = Some(content);
3336
self
3437
}
3538

39+
/// Set document signatures
40+
#[must_use]
41+
pub fn with_signatures(mut self, signatures: Signatures) -> Self {
42+
self.signatures = signatures;
43+
self
44+
}
45+
46+
/// Add a signature to the document
47+
///
48+
/// # Errors
49+
///
50+
/// Fails if a `CatalystSignedDocument` cannot be created due to missing metadata or
51+
/// content, due to malformed data, or when the signed document cannot be
52+
/// converted into `coset::CoseSign`.
53+
pub fn add_signature(self, sk: SecretKey, kid: KidUri) -> anyhow::Result<Self> {
54+
let cose_sign = self
55+
.clone()
56+
.build()
57+
.map_err(|e| anyhow::anyhow!("Failed to sign: {e}"))?
58+
.as_cose_sign()
59+
.map_err(|e| anyhow::anyhow!("Failed to sign: {e}"))?;
60+
let Self {
61+
metadata: Some(metadata),
62+
content: Some(content),
63+
mut signatures,
64+
} = self
65+
else {
66+
anyhow::bail!("Metadata and Content are needed for signing");
67+
};
68+
let sk = ed25519_dalek::SigningKey::from_bytes(&sk);
69+
let protected_header = coset::HeaderBuilder::new()
70+
.key_id(kid.to_string().into_bytes())
71+
.algorithm(metadata.algorithm().into());
72+
let mut signature = coset::CoseSignatureBuilder::new()
73+
.protected(protected_header.build())
74+
.build();
75+
let data_to_sign = cose_sign.tbs_data(&[], &signature);
76+
signature.signature = sk.sign(&data_to_sign).to_vec();
77+
signatures.push(kid, signature);
78+
Ok(Self::new()
79+
.with_decoded_content(content)
80+
.with_metadata(metadata)
81+
.with_signatures(signatures))
82+
}
83+
3684
/// Build a signed document
3785
///
3886
/// ## Errors

rust/signed_doc/src/lib.rs

Lines changed: 11 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pub use builder::Builder;
1717
use catalyst_types::problem_report::ProblemReport;
1818
pub use content::Content;
1919
use coset::{CborSerializable, Header};
20-
use ed25519_dalek::{ed25519::signature::Signer, SecretKey, VerifyingKey};
20+
use ed25519_dalek::VerifyingKey;
2121
use error::CatalystSignedDocError;
2222
pub use metadata::{DocumentRef, ExtraFields, Metadata, UuidV4, UuidV7};
2323
pub use minicbor::{decode, encode, Decode, Decoder, Encode};
@@ -259,50 +259,23 @@ impl CatalystSignedDocument {
259259
Ok(())
260260
}
261261

262-
/// Add a signature to the Catalyst Signed Document.
263-
///
264-
/// # Returns
265-
///
266-
/// A new Catalyst Signed Document with the added signature.
267-
///
268-
/// # Errors
269-
///
270-
/// Fails if the current signed document cannot be encoded as COSE SIGN,
271-
/// or if the Arc inner value cannot be obtained.
272-
pub fn sign(self, sk: SecretKey, kid: KidUri) -> anyhow::Result<Self> {
273-
let cose_sign = self.as_cose_sign()?;
274-
let Some(InnerCatalystSignedDocument {
275-
metadata,
276-
content,
277-
mut signatures,
278-
}) = Arc::into_inner(self.inner)
279-
else {
280-
anyhow::bail!("Failed to extract inner signed document");
281-
};
282-
let sk = ed25519_dalek::SigningKey::from_bytes(&sk);
283-
let protected_header = coset::HeaderBuilder::new()
284-
.key_id(kid.to_string().into_bytes())
285-
.algorithm(metadata.algorithm().into());
286-
let mut signature = coset::CoseSignatureBuilder::new()
287-
.protected(protected_header.build())
288-
.build();
289-
let data_to_sign = cose_sign.tbs_data(&[], &signature);
290-
signature.signature = sk.sign(&data_to_sign).to_vec();
291-
signatures.push(kid, signature);
292-
Ok(InnerCatalystSignedDocument {
293-
metadata,
294-
content,
295-
signatures,
262+
/// Returns a signed document `Builder` pre-loaded with the current signed document's
263+
/// data.
264+
#[must_use]
265+
pub fn as_signed_doc_builder(&self) -> Builder {
266+
Builder {
267+
metadata: Some(self.inner.metadata.clone()),
268+
content: Some(self.inner.content.decoded_bytes().to_vec()),
269+
signatures: self.inner.signatures.clone(),
296270
}
297-
.into())
298271
}
299272

300273
/// Convert Catalyst Signed Document into `coset::CoseSign`
301274
fn as_cose_sign(&self) -> anyhow::Result<coset::CoseSign> {
302275
let mut cose_bytes: Vec<u8> = Vec::new();
303276
minicbor::encode(self, &mut cose_bytes)?;
304277
coset::CoseSign::from_slice(&cose_bytes)
305-
.map_err(|e| anyhow::anyhow!("encoding COSE SIGN failed: {e}"))
278+
.map_err(|e| anyhow::anyhow!("encoding as COSE SIGN failed: {e}"))
306279
}
307280
}
308281

@@ -477,7 +450,7 @@ mod tests {
477450

478451
let doc = Builder::new()
479452
.with_metadata(metadata.clone())
480-
.with_content(content.clone())
453+
.with_decoded_content(content.clone())
481454
.build()
482455
.unwrap();
483456

0 commit comments

Comments
 (0)