Skip to content

Commit 9fe1835

Browse files
committed
Merge branch 'fix/replace-ulid-with-uuidv7' of github.com:input-output-hk/catalyst-libs into fix/replace-ulid-with-uuidv7
2 parents 3f77f5e + 2a2aea1 commit 9fe1835

File tree

21 files changed

+836
-86
lines changed

21 files changed

+836
-86
lines changed

rust/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ members = [
88
"cbork",
99
"cbork-abnf-parser",
1010
"cbork-cddl-parser",
11+
"cbork-utils",
1112
"catalyst-voting",
1213
"catalyst-voting",
1314
"immutable-ledger",

rust/Earthfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ COPY_SRC:
1212
cardano-blockchain-types \
1313
cardano-chain-follower \
1414
catalyst-voting vote-tx-v1 vote-tx-v2 \
15-
cbork cbork-abnf-parser cbork-cddl-parser \
15+
cbork cbork-abnf-parser cbork-cddl-parser cbork-utils \
1616
hermes-ipfs \
1717
signed_doc \
1818
rbac-registration \
@@ -55,7 +55,7 @@ build:
5555
DO rust-ci+EXECUTE \
5656
--cmd="/scripts/std_build.py" \
5757
--args1="--libs=c509-certificate --libs=cardano-blockchain-types --libs=cardano-chain-follower --libs=hermes-ipfs" \
58-
--args2="--libs=cbork-cddl-parser --libs=cbork-abnf-parser" \
58+
--args2="--libs=cbork-cddl-parser --libs=cbork-abnf-parser --libs=cbork-utils" \
5959
--args3="--libs=catalyst-voting --libs=immutable-ledger --libs=vote-tx-v1 --libs=vote-tx-v2" \
6060
--args4="--bins=cbork/cbork --libs=rbac-registration --libs=signed_doc" \
6161
--args5="--cov_report=$HOME/build/coverage-report.info" \

rust/cbork-utils/Cargo.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "cbork-utils"
3+
version = "0.0.1"
4+
edition.workspace = true
5+
license.workspace = true
6+
authors.workspace = true
7+
homepage.workspace = true
8+
repository.workspace = true
9+
10+
[lints]
11+
workspace = true
12+
13+
[dependencies]
14+
minicbor = { version = "0.25.1", features = ["std"] }
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
//! CBOR decoding helper functions.
2+
3+
use minicbor::{data::Tag, decode, Decoder};
4+
5+
/// Generic helper function for decoding different types.
6+
///
7+
/// # Errors
8+
///
9+
/// Error if the decoding fails.
10+
pub fn decode_helper<'a, T, C>(
11+
d: &mut Decoder<'a>, from: &str, context: &mut C,
12+
) -> Result<T, decode::Error>
13+
where T: minicbor::Decode<'a, C> {
14+
T::decode(d, context).map_err(|e| {
15+
decode::Error::message(format!(
16+
"Failed to decode {:?} in {from}: {e}",
17+
std::any::type_name::<T>()
18+
))
19+
})
20+
}
21+
22+
/// Helper function for decoding bytes.
23+
///
24+
/// # Errors
25+
///
26+
/// Error if the decoding fails.
27+
pub fn decode_bytes(d: &mut Decoder, from: &str) -> Result<Vec<u8>, decode::Error> {
28+
d.bytes().map(<[u8]>::to_vec).map_err(|e| {
29+
decode::Error::message(format!(
30+
"Failed to decode bytes in {from}:
31+
{e}"
32+
))
33+
})
34+
}
35+
36+
/// Helper function for decoding array.
37+
///
38+
/// # Errors
39+
///
40+
/// Error if the decoding fails.
41+
pub fn decode_array_len(d: &mut Decoder, from: &str) -> Result<u64, decode::Error> {
42+
d.array()
43+
.map_err(|e| {
44+
decode::Error::message(format!(
45+
"Failed to decode array in {from}:
46+
{e}"
47+
))
48+
})?
49+
.ok_or(decode::Error::message(format!(
50+
"Failed to decode array in {from}, unexpected indefinite length",
51+
)))
52+
}
53+
54+
/// Helper function for decoding map.
55+
///
56+
/// # Errors
57+
///
58+
/// Error if the decoding fails.
59+
pub fn decode_map_len(d: &mut Decoder, from: &str) -> Result<u64, decode::Error> {
60+
d.map()
61+
.map_err(|e| decode::Error::message(format!("Failed to decode map in {from}: {e}")))?
62+
.ok_or(decode::Error::message(format!(
63+
"Failed to decode map in {from}, unexpected indefinite length",
64+
)))
65+
}
66+
67+
/// Helper function for decoding tag.
68+
///
69+
/// # Errors
70+
///
71+
/// Error if the decoding fails.
72+
pub fn decode_tag(d: &mut Decoder, from: &str) -> Result<Tag, decode::Error> {
73+
d.tag()
74+
.map_err(|e| decode::Error::message(format!("Failed to decode tag in {from}: {e}")))
75+
}
76+
77+
/// Decode any in CDDL, only support basic datatype
78+
///
79+
/// # Errors
80+
///
81+
/// Error if the decoding fails.
82+
pub fn decode_any(d: &mut Decoder, from: &str) -> Result<Vec<u8>, decode::Error> {
83+
match d.datatype()? {
84+
minicbor::data::Type::String => {
85+
match decode_helper::<String, _>(d, &format!("{from} Any"), &mut ()) {
86+
Ok(i) => Ok(i.as_bytes().to_vec()),
87+
Err(e) => Err(e),
88+
}
89+
},
90+
minicbor::data::Type::U8 => {
91+
match decode_helper::<u8, _>(d, &format!("{from} Any"), &mut ()) {
92+
Ok(i) => Ok(i.to_be_bytes().to_vec()),
93+
Err(e) => Err(e),
94+
}
95+
},
96+
minicbor::data::Type::U16 => {
97+
match decode_helper::<u16, _>(d, &format!("{from} Any"), &mut ()) {
98+
Ok(i) => Ok(i.to_be_bytes().to_vec()),
99+
Err(e) => Err(e),
100+
}
101+
},
102+
minicbor::data::Type::U32 => {
103+
match decode_helper::<u32, _>(d, &format!("{from} Any"), &mut ()) {
104+
Ok(i) => Ok(i.to_be_bytes().to_vec()),
105+
Err(e) => Err(e),
106+
}
107+
},
108+
minicbor::data::Type::U64 => {
109+
match decode_helper::<u64, _>(d, &format!("{from} Any"), &mut ()) {
110+
Ok(i) => Ok(i.to_be_bytes().to_vec()),
111+
Err(e) => Err(e),
112+
}
113+
},
114+
minicbor::data::Type::I8 => {
115+
match decode_helper::<i8, _>(d, &format!("{from} Any"), &mut ()) {
116+
Ok(i) => Ok(i.to_be_bytes().to_vec()),
117+
Err(e) => Err(e),
118+
}
119+
},
120+
minicbor::data::Type::I16 => {
121+
match decode_helper::<i16, _>(d, &format!("{from} Any"), &mut ()) {
122+
Ok(i) => Ok(i.to_be_bytes().to_vec()),
123+
Err(e) => Err(e),
124+
}
125+
},
126+
minicbor::data::Type::I32 => {
127+
match decode_helper::<i32, _>(d, &format!("{from} Any"), &mut ()) {
128+
Ok(i) => Ok(i.to_be_bytes().to_vec()),
129+
Err(e) => Err(e),
130+
}
131+
},
132+
minicbor::data::Type::I64 => {
133+
match decode_helper::<i64, _>(d, &format!("{from} Any"), &mut ()) {
134+
Ok(i) => Ok(i.to_be_bytes().to_vec()),
135+
Err(e) => Err(e),
136+
}
137+
},
138+
minicbor::data::Type::Bytes => Ok(decode_bytes(d, &format!("{from} Any"))?),
139+
minicbor::data::Type::Array => {
140+
Ok(decode_array_len(d, &format!("{from} Any"))?
141+
.to_be_bytes()
142+
.to_vec())
143+
},
144+
_ => {
145+
Err(decode::Error::message(format!(
146+
"{from} Any, Data type not supported"
147+
)))
148+
},
149+
}
150+
}
151+
152+
#[cfg(test)]
153+
mod tests {
154+
155+
use minicbor::Encoder;
156+
157+
use super::*;
158+
159+
#[test]
160+
fn test_decode_any_bytes() {
161+
let mut buf = Vec::new();
162+
let mut e = Encoder::new(&mut buf);
163+
e.bytes(&[1, 2, 3, 4]).expect("Error encoding bytes");
164+
165+
let mut d = Decoder::new(&buf);
166+
let result = decode_any(&mut d, "test").expect("Error decoding bytes");
167+
assert_eq!(result, vec![1, 2, 3, 4]);
168+
}
169+
170+
#[test]
171+
fn test_decode_any_string() {
172+
let mut buf = Vec::new();
173+
let mut e = Encoder::new(&mut buf);
174+
e.str("hello").expect("Error encoding string");
175+
176+
let mut d = Decoder::new(&buf);
177+
let result = decode_any(&mut d, "test").expect("Error decoding string");
178+
assert_eq!(result, b"hello".to_vec());
179+
}
180+
181+
#[test]
182+
fn test_decode_any_array() {
183+
// The array should contain a supported type
184+
let mut buf = Vec::new();
185+
let mut e = Encoder::new(&mut buf);
186+
e.array(2).expect("Error encoding array");
187+
e.u8(1).expect("Error encoding u8");
188+
e.u8(2).expect("Error encoding u8");
189+
let mut d = Decoder::new(&buf);
190+
let result = decode_any(&mut d, "test").expect("Error decoding array");
191+
// The decode of array is just a length of the array
192+
assert_eq!(
193+
u64::from_be_bytes(result.try_into().expect("Error converting bytes to u64")),
194+
2
195+
);
196+
}
197+
198+
#[test]
199+
fn test_decode_any_u32() {
200+
let mut buf = Vec::new();
201+
let mut e = Encoder::new(&mut buf);
202+
let num: u32 = 123_456_789;
203+
e.u32(num).expect("Error encoding u32");
204+
205+
let mut d = Decoder::new(&buf);
206+
let result = decode_any(&mut d, "test").expect("Error decoding u32");
207+
assert_eq!(
208+
u32::from_be_bytes(result.try_into().expect("Error converting bytes to u32")),
209+
num
210+
);
211+
}
212+
213+
#[test]
214+
fn test_decode_any_i32() {
215+
let mut buf = Vec::new();
216+
let mut e = Encoder::new(&mut buf);
217+
let num: i32 = -123_456_789;
218+
e.i32(num).expect("Error encoding i32");
219+
let mut d = Decoder::new(&buf);
220+
let result = decode_any(&mut d, "test").expect("Error decoding i32");
221+
assert_eq!(
222+
i32::from_be_bytes(result.try_into().expect("Error converting bytes to i32")),
223+
num
224+
);
225+
}
226+
227+
#[test]
228+
fn test_decode_any_unsupported_type() {
229+
let mut buf = Vec::new();
230+
let mut e = Encoder::new(&mut buf);
231+
e.null().expect("Error encoding null"); // Encode a null type which is unsupported
232+
233+
let mut d = Decoder::new(&buf);
234+
let result = decode_any(&mut d, "test");
235+
// Should print out the error message with the location of the error
236+
assert!(result.is_err());
237+
}
238+
}

rust/cbork-utils/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
//! CBOR utility modules.
2+
3+
pub mod decode_helper;

rust/signed_doc/examples/mk_signed_doc.rs

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use ed25519_dalek::{
1414
ed25519::signature::Signer,
1515
pkcs8::{DecodePrivateKey, DecodePublicKey},
1616
};
17-
use signed_doc::{DocumentRef, Metadata, UuidV7};
17+
use signed_doc::{DocumentRef, Kid, Metadata, UuidV7};
1818

1919
fn main() {
2020
if let Err(err) = Cli::parse().exec() {
@@ -57,7 +57,7 @@ enum Cli {
5757
},
5858
}
5959

60-
const CONTENT_ENCODING_KEY: &str = "content encoding";
60+
const CONTENT_ENCODING_KEY: &str = "Content-Encoding";
6161
const CONTENT_ENCODING_VALUE: &str = "br";
6262
const UUID_CBOR_TAG: u64 = 37;
6363

@@ -119,7 +119,8 @@ impl Cli {
119119
} => {
120120
let doc_schema = load_schema_from_file(&schema)?;
121121
let json_doc = load_json_from_file(&doc)?;
122-
let json_meta = load_json_from_file(&meta)?;
122+
let json_meta = load_json_from_file(&meta)
123+
.map_err(|e| anyhow::anyhow!("Failed to load metadata from file: {e}"))?;
123124
validate_json(&json_doc, &doc_schema)?;
124125
let compressed_doc = brotli_compress_json(&json_doc)?;
125126
let empty_cose_sign = build_empty_cose_doc(compressed_doc, &json_meta);
@@ -132,9 +133,13 @@ impl Cli {
132133
store_cose_file(cose, &doc)?;
133134
},
134135
Self::Verify { pk, doc, schema } => {
135-
let pk = load_public_key_from_file(&pk)?;
136-
let schema = load_schema_from_file(&schema)?;
137-
let cose = load_cose_from_file(&doc)?;
136+
let pk = load_public_key_from_file(&pk)
137+
.map_err(|e| anyhow::anyhow!("Failed to load public key from file: {e}"))?;
138+
let schema = load_schema_from_file(&schema).map_err(|e| {
139+
anyhow::anyhow!("Failed to load document schema from file: {e}")
140+
})?;
141+
let cose = load_cose_from_file(&doc)
142+
.map_err(|e| anyhow::anyhow!("Failed to load COSE SIGN from file: {e}"))?;
138143
validate_cose(&cose, &pk, &schema)?;
139144
println!("Document is valid.");
140145
},
@@ -294,11 +299,14 @@ fn validate_cose(
294299
validate_json(&json_doc, schema)?;
295300

296301
for sign in &cose.signatures {
302+
let key_id = &sign.protected.header.key_id;
297303
anyhow::ensure!(
298-
!sign.protected.header.key_id.is_empty(),
304+
!key_id.is_empty(),
299305
"COSE missing signature protected header `kid` field "
300306
);
301307

308+
let kid = Kid::try_from(key_id.as_ref())?;
309+
println!("Signature Key ID: {kid}");
302310
let data_to_sign = cose.tbs_data(&[], sign);
303311
let signature_bytes = sign.signature.as_slice().try_into().map_err(|_| {
304312
anyhow::anyhow!(
@@ -307,6 +315,11 @@ fn validate_cose(
307315
sign.signature.len()
308316
)
309317
})?;
318+
println!(
319+
"Verifying Key Len({}): 0x{}",
320+
pk.as_bytes().len(),
321+
hex::encode(pk.as_bytes())
322+
);
310323
let signature = ed25519_dalek::Signature::from_bytes(signature_bytes);
311324
pk.verify_strict(&data_to_sign, &signature)?;
312325
}
@@ -324,12 +337,13 @@ fn validate_cose_protected_header(cose: &coset::CoseSign) -> anyhow::Result<()>
324337
cose.protected.header.content_type == expected_header.content_type,
325338
"Invalid COSE document protected header `content-type` field"
326339
);
340+
println!("HEADER REST: \n{:?}", cose.protected.header.rest);
327341
anyhow::ensure!(
328342
cose.protected.header.rest.iter().any(|(key, value)| {
329343
key == &coset::Label::Text(CONTENT_ENCODING_KEY.to_string())
330344
&& value == &coset::cbor::Value::Text(CONTENT_ENCODING_VALUE.to_string())
331345
}),
332-
"Invalid COSE document protected header {CONTENT_ENCODING_KEY} field"
346+
"Invalid COSE document protected header"
333347
);
334348

335349
let Some((_, value)) = cose

0 commit comments

Comments
 (0)