diff --git a/rust/catalyst-types/src/catalyst_id/mod.rs b/rust/catalyst-types/src/catalyst_id/mod.rs index 090f2f0e50..1612cec4a1 100644 --- a/rust/catalyst-types/src/catalyst_id/mod.rs +++ b/rust/catalyst-types/src/catalyst_id/mod.rs @@ -676,6 +676,15 @@ impl TryFrom<&[u8]> for CatalystId { } } +impl minicbor::Encode<()> for CatalystId { + fn encode( + &self, e: &mut minicbor::Encoder, _ctx: &mut (), + ) -> Result<(), minicbor::encode::Error> { + e.bytes(self.to_string().into_bytes().as_slice())?; + Ok(()) + } +} + #[cfg(test)] mod tests { use chrono::{DateTime, Utc}; diff --git a/rust/signed_doc/bins/mk_signed_doc.rs b/rust/signed_doc/bins/mk_signed_doc.rs index a22b1ddf91..554ec6e1d5 100644 --- a/rust/signed_doc/bins/mk_signed_doc.rs +++ b/rust/signed_doc/bins/mk_signed_doc.rs @@ -83,7 +83,10 @@ impl Cli { let new_signed_doc = signed_doc .into_builder() - .add_signature(|message| sk.sign::<()>(&message).to_bytes().to_vec(), &kid)? + .add_signature( + |message| sk.sign::<()>(&message).to_bytes().to_vec(), + kid.clone(), + )? .build(); save_signed_doc(new_signed_doc, &doc)?; }, diff --git a/rust/signed_doc/src/builder.rs b/rust/signed_doc/src/builder.rs index 6efdca778d..cec8268777 100644 --- a/rust/signed_doc/src/builder.rs +++ b/rust/signed_doc/src/builder.rs @@ -2,8 +2,9 @@ use catalyst_types::{catalyst_id::CatalystId, problem_report::ProblemReport}; use crate::{ - signature::Signature, CatalystSignedDocument, Content, InnerCatalystSignedDocument, Metadata, - Signatures, PROBLEM_REPORT_CTX, + signature::{tbs_data, Signature}, + CatalystSignedDocument, Content, InnerCatalystSignedDocument, Metadata, Signatures, + PROBLEM_REPORT_CTX, }; /// Catalyst Signed Document Builder. @@ -56,23 +57,14 @@ impl Builder { /// content, due to malformed data, or when the signed document cannot be /// converted into `coset::CoseSign`. pub fn add_signature( - mut self, sign_fn: impl FnOnce(Vec) -> Vec, kid: &CatalystId, + mut self, sign_fn: impl FnOnce(Vec) -> Vec, kid: CatalystId, ) -> anyhow::Result { - let cose_sign = self - .0 - .as_cose_sign() - .map_err(|e| anyhow::anyhow!("Failed to sign: {e}"))?; - - let protected_header = coset::HeaderBuilder::new().key_id(kid.to_string().into_bytes()); - - let mut signature = coset::CoseSignatureBuilder::new() - .protected(protected_header.build()) - .build(); - let data_to_sign = cose_sign.tbs_data(&[], &signature); - signature.signature = sign_fn(data_to_sign); - if let Some(sign) = Signature::from_cose_sig(signature, &self.0.report) { - self.0.signatures.push(sign); + if kid.is_id() { + anyhow::bail!("Provided kid should be in a uri format, kid: {kid}"); } + let data_to_sign = tbs_data(&kid, &self.0.metadata, &self.0.content)?; + let sign_bytes = sign_fn(data_to_sign); + self.0.signatures.push(Signature::new(kid, sign_bytes)); Ok(self) } diff --git a/rust/signed_doc/src/lib.rs b/rust/signed_doc/src/lib.rs index c797d75138..a1c98dffef 100644 --- a/rust/signed_doc/src/lib.rs +++ b/rust/signed_doc/src/lib.rs @@ -15,14 +15,13 @@ use std::{ sync::Arc, }; -use anyhow::Context; pub use builder::Builder; pub use catalyst_types::{ problem_report::ProblemReport, uuid::{Uuid, UuidV4, UuidV7}, }; pub use content::Content; -use coset::{CborSerializable, Header, TaggedCborSerializable}; +use coset::{CborSerializable, TaggedCborSerializable}; use decode_context::{CompatibilityPolicy, DecodeContext}; pub use metadata::{ContentEncoding, ContentType, DocType, DocumentRef, Metadata, Section}; use minicbor::{decode, encode, Decode, Decoder, Encode}; @@ -68,7 +67,7 @@ impl Display for CatalystSignedDocument { if self.inner.signatures.is_empty() { writeln!(f, " This document is unsigned.")?; } else { - for kid in &self.inner.signatures.kids() { + for kid in &self.kids() { writeln!(f, " Signature Key ID: {kid}")?; } } @@ -147,13 +146,21 @@ impl CatalystSignedDocument { /// Return a list of Document's Catalyst IDs. #[must_use] pub fn kids(&self) -> Vec { - self.inner.signatures.kids() + self.inner + .signatures + .iter() + .map(|s| s.kid().clone()) + .collect() } /// Return a list of Document's author IDs (short form of Catalyst IDs). #[must_use] pub fn authors(&self) -> Vec { - self.inner.signatures.authors() + self.inner + .signatures + .iter() + .map(|s| s.kid().as_short_id()) + .collect() } /// Returns a collected problem report for the document. @@ -173,14 +180,6 @@ impl CatalystSignedDocument { &self.inner.report } - /// Convert Catalyst Signed Document into `coset::CoseSign` - /// - /// # Errors - /// Could fails if the `CatalystSignedDocument` object is not valid. - pub(crate) fn as_cose_sign(&self) -> anyhow::Result { - self.inner.as_cose_sign() - } - /// Returns a signed document `Builder` pre-loaded with the current signed document's /// data. #[must_use] @@ -189,39 +188,6 @@ impl CatalystSignedDocument { } } -impl InnerCatalystSignedDocument { - /// Convert Catalyst Signed Document into `coset::CoseSign` - /// - /// # Errors - /// Could fails if the `CatalystSignedDocument` object is not valid. - fn as_cose_sign(&self) -> anyhow::Result { - if let Some(raw_bytes) = self.raw_bytes.clone() { - let cose_sign = coset::CoseSign::from_tagged_slice(raw_bytes.as_slice()) - .or_else(|_| coset::CoseSign::from_slice(raw_bytes.as_slice())) - .map_err(|e| { - minicbor::decode::Error::message(format!("Invalid COSE Sign document: {e}")) - })?; - Ok(cose_sign) - } else { - let protected_header = - Header::try_from(&self.metadata).context("Failed to encode Document Metadata")?; - - let content = self - .content - .encoded_bytes(self.metadata.content_encoding())?; - - let mut builder = coset::CoseSignBuilder::new() - .protected(protected_header) - .payload(content); - - for signature in self.signatures.cose_signatures() { - builder = builder.add_signature(signature); - } - Ok(builder.build()) - } - } -} - impl Decode<'_, ()> for CatalystSignedDocument { fn decode(d: &mut Decoder<'_>, _ctx: &mut ()) -> Result { let start = d.position(); @@ -264,18 +230,38 @@ impl Decode<'_, ()> for CatalystSignedDocument { } } -impl Encode<()> for CatalystSignedDocument { +impl Encode for CatalystSignedDocument { fn encode( - &self, e: &mut encode::Encoder, _ctx: &mut (), + &self, e: &mut encode::Encoder, _ctx: &mut C, ) -> Result<(), encode::Error> { - let cose_sign = self.as_cose_sign().map_err(encode::Error::message)?; - let cose_bytes = cose_sign.to_tagged_vec().map_err(|e| { - minicbor::encode::Error::message(format!("Failed to encode COSE Sign document: {e}")) - })?; - - e.writer_mut() - .write_all(&cose_bytes) - .map_err(|_| minicbor::encode::Error::message("Failed to encode to CBOR")) + if let Some(raw_bytes) = &self.inner.raw_bytes { + e.writer_mut() + .write_all(raw_bytes) + .map_err(minicbor::encode::Error::write)?; + } else { + // COSE_Sign tag + // + e.tag(minicbor::data::Tag::new(98))?; + e.array(4)?; + // protected headers (metadata fields) + e.bytes( + minicbor::to_vec(self.doc_meta()) + .map_err(minicbor::encode::Error::message)? + .as_slice(), + )?; + // empty unprotected headers + e.map(0)?; + // content + let content = self + .doc_content() + .encoded_bytes(self.doc_content_encoding()) + .map_err(minicbor::encode::Error::message)?; + e.bytes(content.as_slice())?; + // signatures + e.encode(self.signatures())?; + } + + Ok(()) } } diff --git a/rust/signed_doc/src/metadata/content_encoding.rs b/rust/signed_doc/src/metadata/content_encoding.rs index d47f696e7f..e736dc74d6 100644 --- a/rust/signed_doc/src/metadata/content_encoding.rs +++ b/rust/signed_doc/src/metadata/content_encoding.rs @@ -84,3 +84,12 @@ impl TryFrom<&coset::cbor::Value> for ContentEncoding { } } } + +impl minicbor::Encode<()> for ContentEncoding { + fn encode( + &self, e: &mut minicbor::Encoder, _ctx: &mut (), + ) -> Result<(), minicbor::encode::Error> { + e.str(self.to_string().as_str())?; + Ok(()) + } +} diff --git a/rust/signed_doc/src/metadata/content_type.rs b/rust/signed_doc/src/metadata/content_type.rs index 199122eddc..260c0196b5 100644 --- a/rust/signed_doc/src/metadata/content_type.rs +++ b/rust/signed_doc/src/metadata/content_type.rs @@ -55,33 +55,34 @@ impl<'de> Deserialize<'de> for ContentType { } } -impl From for CoapContentFormat { - fn from(value: ContentType) -> Self { - match value { - ContentType::Cbor => Self::Cbor, - ContentType::Json => Self::Json, - } - } -} - impl TryFrom<&coset::ContentType> for ContentType { type Error = anyhow::Error; fn try_from(value: &coset::ContentType) -> Result { - let content_type = match value { - coset::ContentType::Assigned(CoapContentFormat::Json) => ContentType::Json, - coset::ContentType::Assigned(CoapContentFormat::Cbor) => ContentType::Cbor, - _ => { + match value { + coset::ContentType::Assigned(CoapContentFormat::Json) => Ok(ContentType::Json), + coset::ContentType::Assigned(CoapContentFormat::Cbor) => Ok(ContentType::Cbor), + coset::ContentType::Text(str) => str.parse(), + coset::RegisteredLabel::Assigned(_) => { anyhow::bail!( - "Unsupported Content Type {value:?}, Supported only: {:?}", + "Unsupported Content Type: {value:?}, Supported only: {:?}", ContentType::VARIANTS .iter() .map(ToString::to_string) .collect::>() ) }, - }; - Ok(content_type) + } + } +} + +impl minicbor::Encode<()> for ContentType { + fn encode( + &self, e: &mut minicbor::Encoder, _ctx: &mut (), + ) -> Result<(), minicbor::encode::Error> { + // encode as media types, not in CoAP Content-Formats + e.str(self.to_string().as_str())?; + Ok(()) } } diff --git a/rust/signed_doc/src/metadata/doc_type.rs b/rust/signed_doc/src/metadata/doc_type.rs index a7c2ca205b..a7b0886420 100644 --- a/rust/signed_doc/src/metadata/doc_type.rs +++ b/rust/signed_doc/src/metadata/doc_type.rs @@ -5,11 +5,7 @@ use std::{ hash::{Hash, Hasher}, }; -use catalyst_types::{ - problem_report::ProblemReport, - uuid::{CborContext, Uuid, UuidV4, UUID_CBOR_TAG}, -}; -use coset::cbor::Value; +use catalyst_types::uuid::{CborContext, Uuid, UuidV4}; use minicbor::{Decode, Decoder, Encode}; use serde::{Deserialize, Deserializer}; use tracing::warn; @@ -46,21 +42,6 @@ impl DocType { pub fn doc_types(&self) -> &Vec { &self.0 } - - /// Convert `DocType` to coset `Value`. - pub(crate) fn to_value(&self) -> Value { - Value::Array( - self.0 - .iter() - .map(|uuidv4| { - Value::Tag( - UUID_CBOR_TAG, - Box::new(Value::Bytes(uuidv4.uuid().as_bytes().to_vec())), - ) - }) - .collect(), - ) - } } impl Hash for DocType { @@ -219,12 +200,7 @@ impl Decode<'_, DecodeContext<'_>> for DocType { minicbor::decode::Error::message(format!("{CONTEXT}: {msg}")) })?; - let doc_type = map_doc_type(uuid.into()).map_err(|e| { - decode_context.report.other(&e.to_string(), CONTEXT); - minicbor::decode::Error::message(format!("{CONTEXT}: {e}")) - })?; - - Ok(doc_type) + Ok(map_doc_type(uuid)) }, CompatibilityPolicy::Fail => { @@ -253,37 +229,28 @@ impl Decode<'_, DecodeContext<'_>> for DocType { /// Map single UUID doc type to new list of doc types /// -fn map_doc_type(uuid: Uuid) -> anyhow::Result { +fn map_doc_type(uuid: UuidV4) -> DocType { match uuid { - id if id == PROPOSAL_UUID_TYPE => Ok(PROPOSAL_DOC_TYPE.clone()), - id if id == COMMENT_UUID_TYPE => Ok(PROPOSAL_COMMENT_DOC.clone()), - id if id == ACTION_UUID_TYPE => Ok(PROPOSAL_ACTION_DOC.clone()), - _ => anyhow::bail!("Unknown document type: {uuid}"), + id if Uuid::from(id) == PROPOSAL_UUID_TYPE => PROPOSAL_DOC_TYPE.clone(), + id if Uuid::from(id) == COMMENT_UUID_TYPE => PROPOSAL_COMMENT_DOC.clone(), + id if Uuid::from(id) == ACTION_UUID_TYPE => PROPOSAL_ACTION_DOC.clone(), + id => DocType(vec![id]), } } -impl Encode for DocType { +impl Encode for DocType { fn encode( - &self, e: &mut minicbor::Encoder, report: &mut ProblemReport, + &self, e: &mut minicbor::Encoder, _ctx: &mut C, ) -> Result<(), minicbor::encode::Error> { - const CONTEXT: &str = "DocType encoding"; - if self.0.is_empty() { - report.invalid_value("DocType", "empty", "DocType cannot be empty", CONTEXT); - return Err(minicbor::encode::Error::message(format!( - "{CONTEXT}: DocType cannot be empty" - ))); - } - - e.array(self.0.len().try_into().map_err(|_| { - report.other("Unable to encode array length", CONTEXT); - minicbor::encode::Error::message(format!("{CONTEXT}, unable to encode array length")) - })?)?; + e.array( + self.0 + .len() + .try_into() + .map_err(minicbor::encode::Error::message)?, + )?; for id in &self.0 { - id.encode(e, &mut CborContext::Tagged).map_err(|_| { - report.other("Failed to encode UUIDv4", CONTEXT); - minicbor::encode::Error::message(format!("{CONTEXT}: UUIDv4 encoding failed")) - })?; + id.encode(e, &mut CborContext::Tagged)?; } Ok(()) } @@ -304,12 +271,12 @@ impl<'de> Deserialize<'de> for DocType { let input = DocTypeInput::deserialize(deserializer)?; let dt = match input { DocTypeInput::Single(s) => { - let uuid = Uuid::parse_str(&s).map_err(|_| { + let uuid = s.parse().map_err(|_| { serde::de::Error::custom(DocTypeError::StringConversion(s.clone())) })?; // If there is a map from old (single uuid) to new use that list, else convert that // single uuid to [uuid] - of type DocType - map_doc_type(uuid).unwrap_or(uuid.try_into().map_err(serde::de::Error::custom)?) + map_doc_type(uuid) }, DocTypeInput::Multiple(v) => v.try_into().map_err(serde::de::Error::custom)?, }; @@ -345,7 +312,7 @@ impl PartialEq for DocType { #[cfg(test)] mod tests { - + use catalyst_types::problem_report::ProblemReport; use minicbor::Encoder; use serde_json::json; @@ -449,22 +416,10 @@ mod tests { assert!(matches!(result, Err(DocTypeError::StringConversion(s)) if s == "not-a-uuid")); } - #[test] - fn test_doc_type_to_value() { - let uuid = uuid::Uuid::new_v4(); - let doc_type = DocType(vec![UuidV4::try_from(uuid).unwrap()]); - - for d in &doc_type.to_value().into_array().unwrap() { - let t = d.clone().into_tag().unwrap(); - assert_eq!(t.0, UUID_CBOR_TAG); - assert_eq!(t.1.as_bytes().unwrap().len(), 16); - } - } - #[test] fn test_doctype_equal_special_cases() { // Direct equal - let uuid = PROPOSAL_UUID_TYPE; + let uuid: UuidV4 = PROPOSAL_UUID_TYPE.try_into().unwrap(); let dt1 = DocType::try_from(vec![uuid]).unwrap(); let dt2 = DocType::try_from(vec![uuid]).unwrap(); assert_eq!(dt1, dt2); diff --git a/rust/signed_doc/src/metadata/document_ref.rs b/rust/signed_doc/src/metadata/document_ref.rs index 00e0bba241..612a80bbf5 100644 --- a/rust/signed_doc/src/metadata/document_ref.rs +++ b/rust/signed_doc/src/metadata/document_ref.rs @@ -21,17 +21,6 @@ impl Display for DocumentRef { } } -impl TryFrom for Value { - type Error = anyhow::Error; - - fn try_from(value: DocumentRef) -> Result { - Ok(Value::Array(vec![ - Value::try_from(CborUuidV7(value.id))?, - Value::try_from(CborUuidV7(value.ver))?, - ])) - } -} - impl TryFrom<&Value> for DocumentRef { type Error = anyhow::Error; @@ -53,3 +42,14 @@ impl TryFrom<&Value> for DocumentRef { Ok(DocumentRef { id, ver }) } } + +impl minicbor::Encode<()> for DocumentRef { + fn encode( + &self, e: &mut minicbor::Encoder, _ctx: &mut (), + ) -> Result<(), minicbor::encode::Error> { + e.array(2)? + .encode_with(self.id, &mut catalyst_types::uuid::CborContext::Tagged)? + .encode_with(self.ver, &mut catalyst_types::uuid::CborContext::Tagged)?; + Ok(()) + } +} diff --git a/rust/signed_doc/src/metadata/mod.rs b/rust/signed_doc/src/metadata/mod.rs index 10a2688081..7fc2ffb80e 100644 --- a/rust/signed_doc/src/metadata/mod.rs +++ b/rust/signed_doc/src/metadata/mod.rs @@ -16,7 +16,7 @@ pub(crate) mod utils; use catalyst_types::{problem_report::ProblemReport, uuid::UuidV7}; pub use content_encoding::ContentEncoding; pub use content_type::ContentType; -use coset::{cbor::Value, iana::CoapContentFormat, CborSerializable}; +use coset::CborSerializable; pub use doc_type::DocType; pub use document_ref::DocumentRef; use minicbor::{Decode, Decoder}; @@ -451,69 +451,7 @@ impl Display for Metadata { } } -impl TryFrom<&Metadata> for coset::Header { - type Error = anyhow::Error; - - fn try_from(meta: &Metadata) -> Result { - let mut builder = coset::HeaderBuilder::new() - .content_format(CoapContentFormat::from(meta.content_type()?)); - - if let Some(content_encoding) = meta.content_encoding() { - builder = builder.text_value( - CONTENT_ENCODING_KEY.to_string(), - format!("{content_encoding}").into(), - ); - } - - builder = builder - .text_value(TYPE_KEY.to_string(), meta.doc_type()?.to_value()) - .text_value( - ID_KEY.to_string(), - Value::try_from(CborUuidV7(meta.doc_id()?))?, - ) - .text_value( - VER_KEY.to_string(), - Value::try_from(CborUuidV7(meta.doc_ver()?))?, - ); - - if let Some(doc_ref) = meta.doc_ref() { - builder = builder.text_value(REF_KEY.to_string(), Value::try_from(doc_ref)?); - } - if let Some(template) = meta.template() { - builder = builder.text_value(TEMPLATE_KEY.to_string(), Value::try_from(template)?); - } - if let Some(reply) = meta.reply() { - builder = builder.text_value(REPLY_KEY.to_string(), Value::try_from(reply)?); - } - - if let Some(section) = meta.section() { - builder = builder.text_value(SECTION_KEY.to_string(), Value::from(section.clone())); - } - - if !meta.collabs().is_empty() { - builder = builder.text_value( - COLLABS_KEY.to_string(), - Value::Array(meta.collabs().iter().cloned().map(Value::Text).collect()), - ); - } - - if let Some(parameters) = meta.parameters() { - builder = builder.text_value(PARAMETERS_KEY.to_string(), Value::try_from(parameters)?); - } - - Ok(builder.build()) - } -} - -/// [`Metadata`] encoding context for the [`minicbor::Encode`] implementation. -pub(crate) struct MetadataEncodeContext { - /// Used by some fields' encoding implementations. - pub uuid_context: catalyst_types::uuid::CborContext, - /// Used by some fields' encoding implementations. - pub report: ProblemReport, -} - -impl minicbor::Encode for Metadata { +impl minicbor::Encode<()> for Metadata { /// Encode as a CBOR map. /// /// Note that to put it in an [RFC 8152] protected header. @@ -523,41 +461,22 @@ impl minicbor::Encode for Metadata { /// so the checks must be done elsewhere. /// /// [RFC 8152]: https://datatracker.ietf.org/doc/html/rfc8152#autoid-8 - #[allow( - clippy::cast_possible_truncation, - reason = "There can't be enough unique fields to overflow `u64`." - )] fn encode( - &self, e: &mut minicbor::Encoder, ctx: &mut MetadataEncodeContext, + &self, e: &mut minicbor::Encoder, _ctx: &mut (), ) -> Result<(), minicbor::encode::Error> { - e.map(self.0.len() as u64)?; + e.map( + self.0 + .len() + .try_into() + .map_err(minicbor::encode::Error::message)?, + )?; self.0 .values() - .try_fold(e, |e, field| e.encode_with(field, ctx))? + .try_fold(e, |e, field| e.encode(field))? .ok() } } -/// [`Metadata`] decoding context for the [`minicbor::Decode`] implementation. -pub(crate) struct MetadataDecodeContext { - /// Used by some fields' decoding implementations. - pub uuid_context: catalyst_types::uuid::CborContext, - /// Used by some fields' decoding implementations. - pub compatibility_policy: crate::CompatibilityPolicy, - /// Used by some fields' decoding implementations. - pub report: ProblemReport, -} - -impl MetadataDecodeContext { - /// [`DocType`] decoding context. - fn doc_type_context(&mut self) -> crate::decode_context::DecodeContext { - crate::decode_context::DecodeContext { - compatibility_policy: self.compatibility_policy, - report: &mut self.report, - } - } -} - /// An error that's been reported, but doesn't affect the further decoding. /// [`minicbor::Decoder`] should be assumed to be in a correct state and advanced towards /// the next item. @@ -579,7 +498,7 @@ fn custom_transient_decode_error( minicbor::decode::Error::custom(TransientDecodeError(inner)) } -impl minicbor::Decode<'_, MetadataDecodeContext> for Metadata { +impl minicbor::Decode<'_, crate::decode_context::DecodeContext<'_>> for Metadata { /// Decode from a CBOR map. /// /// Note that this won't decode an [RFC 8152] protected header as is. @@ -590,7 +509,7 @@ impl minicbor::Decode<'_, MetadataDecodeContext> for Metadata { /// /// [RFC 8152]: https://datatracker.ietf.org/doc/html/rfc8152#autoid-8 fn decode( - d: &mut Decoder<'_>, ctx: &mut MetadataDecodeContext, + d: &mut Decoder<'_>, ctx: &mut crate::decode_context::DecodeContext<'_>, ) -> Result { const REPORT_CONTEXT: &str = "Metadata decoding"; diff --git a/rust/signed_doc/src/metadata/section.rs b/rust/signed_doc/src/metadata/section.rs index 01e6a02a1b..f4d4834415 100644 --- a/rust/signed_doc/src/metadata/section.rs +++ b/rust/signed_doc/src/metadata/section.rs @@ -40,12 +40,6 @@ impl FromStr for Section { } } -impl From
for Value { - fn from(value: Section) -> Self { - Value::Text(value.to_string()) - } -} - impl TryFrom<&Value> for Section { type Error = anyhow::Error; @@ -56,3 +50,12 @@ impl TryFrom<&Value> for Section { Self::from_str(str) } } + +impl minicbor::Encode<()> for Section { + fn encode( + &self, e: &mut minicbor::Encoder, _ctx: &mut (), + ) -> Result<(), minicbor::encode::Error> { + e.str(self.to_string().as_str())?; + Ok(()) + } +} diff --git a/rust/signed_doc/src/metadata/supported_field.rs b/rust/signed_doc/src/metadata/supported_field.rs index 9ce4ecb122..757eba35a8 100644 --- a/rust/signed_doc/src/metadata/supported_field.rs +++ b/rust/signed_doc/src/metadata/supported_field.rs @@ -8,8 +8,8 @@ use catalyst_types::uuid::UuidV7; use strum::{EnumDiscriminants, EnumTryAs, IntoDiscriminant as _}; use crate::{ - metadata::{custom_transient_decode_error, MetadataDecodeContext, MetadataEncodeContext}, - ContentEncoding, ContentType, DocType, DocumentRef, Section, + metadata::custom_transient_decode_error, ContentEncoding, ContentType, DocType, DocumentRef, + Section, }; /// COSE label. May be either a signed integer or a string. @@ -27,9 +27,9 @@ enum Label<'a> { Str(&'a str), } -impl minicbor::Encode for Label<'_> { +impl minicbor::Encode<()> for Label<'_> { fn encode( - &self, e: &mut minicbor::Encoder, _: &mut C, + &self, e: &mut minicbor::Encoder, _ctx: &mut (), ) -> Result<(), minicbor::encode::Error> { match self { &Label::U8(u) => e.u8(u), @@ -166,10 +166,10 @@ impl Display for SupportedLabel { } } -impl minicbor::Decode<'_, MetadataDecodeContext> for SupportedField { +impl minicbor::Decode<'_, crate::decode_context::DecodeContext<'_>> for SupportedField { #[allow(clippy::todo, reason = "Not migrated to `minicbor` yet.")] fn decode( - d: &mut minicbor::Decoder<'_>, ctx: &mut MetadataDecodeContext, + d: &mut minicbor::Decoder<'_>, ctx: &mut crate::decode_context::DecodeContext<'_>, ) -> Result { const REPORT_CONTEXT: &str = "Metadata field decoding"; @@ -195,10 +195,16 @@ impl minicbor::Decode<'_, MetadataDecodeContext> for SupportedField { let field = match key { SupportedLabel::ContentType => todo!(), - SupportedLabel::Id => d.decode_with(&mut ctx.uuid_context).map(Self::Id), + SupportedLabel::Id => { + d.decode_with(&mut catalyst_types::uuid::CborContext::Tagged) + .map(Self::Id) + }, SupportedLabel::Ref => todo!(), - SupportedLabel::Ver => d.decode_with(&mut ctx.uuid_context).map(Self::Ver), - SupportedLabel::Type => d.decode_with(&mut ctx.doc_type_context()).map(Self::Type), + SupportedLabel::Ver => { + d.decode_with(&mut catalyst_types::uuid::CborContext::Tagged) + .map(Self::Ver) + }, + SupportedLabel::Type => d.decode_with(ctx).map(Self::Type), SupportedLabel::Reply => todo!(), SupportedLabel::Collabs => todo!(), SupportedLabel::Section => todo!(), @@ -211,29 +217,40 @@ impl minicbor::Decode<'_, MetadataDecodeContext> for SupportedField { } } -impl minicbor::Encode for SupportedField { - #[allow(clippy::todo, reason = "Not migrated to `minicbor` yet.")] +impl minicbor::Encode<()> for SupportedField { fn encode( - &self, e: &mut minicbor::Encoder, ctx: &mut MetadataEncodeContext, + &self, e: &mut minicbor::Encoder, ctx: &mut (), ) -> Result<(), minicbor::encode::Error> { let key = self.discriminant().to_cose(); e.encode(key)?; match self { - SupportedField::ContentType(_content_type) => todo!(), + SupportedField::ContentType(content_type) => content_type.encode(e, ctx), SupportedField::Id(uuid_v7) | SupportedField::Ver(uuid_v7) => { - e.encode_with(uuid_v7, &mut ctx.uuid_context)? + uuid_v7.encode(e, &mut catalyst_types::uuid::CborContext::Tagged) }, - SupportedField::Ref(_document_ref) - | SupportedField::Reply(_document_ref) - | SupportedField::Template(_document_ref) - | SupportedField::Parameters(_document_ref) => todo!(), - SupportedField::Type(doc_type) => e.encode_with(doc_type, &mut ctx.report)?, - SupportedField::Collabs(_items) => todo!(), - SupportedField::Section(_section) => todo!(), - SupportedField::ContentEncoding(_content_encoding) => todo!(), + SupportedField::Ref(document_ref) + | SupportedField::Reply(document_ref) + | SupportedField::Template(document_ref) + | SupportedField::Parameters(document_ref) => document_ref.encode(e, ctx), + SupportedField::Type(doc_type) => doc_type.encode(e, ctx), + SupportedField::Collabs(collabs) => { + if !collabs.is_empty() { + e.array( + collabs + .len() + .try_into() + .map_err(minicbor::encode::Error::message)?, + )?; + for c in collabs { + e.str(c)?; + } + } + Ok(()) + }, + SupportedField::Section(section) => section.encode(e, ctx), + SupportedField::ContentEncoding(content_encoding) => content_encoding.encode(e, ctx), } - .ok() } } diff --git a/rust/signed_doc/src/metadata/utils.rs b/rust/signed_doc/src/metadata/utils.rs index 0e54f10c43..5614e776cb 100644 --- a/rust/signed_doc/src/metadata/utils.rs +++ b/rust/signed_doc/src/metadata/utils.rs @@ -2,7 +2,7 @@ use catalyst_types::{ problem_report::ProblemReport, - uuid::{CborContext, UuidV4, UuidV7}, + uuid::{CborContext, UuidV7}, }; use coset::{CborSerializable, Label, ProtectedHeader}; @@ -39,24 +39,6 @@ where T: for<'a> TryFrom<&'a coset::cbor::Value> { None } -/// A convenient wrapper over the `UuidV4` type, to implement -/// `TryFrom` and `TryFrom for coset::cbor::Value` traits. -pub(crate) struct CborUuidV4(pub(crate) UuidV4); -impl TryFrom<&coset::cbor::Value> for CborUuidV4 { - type Error = anyhow::Error; - - fn try_from(value: &coset::cbor::Value) -> Result { - Ok(Self(decode_cbor_uuid(value)?)) - } -} -impl TryFrom for coset::cbor::Value { - type Error = anyhow::Error; - - fn try_from(value: CborUuidV4) -> Result { - encode_cbor_uuid(value.0) - } -} - /// A convenient wrapper over the `UuidV7` type, to implement /// `TryFrom` and `TryFrom for coset::cbor::Value` traits. pub(crate) struct CborUuidV7(pub(crate) UuidV7); diff --git a/rust/signed_doc/src/signature/mod.rs b/rust/signed_doc/src/signature/mod.rs index 31dec15b51..c5b884d6c3 100644 --- a/rust/signed_doc/src/signature/mod.rs +++ b/rust/signed_doc/src/signature/mod.rs @@ -4,20 +4,37 @@ pub use catalyst_types::catalyst_id::CatalystId; use catalyst_types::problem_report::ProblemReport; use coset::CoseSignature; +use crate::{Content, Metadata}; + /// Catalyst Signed Document COSE Signature. #[derive(Debug, Clone)] pub struct Signature { /// Key ID kid: CatalystId, - /// COSE Signature - signature: CoseSignature, + /// Raw signature data + signature: Vec, } impl Signature { + /// Creates a `Signature` object from `kid` and raw `signature` bytes + pub(crate) fn new(kid: CatalystId, signature: Vec) -> Self { + Self { kid, signature } + } + + /// Return `kid` field (`CatalystId`), identifier who made a signature + pub fn kid(&self) -> &CatalystId { + &self.kid + } + + /// Return raw signature bytes itself + pub fn signature(&self) -> &[u8] { + &self.signature + } + /// Convert COSE Signature to `Signature`. pub(crate) fn from_cose_sig(signature: CoseSignature, report: &ProblemReport) -> Option { match CatalystId::try_from(signature.protected.header.key_id.as_ref()) { - Ok(kid) if kid.is_uri() => Some(Self { kid, signature }), + Ok(kid) if kid.is_uri() => Some(Self::new(kid, signature.signature)), Ok(kid) => { report.invalid_value( "COSE signature protected header key ID", @@ -47,28 +64,9 @@ impl Signature { pub struct Signatures(Vec); impl Signatures { - /// Return a list of author IDs (short form of Catalyst IDs). - #[must_use] - pub(crate) fn authors(&self) -> Vec { - self.kids().into_iter().map(|k| k.as_short_id()).collect() - } - - /// Return a list of Document's Catalyst IDs. - #[must_use] - pub(crate) fn kids(&self) -> Vec { - self.0.iter().map(|sig| sig.kid.clone()).collect() - } - - /// Iterator of COSE signatures object with kids. - pub(crate) fn cose_signatures_with_kids( - &self, - ) -> impl Iterator + use<'_> { - self.0.iter().map(|sig| (&sig.signature, &sig.kid)) - } - - /// List of COSE signatures object. - pub(crate) fn cose_signatures(&self) -> impl Iterator + use<'_> { - self.0.iter().map(|sig| sig.signature.clone()) + /// Return an iterator over the signatures + pub fn iter(&self) -> impl Iterator + use<'_> { + self.0.iter() } /// Add a `Signature` object into the list @@ -105,3 +103,63 @@ impl Signatures { Self(res) } } + +/// Create a binary blob that will be signed. No support for unprotected headers. +/// +/// Described in [section 2 of RFC 8152](https://datatracker.ietf.org/doc/html/rfc8152#section-2). +pub(crate) fn tbs_data( + kid: &CatalystId, metadata: &Metadata, content: &Content, +) -> anyhow::Result> { + Ok(minicbor::to_vec(( + // The context string as per [RFC 8152 section 4.4](https://datatracker.ietf.org/doc/html/rfc8152#section-4.4). + "Signature", + ::from(minicbor::to_vec(metadata)?), + ::from(protected_header_bytes(kid)?), + minicbor::bytes::ByteArray::from([]), + ::from(content.encoded_bytes(metadata.content_encoding())?), + ))?) +} + +impl minicbor::Encode<()> for Signature { + fn encode( + &self, e: &mut minicbor::Encoder, _ctx: &mut (), + ) -> Result<(), minicbor::encode::Error> { + e.array(3)?; + e.bytes( + protected_header_bytes(&self.kid) + .map_err(minicbor::encode::Error::message)? + .as_slice(), + )?; + // empty unprotected headers + e.map(0)?; + e.bytes(&self.signature)?; + Ok(()) + } +} + +impl minicbor::Encode<()> for Signatures { + fn encode( + &self, e: &mut minicbor::Encoder, _ctx: &mut (), + ) -> Result<(), minicbor::encode::Error> { + e.array( + self.0 + .len() + .try_into() + .map_err(minicbor::encode::Error::message)?, + )?; + for sign in self.iter() { + e.encode(sign)?; + } + Ok(()) + } +} + +/// Signatures protected header bytes +/// +/// Described in [section 3.1 of RFC 8152](https://datatracker.ietf.org/doc/html/rfc8152#section-3.1). +fn protected_header_bytes(kid: &CatalystId) -> anyhow::Result> { + let mut p_headers = minicbor::Encoder::new(Vec::new()); + // protected headers (kid field) + p_headers.map(1)?.u8(4)?.encode(kid)?; + Ok(p_headers.into_writer()) +} diff --git a/rust/signed_doc/src/validator/mod.rs b/rust/signed_doc/src/validator/mod.rs index da1b4604d2..c5acfb3a9e 100644 --- a/rust/signed_doc/src/validator/mod.rs +++ b/rust/signed_doc/src/validator/mod.rs @@ -10,11 +10,7 @@ use std::{ }; use anyhow::Context; -use catalyst_types::{ - catalyst_id::{role_index::RoleId, CatalystId}, - problem_report::ProblemReport, -}; -use coset::{CoseSign, CoseSignature}; +use catalyst_types::{catalyst_id::role_index::RoleId, problem_report::ProblemReport}; use rules::{ ContentEncodingRule, ContentRule, ContentSchema, ContentTypeRule, ParametersRule, RefRule, ReplyRule, Rules, SectionRule, SignatureKidRule, @@ -30,6 +26,7 @@ use crate::{ }, metadata::DocType, providers::{CatalystSignedDocumentProvider, VerifyingKeyProvider}, + signature::{tbs_data, Signature}, CatalystSignedDocument, ContentEncoding, ContentType, }; @@ -286,14 +283,6 @@ where Provider: CatalystSignedDocumentProvider { pub async fn validate_signatures( doc: &CatalystSignedDocument, provider: &impl VerifyingKeyProvider, ) -> anyhow::Result { - let Ok(cose_sign) = doc.as_cose_sign() else { - doc.report().other( - "Cannot build a COSE sign object", - "During encoding signed document as COSE SIGN", - ); - return Ok(false); - }; - if doc.signatures().is_empty() { doc.report().other( "Catalyst Signed Document is unsigned", @@ -304,10 +293,8 @@ pub async fn validate_signatures( let sign_rules = doc .signatures() - .cose_signatures_with_kids() - .map(|(signature, kid)| { - validate_signature(&cose_sign, signature, kid, provider, doc.report()) - }); + .iter() + .map(|sign| validate_signature(doc, sign, provider, doc.report())); let res = futures::future::join_all(sign_rules) .await @@ -321,12 +308,11 @@ pub async fn validate_signatures( /// A single signature validation function async fn validate_signature( - cose_sign: &CoseSign, signature: &CoseSignature, kid: &CatalystId, provider: &Provider, - report: &ProblemReport, + doc: &CatalystSignedDocument, sign: &Signature, provider: &Provider, report: &ProblemReport, ) -> anyhow::Result -where - Provider: VerifyingKeyProvider, -{ +where Provider: VerifyingKeyProvider { + let kid = sign.kid(); + let Some(pk) = provider.try_get_key(kid).await? else { report.other( &format!("Missing public key for {kid}."), @@ -335,11 +321,18 @@ where return Ok(false); }; - let tbs_data = cose_sign.tbs_data(&[], signature); - let Ok(signature_bytes) = signature.signature.as_slice().try_into() else { + let Ok(tbs_data) = tbs_data(kid, doc.doc_meta(), doc.doc_content()) else { + doc.report().other( + "Cannot build a COSE to be signed data", + "During creating COSE to be signed data", + ); + return Ok(false); + }; + + let Ok(signature_bytes) = sign.signature().try_into() else { report.invalid_value( "cose signature", - &format!("{}", signature.signature.len()), + &format!("{}", sign.signature().len()), &format!("must be {}", ed25519_dalek::Signature::BYTE_SIZE), "During encoding cose signature to bytes", ); diff --git a/rust/signed_doc/src/validator/rules/signature_kid.rs b/rust/signed_doc/src/validator/rules/signature_kid.rs index 2e45517b8e..7665fa7ad9 100644 --- a/rust/signed_doc/src/validator/rules/signature_kid.rs +++ b/rust/signed_doc/src/validator/rules/signature_kid.rs @@ -68,7 +68,7 @@ mod tests { "content-type": ContentType::Json.to_string(), })) .unwrap() - .add_signature(|m| sk.sign(&m).to_vec(), &kid) + .add_signature(|m| sk.sign(&m).to_vec(), kid) .unwrap() .build(); diff --git a/rust/signed_doc/tests/common/mod.rs b/rust/signed_doc/tests/common/mod.rs index ae3d348f8a..87aa4900a1 100644 --- a/rust/signed_doc/tests/common/mod.rs +++ b/rust/signed_doc/tests/common/mod.rs @@ -106,7 +106,7 @@ pub fn create_dummy_signed_doc( let signed_doc = Builder::new() .with_decoded_content(content) .with_json_metadata(metadata)? - .add_signature(|m| sk.sign(&m).to_vec(), &kid)? + .add_signature(|m| sk.sign(&m).to_vec(), kid.clone())? .build(); Ok((signed_doc, pk, kid)) diff --git a/rust/signed_doc/tests/decoding.rs b/rust/signed_doc/tests/decoding.rs index 795183e699..1d8b4354ea 100644 --- a/rust/signed_doc/tests/decoding.rs +++ b/rust/signed_doc/tests/decoding.rs @@ -3,40 +3,12 @@ use catalyst_signed_doc::{providers::tests::TestVerifyingKeyProvider, *}; use catalyst_types::catalyst_id::role_index::RoleId; use common::create_dummy_key_pair; -use coset::TaggedCborSerializable; +use coset::{CborSerializable, TaggedCborSerializable}; use ed25519_dalek::ed25519::signature::Signer; +use minicbor::{data::Tag, Encoder}; mod common; -#[test] -fn catalyst_signed_doc_cbor_roundtrip_kid_as_id_test() { - catalyst_signed_doc_cbor_roundtrip_kid_as_id(common::test_metadata()); - catalyst_signed_doc_cbor_roundtrip_kid_as_id(common::test_metadata_specific_type( - Some(doc_types::PROPOSAL_UUID_TYPE.try_into().unwrap()), - None, - )); -} - -#[allow(clippy::unwrap_used)] -fn catalyst_signed_doc_cbor_roundtrip_kid_as_id(data: (UuidV7, UuidV4, serde_json::Value)) { - let (_, _, metadata_fields) = data; - let (sk, _, kid) = create_dummy_key_pair(RoleId::Role0).unwrap(); - // transform Catalyst ID URI form to the ID form - let kid = kid.as_id(); - - let content = serde_json::to_vec(&serde_json::Value::Null).unwrap(); - - let doc = Builder::new() - .with_json_metadata(metadata_fields.clone()) - .unwrap() - .with_decoded_content(content.clone()) - .add_signature(|m| sk.sign(&m).to_vec(), &kid) - .unwrap() - .build(); - - assert!(doc.problem_report().is_problematic()); -} - #[tokio::test] async fn catalyst_signed_doc_parameters_aliases_test() { catalyst_signed_doc_parameters_aliases(common::test_metadata()).await; @@ -65,7 +37,9 @@ async fn catalyst_signed_doc_parameters_aliases(data: (UuidV7, UuidV4, serde_jso assert!(!doc.problem_report().is_problematic()); let parameters_val = doc.doc_meta().parameters().unwrap(); - let parameters_val_cbor: coset::cbor::Value = parameters_val.try_into().unwrap(); + let parameters_val_cbor = + coset::cbor::Value::from_slice(minicbor::to_vec(parameters_val).unwrap().as_slice()) + .unwrap(); // replace parameters with the alias values `category_id`, `brand_id`, `campaign_id`. let bytes: Vec = doc.try_into().unwrap(); let mut cose = coset::CoseSign::from_tagged_slice(bytes.as_slice()).unwrap(); @@ -96,7 +70,7 @@ async fn catalyst_signed_doc_parameters_aliases(data: (UuidV7, UuidV4, serde_jso let doc: CatalystSignedDocument = cbor_bytes.as_slice().try_into().unwrap(); let doc = doc .into_builder() - .add_signature(|m| sk.sign(&m).to_vec(), &kid) + .add_signature(|m| sk.sign(&m).to_vec(), kid.clone()) .unwrap() .build(); assert!(!doc.problem_report().is_problematic()); @@ -116,7 +90,7 @@ async fn catalyst_signed_doc_parameters_aliases(data: (UuidV7, UuidV4, serde_jso let doc: CatalystSignedDocument = cbor_bytes.as_slice().try_into().unwrap(); let doc = doc .into_builder() - .add_signature(|m| sk.sign(&m).to_vec(), &kid) + .add_signature(|m| sk.sign(&m).to_vec(), kid.clone()) .unwrap() .build(); assert!(!doc.problem_report().is_problematic()); @@ -136,7 +110,7 @@ async fn catalyst_signed_doc_parameters_aliases(data: (UuidV7, UuidV4, serde_jso let doc: CatalystSignedDocument = cbor_bytes.as_slice().try_into().unwrap(); let doc = doc .into_builder() - .add_signature(|m| sk.sign(&m).to_vec(), &kid) + .add_signature(|m| sk.sign(&m).to_vec(), kid) .unwrap() .build(); assert!(!doc.problem_report().is_problematic()); @@ -177,7 +151,7 @@ type PostCheck = dyn Fn(&CatalystSignedDocument) -> bool; struct TestCase { name: &'static str, - bytes_gen: Box Vec>, + bytes_gen: Box anyhow::Result>>>, // If the provided bytes can be even decoded without error (valid COSE or not). // If set to `false` all further checks will not even happen. can_decode: bool, @@ -189,7 +163,7 @@ struct TestCase { fn decoding_empty_bytes_case() -> TestCase { TestCase { name: "Decoding empty bytes", - bytes_gen: Box::new(Vec::new), + bytes_gen: Box::new(|| Ok(Encoder::new(Vec::new()))), can_decode: false, valid_doc: false, post_checks: None, @@ -200,34 +174,41 @@ fn decoding_empty_bytes_case() -> TestCase { fn signed_doc_with_all_fields_case() -> TestCase { let uuid_v7 = UuidV7::new(); let uuid_v4 = UuidV4::new(); - let (sk, _, kid) = create_dummy_key_pair(RoleId::Role0).unwrap(); TestCase { - name: "Catalyst Signed Doc with ALL defined metadata fields and signatures", + name: "Catalyst Signed Doc with minimally defined metadata fields, signed (one signature), CBOR tagged.", bytes_gen: Box::new({ - let kid = kid.clone(); move || { - Builder::new() - .with_json_metadata(serde_json::json!({ - "content-type": ContentType::Json.to_string(), - "content-encoding": ContentEncoding::Brotli.to_string(), - "type": uuid_v4.to_string(), - "id": uuid_v7.to_string(), - "ver": uuid_v7.to_string(), - "ref": {"id": uuid_v7.to_string(), "ver": uuid_v7.to_string()}, - "reply": {"id": uuid_v7.to_string(), "ver": uuid_v7.to_string()}, - "template": {"id": uuid_v7.to_string(), "ver": uuid_v7.to_string()}, - "section": "$".to_string(), - "collabs": vec!["Alex1".to_string(), "Alex2".to_string()], - "parameters": {"id": uuid_v7.to_string(), "ver": uuid_v7.to_string()}, - })) - .unwrap() - .with_decoded_content(serde_json::to_vec(&serde_json::Value::Null).unwrap()) - .add_signature(|m| sk.sign(&m).to_vec(), &kid) - .unwrap() - .build() - .try_into() - .unwrap() + let (_, _, kid) = create_dummy_key_pair(RoleId::Role0).unwrap(); + + let mut e = Encoder::new(Vec::new()); + e.tag(Tag::new(98))?; + e.array(4)?; + // protected headers (metadata fields) + let mut p_headers = Encoder::new(Vec::new()); + + p_headers.map(4)?; + p_headers.u8(3)?.encode(ContentType::Json)?; + p_headers.str("type")?.encode_with(uuid_v4, &mut catalyst_types::uuid::CborContext::Tagged)?; + p_headers.str("id")?.encode_with(uuid_v7, &mut catalyst_types::uuid::CborContext::Tagged)?; + p_headers.str("ver")?.encode_with(uuid_v7, &mut catalyst_types::uuid::CborContext::Tagged)?; + + e.bytes(p_headers.into_writer().as_slice())?; + // empty unprotected headers + e.map(0)?; + // content + e.bytes(serde_json::to_vec(&serde_json::Value::Null)?.as_slice())?; + // signatures + // one signature + e.array(1)?; + e.array(3)?; + // protected headers (kid field) + let mut p_headers = minicbor::Encoder::new(Vec::new()); + p_headers.map(1)?.u8(4)?.encode(kid)?; + e.bytes(p_headers.into_writer().as_slice())?; + e.map(0)?; + e.bytes(&[1,2,3])?; + Ok(e) } }), can_decode: true, @@ -238,32 +219,8 @@ fn signed_doc_with_all_fields_case() -> TestCase { && (doc.doc_id().unwrap() == uuid_v7) && (doc.doc_ver().unwrap() == uuid_v7) && (doc.doc_content_type().unwrap() == ContentType::Json) - && (doc.doc_content_encoding().unwrap() == ContentEncoding::Brotli) - && (doc.doc_meta().doc_ref().unwrap() - == DocumentRef { - id: uuid_v7, - ver: uuid_v7, - }) - && (doc.doc_meta().reply().unwrap() - == DocumentRef { - id: uuid_v7, - ver: uuid_v7, - }) - && (doc.doc_meta().template().unwrap() - == DocumentRef { - id: uuid_v7, - ver: uuid_v7, - }) - && (doc.doc_meta().parameters().unwrap() - == DocumentRef { - id: uuid_v7, - ver: uuid_v7, - }) - && (doc.doc_meta().section().unwrap() == &"$".parse::
().unwrap()) - && (doc.doc_meta().collabs() == ["Alex1".to_string(), "Alex2".to_string()]) && (doc.doc_content().decoded_bytes().unwrap() - == serde_json::to_vec(&serde_json::Value::Null).unwrap()) - && (doc.kids() == vec![kid.clone()]) + == serde_json::to_vec(&serde_json::Value::Null).unwrap()) && doc.kids().len() == 1 } })), } @@ -277,9 +234,15 @@ fn catalyst_signed_doc_decoding_test() { ]; for case in test_cases { - let bytes = case.bytes_gen.as_ref()(); + let bytes = case.bytes_gen.as_ref()().unwrap().into_writer(); let doc_res = CatalystSignedDocument::try_from(bytes.as_slice()); - assert_eq!(doc_res.is_ok(), case.can_decode, "Case: [{}]", case.name); + assert_eq!( + doc_res.is_ok(), + case.can_decode, + "Case: [{}], error: {:?}", + case.name, + doc_res.err() + ); if let Ok(doc) = doc_res { assert_eq!( !doc.problem_report().is_problematic(), @@ -290,7 +253,11 @@ fn catalyst_signed_doc_decoding_test() { ); if let Some(post_checks) = &case.post_checks { - assert!((post_checks.as_ref())(&doc), "Case: [{}]", case.name); + assert!( + (post_checks.as_ref())(&doc), + "Case: [{}]. Post checks fails", + case.name + ); } assert_eq!( diff --git a/rust/signed_doc/tests/signature.rs b/rust/signed_doc/tests/signature.rs index 5c93ec25bb..e734897d2b 100644 --- a/rust/signed_doc/tests/signature.rs +++ b/rust/signed_doc/tests/signature.rs @@ -50,11 +50,11 @@ async fn multiple_signatures_validation_test() { .with_decoded_content(serde_json::to_vec(&serde_json::Value::Null).unwrap()) .with_json_metadata(common::test_metadata().2) .unwrap() - .add_signature(|m| sk1.sign(&m).to_vec(), &kid1) + .add_signature(|m| sk1.sign(&m).to_vec(), kid1.clone()) .unwrap() - .add_signature(|m| sk2.sign(&m).to_vec(), &kid2) + .add_signature(|m| sk2.sign(&m).to_vec(), kid2.clone()) .unwrap() - .add_signature(|m| sk3.sign(&m).to_vec(), &kid3) + .add_signature(|m| sk3.sign(&m).to_vec(), kid3.clone()) .unwrap() .build();