Skip to content

Commit 2d4d633

Browse files
authored
feat(rust/signed-doc): Apply deterministic CBOR decoding from cbork-utils (#418)
* initial * chore: minor name * chore: lintfix * fix: document refs 2 elements * fix: spell * chore: error message
1 parent a8b219d commit 2d4d633

File tree

8 files changed

+256
-197
lines changed

8 files changed

+256
-197
lines changed

rust/signed_doc/src/lib.rs

Lines changed: 50 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pub use catalyst_types::{
2020
problem_report::ProblemReport,
2121
uuid::{Uuid, UuidV4, UuidV7},
2222
};
23-
use cbork_utils::with_cbor_bytes::WithCborBytes;
23+
use cbork_utils::{array::Array, decode_context::DecodeCtx, with_cbor_bytes::WithCborBytes};
2424
pub use content::Content;
2525
use decode_context::{CompatibilityPolicy, DecodeContext};
2626
pub use metadata::{
@@ -226,42 +226,55 @@ impl Decode<'_, CompatibilityPolicy> for CatalystSignedDocument {
226226
d.set_position(p);
227227
}
228228

229-
if !matches!(d.array()?, Some(4)) {
230-
return Err(minicbor::decode::Error::message(
231-
"Must be a definite size array of 4 elements",
232-
));
233-
}
234-
235-
let metadata_bytes = d.bytes()?;
236-
let metadata = WithCborBytes::<Metadata>::decode(
237-
&mut minicbor::Decoder::new(metadata_bytes),
238-
&mut ctx,
239-
)?;
240-
241-
// empty unprotected headers
242-
let mut map = cbork_utils::map::Map::decode(
243-
d,
244-
&mut cbork_utils::decode_context::DecodeCtx::Deterministic,
245-
)?
246-
.into_iter();
247-
if map.next().is_some() {
248-
ctx.report().unknown_field(
249-
"unprotected headers",
250-
"non empty unprotected headers",
251-
"COSE unprotected headers must be empty",
252-
);
253-
}
254-
255-
let content = WithCborBytes::<Content>::decode(d, &mut ())?;
256-
let signatures = Signatures::decode(d, &mut ctx)?;
257-
258-
Ok(InnerCatalystSignedDocument {
259-
metadata,
260-
content,
261-
signatures,
262-
report: ctx.into_report(),
263-
}
264-
.into())
229+
let arr = Array::decode(d, &mut DecodeCtx::Deterministic)?;
230+
231+
let signed_doc = match arr.as_slice() {
232+
[metadata_bytes, headers_bytes, content_bytes, signatures_bytes] => {
233+
let metadata_bytes = minicbor::Decoder::new(metadata_bytes).bytes()?;
234+
let metadata = WithCborBytes::<Metadata>::decode(
235+
&mut minicbor::Decoder::new(metadata_bytes),
236+
&mut ctx,
237+
)?;
238+
239+
// empty unprotected headers
240+
let mut map = cbork_utils::map::Map::decode(
241+
&mut minicbor::Decoder::new(headers_bytes.as_slice()),
242+
&mut cbork_utils::decode_context::DecodeCtx::Deterministic,
243+
)?
244+
.into_iter();
245+
if map.next().is_some() {
246+
ctx.report().unknown_field(
247+
"unprotected headers",
248+
"non empty unprotected headers",
249+
"COSE unprotected headers must be empty",
250+
);
251+
}
252+
253+
let content = WithCborBytes::<Content>::decode(
254+
&mut minicbor::Decoder::new(content_bytes.as_slice()),
255+
&mut (),
256+
)?;
257+
258+
let signatures = Signatures::decode(
259+
&mut minicbor::Decoder::new(signatures_bytes.as_slice()),
260+
&mut ctx,
261+
)?;
262+
263+
InnerCatalystSignedDocument {
264+
metadata,
265+
content,
266+
signatures,
267+
report: ctx.into_report(),
268+
}
269+
},
270+
_ => {
271+
return Err(minicbor::decode::Error::message(
272+
"Must be a definite size array of 4 elements",
273+
));
274+
},
275+
};
276+
277+
Ok(signed_doc.into())
265278
}
266279
}
267280

rust/signed_doc/src/metadata/collaborators.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use std::{ops::Deref, str::FromStr};
44

55
use catalyst_types::catalyst_id::CatalystId;
6+
use cbork_utils::{array::Array, decode_context::DecodeCtx};
67

78
/// 'collaborators' field type definition, which is a JSON path string
89
#[derive(Clone, Debug, PartialEq)]
@@ -39,14 +40,9 @@ impl minicbor::Decode<'_, ()> for Collaborators {
3940
fn decode(
4041
d: &mut minicbor::Decoder<'_>, _ctx: &mut (),
4142
) -> Result<Self, minicbor::decode::Error> {
42-
let Some(items) = d.array()? else {
43-
return Err(minicbor::decode::Error::message(
44-
"Must a definite size array",
45-
));
46-
};
47-
48-
(0..items)
49-
.map(|_| d.bytes())
43+
Array::decode(d, &mut DecodeCtx::Deterministic)?
44+
.iter()
45+
.map(|item| minicbor::Decoder::new(item).bytes())
5046
.collect::<Result<Vec<_>, _>>()?
5147
.into_iter()
5248
.map(CatalystId::try_from)

rust/signed_doc/src/metadata/doc_type.rs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::{
66
};
77

88
use catalyst_types::uuid::{CborContext, Uuid, UuidV4};
9+
use cbork_utils::{array::Array, decode_context::DecodeCtx};
910
use minicbor::{Decode, Decoder, Encode};
1011
use serde::{Deserialize, Deserializer};
1112
use tracing::warn;
@@ -149,25 +150,22 @@ impl Decode<'_, CompatibilityPolicy> for DocType {
149150
d: &mut Decoder, policy: &mut CompatibilityPolicy,
150151
) -> Result<Self, minicbor::decode::Error> {
151152
const CONTEXT: &str = "DocType decoding";
152-
let parse_uuid = |d: &mut Decoder| UuidV4::decode(d, &mut CborContext::Tagged);
153153

154154
match d.datatype()? {
155155
minicbor::data::Type::Array => {
156-
let len = d.array()?.ok_or_else(|| {
157-
minicbor::decode::Error::message(format!(
158-
"{CONTEXT}: Unable to decode array length"
159-
))
160-
})?;
156+
let arr = Array::decode(d, &mut DecodeCtx::Deterministic)?;
161157

162-
if len == 0 {
158+
if arr.is_empty() {
163159
return Err(minicbor::decode::Error::message(format!(
164160
"{CONTEXT}: empty array"
165161
)));
166162
}
167163

168-
(0..len)
169-
.map(|_| parse_uuid(d))
170-
.collect::<Result<Vec<_>, _>>()
164+
arr.into_iter()
165+
.map(|uuid| {
166+
UuidV4::decode(&mut minicbor::Decoder::new(&uuid), &mut CborContext::Tagged)
167+
})
168+
.collect::<Result<_, _>>()
171169
.map(Self)
172170
.map_err(|e| {
173171
minicbor::decode::Error::message(format!(
@@ -183,7 +181,7 @@ impl Decode<'_, CompatibilityPolicy> for DocType {
183181
warn!("{CONTEXT}: Conversion of document type single UUID to type DocType");
184182
}
185183

186-
let uuid = parse_uuid(d).map_err(|e| {
184+
let uuid = UuidV4::decode(d, &mut CborContext::Tagged).map_err(|e| {
187185
minicbor::decode::Error::message(format!(
188186
"{CONTEXT}: Cannot decode single UUIDv4: {e}"
189187
))

rust/signed_doc/src/metadata/document_refs/doc_locator.rs

Lines changed: 40 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
55
use std::fmt::Display;
66

7+
use cbork_utils::{decode_context::DecodeCtx, map::Map};
78
use minicbor::{Decode, Decoder, Encode};
89

910
/// CBOR tag of IPLD content identifiers (CIDs).
@@ -50,42 +51,46 @@ impl Decode<'_, ()> for DocLocator {
5051
fn decode(d: &mut Decoder, _ctx: &mut ()) -> Result<Self, minicbor::decode::Error> {
5152
const CONTEXT: &str = "DocLocator decoding";
5253

53-
let len = d.map()?.ok_or_else(|| {
54-
minicbor::decode::Error::message(format!("{CONTEXT}: expected valid map length"))
55-
})?;
56-
57-
if len != DOC_LOC_MAP_ITEM {
58-
return Err(minicbor::decode::Error::message(format!(
59-
"{CONTEXT}: expected map length {DOC_LOC_MAP_ITEM}, found {len}"
60-
)));
54+
let entries = Map::decode(d, &mut DecodeCtx::Deterministic)?;
55+
56+
match entries.as_slice() {
57+
[entry] => {
58+
let key = minicbor::Decoder::new(&entry.key_bytes)
59+
.str()
60+
.map_err(|e| e.with_message(format!("{CONTEXT}: expected string")))?;
61+
62+
if key != "cid" {
63+
return Err(minicbor::decode::Error::message(format!(
64+
"{CONTEXT}: expected key 'cid', found '{key}'"
65+
)));
66+
}
67+
68+
let mut value_decoder = minicbor::Decoder::new(&entry.value);
69+
70+
let tag = value_decoder
71+
.tag()
72+
.map_err(|e| e.with_message(format!("{CONTEXT}: expected tag")))?;
73+
74+
if tag.as_u64() != CID_TAG {
75+
return Err(minicbor::decode::Error::message(format!(
76+
"{CONTEXT}: expected tag {CID_TAG}, found {tag}",
77+
)));
78+
}
79+
80+
// No length limit
81+
let cid_bytes = value_decoder
82+
.bytes()
83+
.map_err(|e| e.with_message(format!("{CONTEXT}: expected bytes")))?;
84+
85+
Ok(DocLocator(cid_bytes.to_vec()))
86+
},
87+
_ => {
88+
Err(minicbor::decode::Error::message(format!(
89+
"{CONTEXT}: expected map length {DOC_LOC_MAP_ITEM}, found {}",
90+
entries.len()
91+
)))
92+
},
6193
}
62-
63-
let key = d
64-
.str()
65-
.map_err(|e| e.with_message(format!("{CONTEXT}: expected string")))?;
66-
67-
if key != "cid" {
68-
return Err(minicbor::decode::Error::message(format!(
69-
"{CONTEXT}: expected key 'cid', found '{key}'"
70-
)));
71-
}
72-
73-
let tag = d
74-
.tag()
75-
.map_err(|e| e.with_message(format!("{CONTEXT}: expected tag")))?;
76-
77-
if tag.as_u64() != CID_TAG {
78-
return Err(minicbor::decode::Error::message(format!(
79-
"{CONTEXT}: expected tag {CID_TAG}, found {tag}",
80-
)));
81-
}
82-
83-
// No length limit
84-
let cid_bytes = d
85-
.bytes()
86-
.map_err(|e| e.with_message(format!("{CONTEXT}: expected bytes")))?;
87-
88-
Ok(DocLocator(cid_bytes.to_vec()))
8994
}
9095
}
9196

rust/signed_doc/src/metadata/document_refs/doc_ref.rs

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
use std::fmt::Display;
44

55
use catalyst_types::uuid::{CborContext, UuidV7};
6-
use minicbor::{Decode, Decoder, Encode};
6+
use cbork_utils::{array::Array, decode_context::DecodeCtx};
7+
use minicbor::{Decode, Encode};
78

89
use super::doc_locator::DocLocator;
910

@@ -66,29 +67,43 @@ impl Decode<'_, ()> for DocumentRef {
6667
d: &mut minicbor::Decoder<'_>, _ctx: &mut (),
6768
) -> Result<Self, minicbor::decode::Error> {
6869
const CONTEXT: &str = "DocumentRef decoding";
69-
let parse_uuid = |d: &mut Decoder| UuidV7::decode(d, &mut CborContext::Tagged);
70-
71-
let arr = d.array()?.ok_or_else(|| {
72-
minicbor::decode::Error::message(format!("{CONTEXT}: Unable to decode array length"))
73-
})?;
74-
if arr != DOC_REF_ARR_ITEM {
75-
return Err(minicbor::decode::Error::message(format!(
76-
"{CONTEXT}: expected {DOC_REF_ARR_ITEM} items, found {arr}"
77-
)));
78-
}
79-
let id = parse_uuid(d).map_err(|e| e.with_message("Invalid ID UUIDv7"))?;
80-
81-
let ver = parse_uuid(d).map_err(|e| e.with_message("Invalid Ver UUIDv7"))?;
82-
83-
let locator = d
84-
.decode::<DocLocator>()
85-
.map_err(|e| e.with_message("Failed to decode locator"))?;
8670

87-
Ok(DocumentRef {
88-
id,
89-
ver,
90-
doc_locator: locator,
91-
})
71+
let arr = Array::decode(d, &mut DecodeCtx::Deterministic)
72+
.map_err(|e| minicbor::decode::Error::message(format!("{CONTEXT}: {e}")))?;
73+
74+
let doc_ref = match arr.as_slice() {
75+
[id_bytes, ver_bytes, locator_bytes] => {
76+
let id = UuidV7::decode(
77+
&mut minicbor::Decoder::new(id_bytes.as_slice()),
78+
&mut CborContext::Tagged,
79+
)
80+
.map_err(|e| e.with_message("Invalid ID UUIDv7"))?;
81+
82+
let ver = UuidV7::decode(
83+
&mut minicbor::Decoder::new(ver_bytes.as_slice()),
84+
&mut CborContext::Tagged,
85+
)
86+
.map_err(|e| e.with_message("Invalid Ver UUIDv7"))?;
87+
88+
let doc_locator = minicbor::Decoder::new(locator_bytes.as_slice())
89+
.decode()
90+
.map_err(|e| e.with_message("Failed to decode locator"))?;
91+
92+
DocumentRef {
93+
id,
94+
ver,
95+
doc_locator,
96+
}
97+
},
98+
_ => {
99+
return Err(minicbor::decode::Error::message(format!(
100+
"{CONTEXT}: expected {DOC_REF_ARR_ITEM} items, found {}",
101+
arr.len()
102+
)));
103+
},
104+
};
105+
106+
Ok(doc_ref)
92107
}
93108
}
94109

0 commit comments

Comments
 (0)