Skip to content

Commit 3955197

Browse files
Merge branch 'main' into arithmetic_side_effects-clippy-lint
2 parents f83cb73 + c777f93 commit 3955197

File tree

11 files changed

+363
-184
lines changed

11 files changed

+363
-184
lines changed

rust/signed_doc/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ brotli = "7.0.0"
2121
ed25519-dalek = { version = "2.1.1", features = ["pem", "rand_core"] }
2222
uuid = { version = "1.11.0", features = ["v4", "v7", "serde"] }
2323
hex = "0.4.3"
24-
thiserror = "2.0.9"
24+
strum = { version = "0.26.3", features = ["derive"] }
2525

2626
[dev-dependencies]
2727
clap = { version = "4.5.23", features = ["derive", "env"] }

rust/signed_doc/examples/mk_signed_doc.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::{
88
path::PathBuf,
99
};
1010

11-
use catalyst_signed_doc::{Builder, CatalystSignedDocument, Decode, Decoder, KidUri, Metadata};
11+
use catalyst_signed_doc::{Builder, CatalystSignedDocument, KidUri, Metadata};
1212
use clap::Parser;
1313
use coset::CborSerializable;
1414
use ed25519_dalek::{ed25519::signature::Signer, pkcs8::DecodePrivateKey};
@@ -107,7 +107,8 @@ fn decode_signed_doc(cose_bytes: &[u8]) {
107107
cose_bytes.len(),
108108
hex::encode(cose_bytes)
109109
);
110-
match CatalystSignedDocument::decode(&mut Decoder::new(cose_bytes), &mut ()) {
110+
111+
match CatalystSignedDocument::try_from(cose_bytes) {
111112
Ok(cat_signed_doc) => {
112113
println!("This is a valid Catalyst Document.");
113114
println!("{cat_signed_doc}");

rust/signed_doc/src/error.rs

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,48 @@
1-
//! Catalyst Signed Document errors.
1+
//! Catalyst Signed Document Error
22
3-
/// Catalyst Signed Document error.
4-
#[derive(thiserror::Error, Debug)]
5-
#[error("Catalyst Signed Document Error: {0:?}")]
6-
pub struct Error(pub(crate) List);
3+
use std::fmt;
74

8-
/// List of errors.
5+
use catalyst_types::problem_report::ProblemReport;
6+
7+
/// Catalyst Signed Document Error
8+
#[allow(clippy::module_name_repetitions)]
99
#[derive(Debug)]
10-
pub(crate) struct List(pub(crate) Vec<anyhow::Error>);
10+
pub struct CatalystSignedDocError {
11+
/// List of errors during processing.
12+
report: ProblemReport,
13+
/// Actual error.
14+
error: anyhow::Error,
15+
}
1116

12-
impl From<Vec<anyhow::Error>> for List {
13-
fn from(e: Vec<anyhow::Error>) -> Self {
14-
Self(e)
17+
impl CatalystSignedDocError {
18+
/// Create a new `CatalystSignedDocError`.
19+
#[must_use]
20+
pub fn new(report: ProblemReport, error: anyhow::Error) -> Self {
21+
Self { report, error }
1522
}
16-
}
1723

18-
impl From<Vec<anyhow::Error>> for Error {
19-
fn from(e: Vec<anyhow::Error>) -> Self {
20-
Self(e.into())
24+
/// Get the error report.
25+
#[must_use]
26+
pub fn report(&self) -> &ProblemReport {
27+
&self.report
28+
}
29+
30+
/// Get the actual error.
31+
#[must_use]
32+
pub fn error(&self) -> &anyhow::Error {
33+
&self.error
2134
}
2235
}
2336

24-
impl Error {
25-
/// List of errors.
26-
pub fn errors(&self) -> &Vec<anyhow::Error> {
27-
&self.0 .0
37+
impl fmt::Display for CatalystSignedDocError {
38+
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
39+
let report_json = serde_json::to_string(&self.report)
40+
.unwrap_or_else(|_| String::from("Failed to serialize ProblemReport"));
41+
42+
write!(
43+
fmt,
44+
"CatalystSignedDocError {{ error: {}, report: {} }}",
45+
self.error, report_json
46+
)
2847
}
2948
}

rust/signed_doc/src/lib.rs

Lines changed: 73 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,26 @@
22
33
mod builder;
44
mod content;
5-
mod error;
5+
pub mod error;
66
mod metadata;
77
mod signature;
8+
mod utils;
89

910
use std::{
1011
convert::TryFrom,
1112
fmt::{Display, Formatter},
1213
sync::Arc,
1314
};
1415

15-
use anyhow::anyhow;
1616
pub use builder::Builder;
17+
use catalyst_types::problem_report::ProblemReport;
1718
pub use content::Content;
1819
use coset::{CborSerializable, Header};
20+
use error::CatalystSignedDocError;
1921
pub use metadata::{DocumentRef, ExtraFields, Metadata, UuidV4, UuidV7};
2022
pub use minicbor::{decode, encode, Decode, Decoder, Encode};
2123
pub use signature::{KidUri, Signatures};
24+
use utils::context::DecodeSignDocCtx;
2225

2326
/// Inner type that holds the Catalyst Signed Document with parsing errors.
2427
#[derive(Debug, Clone)]
@@ -104,8 +107,20 @@ impl CatalystSignedDocument {
104107
}
105108
}
106109

107-
impl Decode<'_, ()> for CatalystSignedDocument {
108-
fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result<Self, decode::Error> {
110+
impl TryFrom<&[u8]> for CatalystSignedDocument {
111+
type Error = CatalystSignedDocError;
112+
113+
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
114+
let error_report = ProblemReport::new("Catalyst Signed Document");
115+
let mut ctx = DecodeSignDocCtx { error_report };
116+
let decoded: CatalystSignedDocument = minicbor::decode_with(value, &mut ctx)
117+
.map_err(|e| CatalystSignedDocError::new(ctx.error_report, e.into()))?;
118+
Ok(decoded)
119+
}
120+
}
121+
122+
impl Decode<'_, DecodeSignDocCtx> for CatalystSignedDocument {
123+
fn decode(d: &mut Decoder<'_>, ctx: &mut DecodeSignDocCtx) -> Result<Self, decode::Error> {
109124
let start = d.position();
110125
d.skip()?;
111126
let end = d.position();
@@ -115,40 +130,67 @@ impl Decode<'_, ()> for CatalystSignedDocument {
115130
.ok_or(minicbor::decode::Error::end_of_input())?;
116131

117132
let cose_sign = coset::CoseSign::from_slice(cose_bytes).map_err(|e| {
133+
ctx.error_report.invalid_value(
134+
"COSE sign document bytes",
135+
&format!("{:?}", &cose_bytes),
136+
&format!("Cannot convert bytes to CoseSign {e:?}"),
137+
"Creating COSE Sign document",
138+
);
118139
minicbor::decode::Error::message(format!("Invalid COSE Sign document: {e}"))
119140
})?;
120141

121-
let mut errors = Vec::new();
122-
123-
let metadata = Metadata::try_from(&cose_sign.protected).map_or_else(
124-
|e| {
125-
errors.extend(e.0 .0);
126-
None
127-
},
128-
Some,
129-
);
130-
let signatures = Signatures::try_from(&cose_sign.signatures).map_or_else(
131-
|e| {
132-
errors.extend(e.0 .0);
133-
None
134-
},
135-
Some,
136-
);
142+
let metadata = Metadata::from_protected_header(&cose_sign.protected, &ctx.error_report)
143+
.map_or_else(
144+
|e| {
145+
ctx.error_report.conversion_error(
146+
"COSE sign protected header",
147+
&format!("{:?}", &cose_sign.protected),
148+
&format!("Expected Metadata: {e:?}"),
149+
"Converting COSE Sign protected header to Metadata",
150+
);
151+
None
152+
},
153+
Some,
154+
);
155+
let signatures = Signatures::from_cose_sig(&cose_sign.signatures, &ctx.error_report)
156+
.map_or_else(
157+
|e| {
158+
ctx.error_report.conversion_error(
159+
"COSE sign signatures",
160+
&format!("{:?}", &cose_sign.signatures),
161+
&format!("Expected Signatures {e:?}"),
162+
"Converting COSE Sign signatures to Signatures",
163+
);
164+
None
165+
},
166+
Some,
167+
);
137168

138169
if cose_sign.payload.is_none() {
139-
errors.push(anyhow!("Document Content is missing"));
170+
ctx.error_report
171+
.missing_field("COSE Sign Payload", "Missing document content (payload)");
140172
}
141173

142174
match (cose_sign.payload, metadata, signatures) {
143175
(Some(payload), Some(metadata), Some(signatures)) => {
144176
let content = Content::from_encoded(
145-
payload,
177+
payload.clone(),
146178
metadata.content_type(),
147179
metadata.content_encoding(),
148180
)
149181
.map_err(|e| {
150-
errors.push(anyhow!("Invalid Document Content: {e}"));
151-
minicbor::decode::Error::message(error::Error::from(errors))
182+
ctx.error_report.invalid_value(
183+
"Document Content",
184+
&format!(
185+
"Given value {:?}, {:?}, {:?}",
186+
payload,
187+
metadata.content_type(),
188+
metadata.content_encoding()
189+
),
190+
&format!("{e:?}"),
191+
"Creating document content",
192+
);
193+
minicbor::decode::Error::message("Failed to create Document Content")
152194
})?;
153195

154196
Ok(InnerCatalystSignedDocument {
@@ -158,7 +200,11 @@ impl Decode<'_, ()> for CatalystSignedDocument {
158200
}
159201
.into())
160202
},
161-
_ => Err(minicbor::decode::Error::message(error::Error::from(errors))),
203+
_ => {
204+
Err(minicbor::decode::Error::message(
205+
"Failed to decode Catalyst Signed Document",
206+
))
207+
},
162208
}
163209
}
164210
}
@@ -238,8 +284,8 @@ mod tests {
238284

239285
let mut bytes = Vec::new();
240286
minicbor::encode_with(doc, &mut bytes, &mut ()).unwrap();
241-
let decoded: CatalystSignedDocument =
242-
minicbor::decode_with(bytes.as_slice(), &mut ()).unwrap();
287+
288+
let decoded: CatalystSignedDocument = bytes.as_slice().try_into().unwrap();
243289

244290
assert_eq!(decoded.doc_type(), uuid_v4);
245291
assert_eq!(decoded.doc_id(), uuid_v7);

rust/signed_doc/src/metadata/algorithm.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
//! Cryptographic Algorithm in COSE SIGN protected header.
22
3+
use strum::VariantArray;
4+
35
/// Cryptography Algorithm.
4-
#[derive(Copy, Clone, Debug, PartialEq, serde::Deserialize)]
6+
#[derive(Copy, Clone, Debug, PartialEq, serde::Deserialize, VariantArray)]
57
pub enum Algorithm {
68
/// `EdDSA`
79
EdDSA,
@@ -25,7 +27,12 @@ impl TryFrom<coset::iana::Algorithm> for Algorithm {
2527
fn try_from(value: coset::iana::Algorithm) -> Result<Self, Self::Error> {
2628
match value {
2729
coset::iana::Algorithm::EdDSA => Ok(Self::EdDSA),
28-
_ => anyhow::bail!("Unsupported algorithm: {value:?}"),
30+
_ => {
31+
anyhow::bail!(
32+
"Unsupported algorithm: {value:?}, Supported only: {:?}",
33+
Algorithm::VARIANTS
34+
)
35+
},
2936
}
3037
}
3138
}

rust/signed_doc/src/metadata/content_type.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ use std::{
77

88
use coset::iana::CoapContentFormat;
99
use serde::{de, Deserialize, Deserializer};
10+
use strum::VariantArray;
1011

1112
/// Payload Content Type.
12-
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
13+
#[derive(Debug, Copy, Clone, PartialEq, Eq, VariantArray)]
1314
pub enum ContentType {
1415
/// 'application/cbor'
1516
Cbor,
@@ -33,7 +34,12 @@ impl FromStr for ContentType {
3334
match s {
3435
"cbor" => Ok(Self::Cbor),
3536
"json" => Ok(Self::Json),
36-
_ => anyhow::bail!("Unsupported Content Type: {s:?}"),
37+
_ => {
38+
anyhow::bail!(
39+
"Unsupported Content Type: {s:?}, Supported only: {:?}",
40+
ContentType::VARIANTS
41+
)
42+
},
3743
}
3844
}
3945
}
@@ -62,7 +68,12 @@ impl TryFrom<&coset::ContentType> for ContentType {
6268
let content_type = match value {
6369
coset::ContentType::Assigned(CoapContentFormat::Json) => ContentType::Json,
6470
coset::ContentType::Assigned(CoapContentFormat::Cbor) => ContentType::Cbor,
65-
_ => anyhow::bail!("Unsupported Content Type {value:?}"),
71+
_ => {
72+
anyhow::bail!(
73+
"Unsupported Content Type {value:?}, Supported only: {:?}",
74+
ContentType::VARIANTS
75+
)
76+
},
6677
};
6778
Ok(content_type)
6879
}

0 commit comments

Comments
 (0)