diff --git a/rust/cbork-utils/Cargo.toml b/rust/cbork-utils/Cargo.toml index a32ad179b8..87bf9aced8 100644 --- a/rust/cbork-utils/Cargo.toml +++ b/rust/cbork-utils/Cargo.toml @@ -12,3 +12,9 @@ workspace = true [dependencies] minicbor = { version = "0.25.1", features = ["std"] } + +[dev-dependencies] +proptest = { version = "1.5.0" } +# Potentially it could be replaced with using `proptest::property_test` attribute macro, +# after this PR will be merged https://github.com/proptest-rs/proptest/pull/523 +test-strategy = "0.4.0" diff --git a/rust/cbork-utils/src/decode_helper.rs b/rust/cbork-utils/src/decode_helper.rs index 7ebf2d7afb..db7462de44 100644 --- a/rust/cbork-utils/src/decode_helper.rs +++ b/rust/cbork-utils/src/decode_helper.rs @@ -74,163 +74,105 @@ pub fn decode_tag(d: &mut Decoder, from: &str) -> Result { .map_err(|e| decode::Error::message(format!("Failed to decode tag in {from}: {e}"))) } -/// Decode any in CDDL, only support basic datatype +/// Decode any in CDDL (any CBOR type) and return its bytes. /// /// # Errors /// /// Error if the decoding fails. -pub fn decode_any(d: &mut Decoder, from: &str) -> Result, decode::Error> { - match d.datatype()? { - minicbor::data::Type::String => { - match decode_helper::(d, &format!("{from} Any"), &mut ()) { - Ok(i) => Ok(i.as_bytes().to_vec()), - Err(e) => Err(e), - } - }, - minicbor::data::Type::U8 => { - match decode_helper::(d, &format!("{from} Any"), &mut ()) { - Ok(i) => Ok(i.to_be_bytes().to_vec()), - Err(e) => Err(e), - } - }, - minicbor::data::Type::U16 => { - match decode_helper::(d, &format!("{from} Any"), &mut ()) { - Ok(i) => Ok(i.to_be_bytes().to_vec()), - Err(e) => Err(e), - } - }, - minicbor::data::Type::U32 => { - match decode_helper::(d, &format!("{from} Any"), &mut ()) { - Ok(i) => Ok(i.to_be_bytes().to_vec()), - Err(e) => Err(e), - } - }, - minicbor::data::Type::U64 => { - match decode_helper::(d, &format!("{from} Any"), &mut ()) { - Ok(i) => Ok(i.to_be_bytes().to_vec()), - Err(e) => Err(e), - } - }, - minicbor::data::Type::I8 => { - match decode_helper::(d, &format!("{from} Any"), &mut ()) { - Ok(i) => Ok(i.to_be_bytes().to_vec()), - Err(e) => Err(e), - } - }, - minicbor::data::Type::I16 => { - match decode_helper::(d, &format!("{from} Any"), &mut ()) { - Ok(i) => Ok(i.to_be_bytes().to_vec()), - Err(e) => Err(e), - } - }, - minicbor::data::Type::I32 => { - match decode_helper::(d, &format!("{from} Any"), &mut ()) { - Ok(i) => Ok(i.to_be_bytes().to_vec()), - Err(e) => Err(e), - } - }, - minicbor::data::Type::I64 => { - match decode_helper::(d, &format!("{from} Any"), &mut ()) { - Ok(i) => Ok(i.to_be_bytes().to_vec()), - Err(e) => Err(e), - } - }, - minicbor::data::Type::Bytes => Ok(decode_bytes(d, &format!("{from} Any"))?), - minicbor::data::Type::Array => { - Ok(decode_array_len(d, &format!("{from} Any"))? - .to_be_bytes() - .to_vec()) - }, - _ => { - Err(decode::Error::message(format!( - "{from} Any, Data type not supported" - ))) - }, - } +pub fn decode_any<'d>(d: &mut Decoder<'d>, from: &str) -> Result<&'d [u8], decode::Error> { + let start = d.position(); + d.skip()?; + let end = d.position(); + let bytes = d + .input() + .get(start..end) + .ok_or(decode::Error::message(format!( + "Failed to get any CBOR bytes in {from}. Invalid CBOR bytes." + )))?; + Ok(bytes) } #[cfg(test)] mod tests { - use minicbor::Encoder; + use test_strategy::proptest; use super::*; - #[test] - fn test_decode_any_bytes() { + #[proptest] + fn test_decode_any_bytes(random_bytes: Vec) { let mut buf = Vec::new(); let mut e = Encoder::new(&mut buf); - e.bytes(&[1, 2, 3, 4]).expect("Error encoding bytes"); + e.bytes(&random_bytes).expect("Error encoding bytes"); let mut d = Decoder::new(&buf); - let result = decode_any(&mut d, "test").expect("Error decoding bytes"); - assert_eq!(result, vec![1, 2, 3, 4]); + let cbor_bytes = decode_any(&mut d, "test").expect("Error decoding bytes"); + + let result = decode_bytes(&mut Decoder::new(cbor_bytes), "test").unwrap(); + assert_eq!(result, random_bytes); } - #[test] - fn test_decode_any_string() { + #[proptest] + fn test_decode_any_string(random_string: String) { let mut buf = Vec::new(); let mut e = Encoder::new(&mut buf); - e.str("hello").expect("Error encoding string"); + e.str(&random_string).expect("Error encoding string"); let mut d = Decoder::new(&buf); - let result = decode_any(&mut d, "test").expect("Error decoding string"); - assert_eq!(result, b"hello".to_vec()); + let cbor_bytes = decode_any(&mut d, "test").expect("Error decoding string"); + + let result = + decode_helper::(&mut Decoder::new(cbor_bytes), "test", &mut ()).unwrap(); + assert_eq!(result, random_string); } - #[test] - fn test_decode_any_array() { + #[proptest] + fn test_decode_any_array(random_array: Vec) { // The array should contain a supported type let mut buf = Vec::new(); let mut e = Encoder::new(&mut buf); - e.array(2).expect("Error encoding array"); - e.u8(1).expect("Error encoding u8"); - e.u8(2).expect("Error encoding u8"); + e.array(random_array.len() as u64) + .expect("Error encoding array"); + for el in &random_array { + e.u8(*el).expect("Error encoding u8"); + } let mut d = Decoder::new(&buf); - let result = decode_any(&mut d, "test").expect("Error decoding array"); + let cbor_bytes = decode_any(&mut d, "test").expect("Error decoding array"); // The decode of array is just a length of the array - assert_eq!( - u64::from_be_bytes(result.try_into().expect("Error converting bytes to u64")), - 2 - ); + let result = decode_array_len(&mut Decoder::new(cbor_bytes), "test").unwrap(); + assert_eq!(result, random_array.len() as u64); } - #[test] - fn test_decode_any_u32() { + #[proptest] + fn test_decode_any_u32(random_u32: u32) { let mut buf = Vec::new(); let mut e = Encoder::new(&mut buf); - let num: u32 = 123_456_789; - e.u32(num).expect("Error encoding u32"); + e.u32(random_u32).expect("Error encoding u32"); let mut d = Decoder::new(&buf); - let result = decode_any(&mut d, "test").expect("Error decoding u32"); - assert_eq!( - u32::from_be_bytes(result.try_into().expect("Error converting bytes to u32")), - num - ); + let cbor_bytes = decode_any(&mut d, "test").expect("Error decoding u32"); + + let result = + decode_helper::(&mut Decoder::new(cbor_bytes), "test", &mut ()).unwrap(); + assert_eq!(result, random_u32); } - #[test] - fn test_decode_any_i32() { + #[proptest] + fn test_decode_any_i32(random_i32: i32) { let mut buf = Vec::new(); let mut e = Encoder::new(&mut buf); - let num: i32 = -123_456_789; - e.i32(num).expect("Error encoding i32"); + e.i32(random_i32).expect("Error encoding i32"); let mut d = Decoder::new(&buf); - let result = decode_any(&mut d, "test").expect("Error decoding i32"); - assert_eq!( - i32::from_be_bytes(result.try_into().expect("Error converting bytes to i32")), - num - ); + let cbor_bytes = decode_any(&mut d, "test").expect("Error decoding i32"); + + let result = + decode_helper::(&mut Decoder::new(cbor_bytes), "test", &mut ()).unwrap(); + assert_eq!(result, random_i32); } #[test] - fn test_decode_any_unsupported_type() { - let mut buf = Vec::new(); - let mut e = Encoder::new(&mut buf); - e.null().expect("Error encoding null"); // Encode a null type which is unsupported - - let mut d = Decoder::new(&buf); + fn test_decode_any_not_cbor() { + let mut d = Decoder::new(&[]); let result = decode_any(&mut d, "test"); // Should print out the error message with the location of the error assert!(result.is_err());