diff --git a/rust/signed_doc/src/builder.rs b/rust/signed_doc/src/builder.rs index 8a2c6c925a..6efdca778d 100644 --- a/rust/signed_doc/src/builder.rs +++ b/rust/signed_doc/src/builder.rs @@ -26,6 +26,7 @@ impl Builder { metadata: Metadata::default(), content: Content::default(), signatures: Signatures::default(), + raw_bytes: None, }) } @@ -91,6 +92,7 @@ impl From<&CatalystSignedDocument> for Builder { content: value.inner.content.clone(), signatures: value.inner.signatures.clone(), report: value.inner.report.clone(), + raw_bytes: None, }) } } diff --git a/rust/signed_doc/src/lib.rs b/rust/signed_doc/src/lib.rs index 39908e7f11..c5d5779733 100644 --- a/rust/signed_doc/src/lib.rs +++ b/rust/signed_doc/src/lib.rs @@ -41,6 +41,11 @@ struct InnerCatalystSignedDocument { /// A comprehensive problem report, which could include a decoding errors along with /// the other validation errors report: ProblemReport, + + /// raw CBOR bytes of the `CatalystSignedDocument` object. + /// It is important to keep them to have a consistency what comes from the decoding + /// process, so we would return the same data again + raw_bytes: Option>, } /// Keep all the contents private. @@ -187,21 +192,30 @@ impl InnerCatalystSignedDocument { /// # Errors /// Could fails if the `CatalystSignedDocument` object is not valid. fn as_cose_sign(&self) -> anyhow::Result { - let protected_header = - Header::try_from(&self.metadata).context("Failed to encode Document Metadata")?; + 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 content = self + .content + .encoded_bytes(self.metadata.content_encoding())?; - let mut builder = coset::CoseSignBuilder::new() - .protected(protected_header) - .payload(content); + let mut builder = coset::CoseSignBuilder::new() + .protected(protected_header) + .payload(content); - for signature in self.signatures.cose_signatures() { - builder = builder.add_signature(signature); + for signature in self.signatures.cose_signatures() { + builder = builder.add_signature(signature); + } + Ok(builder.build()) } - Ok(builder.build()) } } @@ -237,6 +251,7 @@ impl Decode<'_, ()> for CatalystSignedDocument { content, signatures, report, + raw_bytes: Some(cose_bytes.to_vec()), } .into()) } @@ -247,7 +262,6 @@ impl Encode<()> for CatalystSignedDocument { &self, e: &mut encode::Encoder, _ctx: &mut (), ) -> 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}")) })?; diff --git a/rust/signed_doc/tests/decoding.rs b/rust/signed_doc/tests/decoding.rs index 38a6615aaf..c1f632f84a 100644 --- a/rust/signed_doc/tests/decoding.rs +++ b/rust/signed_doc/tests/decoding.rs @@ -1,6 +1,6 @@ //! Integration test for COSE decoding part. -use catalyst_signed_doc::*; +use catalyst_signed_doc::{providers::tests::TestVerifyingKeyProvider, *}; use catalyst_types::catalyst_id::role_index::RoleId; use common::create_dummy_key_pair; use coset::TaggedCborSerializable; @@ -56,9 +56,13 @@ fn catalyst_signed_doc_cbor_roundtrip_kid_as_id_test() { assert!(doc.problem_report().is_problematic()); } -#[test] -fn catalyst_signed_doc_parameters_aliases_test() { +#[tokio::test] +#[allow(clippy::too_many_lines)] +async fn catalyst_signed_doc_parameters_aliases_test() { let (_, _, metadata_fields) = common::test_metadata(); + let (sk, pk, kid) = common::create_dummy_key_pair(RoleId::Role0).unwrap(); + let mut provider = TestVerifyingKeyProvider::default(); + provider.add_pk(kid.clone(), pk); let content = serde_json::to_vec(&serde_json::Value::Null).unwrap(); @@ -97,46 +101,58 @@ fn catalyst_signed_doc_parameters_aliases_test() { parameters_val_cbor.clone(), )); - let doc: CatalystSignedDocument = cose_with_category_id - .to_tagged_vec() + let cbor_bytes = cose_with_category_id.to_tagged_vec().unwrap(); + let doc: CatalystSignedDocument = cbor_bytes.as_slice().try_into().unwrap(); + let doc = doc + .into_builder() + .add_signature(|m| sk.sign(&m).to_vec(), &kid) .unwrap() - .as_slice() - .try_into() - .unwrap(); + .build(); assert!(!doc.problem_report().is_problematic()); assert!(doc.doc_meta().parameters().is_some()); + assert!(validator::validate_signatures(&doc, &provider) + .await + .unwrap()); // case: `brand_id`. - let mut cose_with_category_id = cose.clone(); - cose_with_category_id.protected.header.rest.push(( + let mut cose_with_brand_id = cose.clone(); + cose_with_brand_id.protected.header.rest.push(( coset::Label::Text("brand_id".to_string()), parameters_val_cbor.clone(), )); - let doc: CatalystSignedDocument = cose_with_category_id - .to_tagged_vec() + let cbor_bytes = cose_with_brand_id.to_tagged_vec().unwrap(); + let doc: CatalystSignedDocument = cbor_bytes.as_slice().try_into().unwrap(); + let doc = doc + .into_builder() + .add_signature(|m| sk.sign(&m).to_vec(), &kid) .unwrap() - .as_slice() - .try_into() - .unwrap(); + .build(); assert!(!doc.problem_report().is_problematic()); assert!(doc.doc_meta().parameters().is_some()); + assert!(validator::validate_signatures(&doc, &provider) + .await + .unwrap()); // case: `campaign_id`. - let mut cose_with_category_id = cose.clone(); - cose_with_category_id.protected.header.rest.push(( + let mut cose_with_campaign_id = cose.clone(); + cose_with_campaign_id.protected.header.rest.push(( coset::Label::Text("campaign_id".to_string()), parameters_val_cbor.clone(), )); - let doc: CatalystSignedDocument = cose_with_category_id - .to_tagged_vec() + let cbor_bytes = cose_with_campaign_id.to_tagged_vec().unwrap(); + let doc: CatalystSignedDocument = cbor_bytes.as_slice().try_into().unwrap(); + let doc = doc + .into_builder() + .add_signature(|m| sk.sign(&m).to_vec(), &kid) .unwrap() - .as_slice() - .try_into() - .unwrap(); + .build(); assert!(!doc.problem_report().is_problematic()); assert!(doc.doc_meta().parameters().is_some()); + assert!(validator::validate_signatures(&doc, &provider) + .await + .unwrap()); // `parameters` value along with its aliases are not allowed to be present at the let mut cose_with_category_id = cose.clone();