diff --git a/Cargo.lock b/Cargo.lock index 7f776f128..1c76e6924 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -932,6 +932,12 @@ dependencies = [ "sha2", ] +[[package]] +name = "paste" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" + [[package]] name = "pbkdf2" version = "0.12.2" @@ -1189,6 +1195,28 @@ dependencies = [ "subtle", ] +[[package]] +name = "rmp" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44519172358fd6d58656c86ab8e7fbc9e1490c3e8f14d35ed78ca0dd07403c9f" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmp-serde" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5b13be192e0220b8afb7222aa5813cb62cc269ebb5cac346ca6487681d2913e" +dependencies = [ + "byteorder", + "rmp", + "serde", +] + [[package]] name = "rsa" version = "0.9.3" @@ -1388,6 +1416,7 @@ dependencies = [ "ciborium", "hex-literal 0.4.1", "proptest", + "rmp-serde", "serde", "serde-json-core", "serde_json", diff --git a/serdect/Cargo.toml b/serdect/Cargo.toml index 7ae62bd74..64e32cb26 100644 --- a/serdect/Cargo.toml +++ b/serdect/Cargo.toml @@ -26,6 +26,7 @@ bincode = "1" ciborium = "0.2" hex-literal = "0.4" proptest = "1" +rmp-serde = "1" serde = { version = "1.0.184", default-features = false, features = ["derive"] } serde_json = "1" serde-json-core = { version = "0.5", default-features = false, features = ["std"] } diff --git a/serdect/README.md b/serdect/README.md index 1983dfb4b..ac933c7b7 100644 --- a/serdect/README.md +++ b/serdect/README.md @@ -30,6 +30,15 @@ other kinds of data-dependent branching on the contents of the serialized data, using a constant-time hex serialization with human-readable formats should help reduce the overall timing variability. +`serdect` is tested against the following crates: +- [`bincode`](https://crates.io/crates/bincode) v1 +- [`ciborium`](https://crates.io/crates/ciborium) v0.2 +- [`rmp-serde`](https://crates.io/crates/rmp-serde) v1 +- [`serde-json-core`](https://crates.io/crates/serde-json-core) v0.5 +- [`serde-json`](https://crates.io/crates/serde-json) v1 +- [`toml`](https://crates.io/crates/toml) v0.7 + + ## Minimum Supported Rust Version Rust **1.60** or newer. diff --git a/serdect/src/array.rs b/serdect/src/array.rs index 68be6002b..551f05611 100644 --- a/serdect/src/array.rs +++ b/serdect/src/array.rs @@ -1,11 +1,21 @@ //! Serialization primitives for arrays. +// Unfortunately, we currently cannot tell `serde` in a uniform fashion that we are serializing +// a fixed-size byte array. +// See https://github.com/serde-rs/serde/issues/2120 for the discussion. +// Therefore we have to fall back to the slice methods, +// which will add the size information in the binary formats. +// The only difference is that for the arrays we require the size of the data +// to be exactly equal to the size of the buffer during deserialization, +// while for slices the buffer can be larger than the deserialized data. + use core::fmt; +use core::marker::PhantomData; -use serde::de::{Error, SeqAccess, Visitor}; -use serde::ser::SerializeTuple; use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use crate::common::{self, LengthCheck, SliceVisitor, StrIntoBufVisitor}; + #[cfg(feature = "zeroize")] use zeroize::Zeroize; @@ -16,17 +26,7 @@ where S: Serializer, T: AsRef<[u8]>, { - if serializer.is_human_readable() { - crate::serialize_hex::<_, _, false>(value, serializer) - } else { - let mut seq = serializer.serialize_tuple(value.as_ref().len())?; - - for byte in value.as_ref() { - seq.serialize_element(byte)?; - } - - seq.end() - } + common::serialize_hex_lower_or_bin(value, serializer) } /// Serialize the given type as upper case hex when using human-readable @@ -36,16 +36,21 @@ where S: Serializer, T: AsRef<[u8]>, { - if serializer.is_human_readable() { - crate::serialize_hex::<_, _, true>(value, serializer) - } else { - let mut seq = serializer.serialize_tuple(value.as_ref().len())?; + common::serialize_hex_upper_or_bin(value, serializer) +} - for byte in value.as_ref() { - seq.serialize_element(byte)?; - } +struct ExactLength; - seq.end() +impl LengthCheck for ExactLength { + fn length_check(buffer_length: usize, data_length: usize) -> bool { + buffer_length == data_length + } + fn expecting( + formatter: &mut fmt::Formatter<'_>, + data_type: &str, + data_length: usize, + ) -> fmt::Result { + write!(formatter, "{} of length {}", data_type, data_length) } } @@ -57,56 +62,9 @@ where D: Deserializer<'de>, { if deserializer.is_human_readable() { - struct StrVisitor<'b>(&'b mut [u8]); - - impl<'de> Visitor<'de> for StrVisitor<'_> { - type Value = (); - - fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(formatter, "a string of length {}", self.0.len() * 2) - } - - fn visit_str(self, v: &str) -> Result - where - E: Error, - { - if v.len() != self.0.len() * 2 { - return Err(Error::invalid_length(v.len(), &self)); - } - - base16ct::mixed::decode(v, self.0).map_err(E::custom)?; - - Ok(()) - } - } - - deserializer.deserialize_str(StrVisitor(buffer)) + deserializer.deserialize_str(StrIntoBufVisitor::(buffer, PhantomData)) } else { - struct ArrayVisitor<'b>(&'b mut [u8]); - - impl<'de> Visitor<'de> for ArrayVisitor<'_> { - type Value = (); - - fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(formatter, "an array of length {}", self.0.len()) - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: SeqAccess<'de>, - { - for (index, byte) in self.0.iter_mut().enumerate() { - *byte = match seq.next_element()? { - Some(byte) => byte, - None => return Err(Error::invalid_length(index, &self)), - }; - } - - Ok(()) - } - } - - deserializer.deserialize_tuple(buffer.len(), ArrayVisitor(buffer)) + deserializer.deserialize_byte_buf(SliceVisitor::(buffer, PhantomData)) } } diff --git a/serdect/src/common.rs b/serdect/src/common.rs new file mode 100644 index 000000000..52eb172d5 --- /dev/null +++ b/serdect/src/common.rs @@ -0,0 +1,181 @@ +use core::fmt; +use core::marker::PhantomData; + +use serde::{ + de::{Error, Visitor}, + Serializer, +}; + +#[cfg(feature = "alloc")] +use ::{alloc::vec::Vec, serde::Serialize}; + +#[cfg(not(feature = "alloc"))] +use serde::ser::Error as SerError; + +pub(crate) fn serialize_hex( + value: &T, + serializer: S, +) -> Result +where + S: Serializer, + T: AsRef<[u8]>, +{ + #[cfg(feature = "alloc")] + if UPPERCASE { + return base16ct::upper::encode_string(value.as_ref()).serialize(serializer); + } else { + return base16ct::lower::encode_string(value.as_ref()).serialize(serializer); + } + #[cfg(not(feature = "alloc"))] + { + let _ = value; + let _ = serializer; + return Err(S::Error::custom( + "serializer is human readable, which requires the `alloc` crate feature", + )); + } +} + +pub(crate) fn serialize_hex_lower_or_bin(value: &T, serializer: S) -> Result +where + S: Serializer, + T: AsRef<[u8]>, +{ + if serializer.is_human_readable() { + serialize_hex::<_, _, false>(value, serializer) + } else { + serializer.serialize_bytes(value.as_ref()) + } +} + +/// Serialize the given type as upper case hex when using human-readable +/// formats or binary if the format is binary. +pub(crate) fn serialize_hex_upper_or_bin(value: &T, serializer: S) -> Result +where + S: Serializer, + T: AsRef<[u8]>, +{ + if serializer.is_human_readable() { + serialize_hex::<_, _, true>(value, serializer) + } else { + serializer.serialize_bytes(value.as_ref()) + } +} + +pub(crate) trait LengthCheck { + fn length_check(buffer_length: usize, data_length: usize) -> bool; + fn expecting( + formatter: &mut fmt::Formatter<'_>, + data_type: &str, + data_length: usize, + ) -> fmt::Result; +} + +pub(crate) struct StrIntoBufVisitor<'b, T: LengthCheck>(pub &'b mut [u8], pub PhantomData); + +impl<'de, 'b, T: LengthCheck> Visitor<'de> for StrIntoBufVisitor<'b, T> { + type Value = (); + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + T::expecting(formatter, "a string", self.0.len() * 2) + } + + fn visit_str(self, v: &str) -> Result + where + E: Error, + { + if !T::length_check(self.0.len() * 2, v.len()) { + return Err(Error::invalid_length(v.len(), &self)); + } + // TODO: Map `base16ct::Error::InvalidLength` to `Error::invalid_length`. + base16ct::mixed::decode(v, self.0) + .map(|_| ()) + .map_err(E::custom) + } +} + +#[cfg(feature = "alloc")] +pub(crate) struct StrIntoVecVisitor; + +#[cfg(feature = "alloc")] +impl<'de> Visitor<'de> for StrIntoVecVisitor { + type Value = Vec; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(formatter, "a string") + } + + fn visit_str(self, v: &str) -> Result + where + E: Error, + { + base16ct::mixed::decode_vec(v).map_err(E::custom) + } +} + +pub(crate) struct SliceVisitor<'b, T: LengthCheck>(pub &'b mut [u8], pub PhantomData); + +impl<'de, 'b, T: LengthCheck> Visitor<'de> for SliceVisitor<'b, T> { + type Value = (); + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + T::expecting(formatter, "an array", self.0.len()) + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + E: Error, + { + // Workaround for + // https://github.com/rust-lang/rfcs/blob/b1de05846d9bc5591d753f611ab8ee84a01fa500/text/2094-nll.md#problem-case-3-conditional-control-flow-across-functions + if T::length_check(self.0.len(), v.len()) { + let buffer = &mut self.0[..v.len()]; + buffer.copy_from_slice(v); + return Ok(()); + } + + Err(E::invalid_length(v.len(), &self)) + } + + #[cfg(feature = "alloc")] + fn visit_byte_buf(self, mut v: Vec) -> Result + where + E: Error, + { + // Workaround for + // https://github.com/rust-lang/rfcs/blob/b1de05846d9bc5591d753f611ab8ee84a01fa500/text/2094-nll.md#problem-case-3-conditional-control-flow-across-functions + if T::length_check(self.0.len(), v.len()) { + let buffer = &mut self.0[..v.len()]; + buffer.swap_with_slice(&mut v); + return Ok(()); + } + + Err(E::invalid_length(v.len(), &self)) + } +} + +#[cfg(feature = "alloc")] +pub(crate) struct VecVisitor; + +#[cfg(feature = "alloc")] +impl<'de> Visitor<'de> for VecVisitor { + type Value = Vec; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(formatter, "a bytestring") + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + E: Error, + { + Ok(v.into()) + } + + fn visit_byte_buf(self, v: Vec) -> Result + where + E: Error, + { + Ok(v) + } +} diff --git a/serdect/src/lib.rs b/serdect/src/lib.rs index d7d130b1e..7bc289f68 100644 --- a/serdect/src/lib.rs +++ b/serdect/src/lib.rs @@ -49,8 +49,17 @@ //! let data = SecretData([42; 32]); //! //! let serialized = bincode::serialize(&data).unwrap(); -//! // bincode, a binary serialization format, is serialized into bytes. -//! assert_eq!(serialized.as_slice(), [42; 32]); +//! // bincode, a binary serialization format is serialized into bytes. +//! assert_eq!( +//! serialized.as_slice(), +//! [ +//! // Array size. +//! 32, 0, 0, 0, 0, 0, 0, 0, +//! // Actual data. +//! 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, +//! 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, +//! ] +//! ); //! # let deserialized: SecretData = bincode::deserialize(&serialized).unwrap(); //! # assert_eq!(deserialized, data); //! @@ -98,7 +107,7 @@ //! assert_eq!( //! serialized.as_slice(), //! [ -//! // Not fixed-size, so a size will be encoded. +//! // Slice size. //! 32, 0, 0, 0, 0, 0, 0, 0, //! // Actual data. //! 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, @@ -122,35 +131,7 @@ extern crate alloc; pub mod array; +mod common; pub mod slice; pub use serde; - -use serde::Serializer; - -#[cfg(not(feature = "alloc"))] -use serde::ser::Error; - -#[cfg(feature = "alloc")] -use serde::Serialize; - -fn serialize_hex(value: &T, serializer: S) -> Result -where - S: Serializer, - T: AsRef<[u8]>, -{ - #[cfg(feature = "alloc")] - if UPPERCASE { - return base16ct::upper::encode_string(value.as_ref()).serialize(serializer); - } else { - return base16ct::lower::encode_string(value.as_ref()).serialize(serializer); - } - #[cfg(not(feature = "alloc"))] - { - let _ = value; - let _ = serializer; - return Err(S::Error::custom( - "serializer is human readable, which requires the `alloc` crate feature", - )); - } -} diff --git a/serdect/src/slice.rs b/serdect/src/slice.rs index ee47062f0..9104d177a 100644 --- a/serdect/src/slice.rs +++ b/serdect/src/slice.rs @@ -1,12 +1,20 @@ //! Serialization primitives for slices. use core::fmt; +use core::marker::PhantomData; -use serde::de::{Error, Visitor}; -use serde::{Deserializer, Serialize, Serializer}; +use serde::{Deserializer, Serializer}; + +use crate::common::{self, LengthCheck, SliceVisitor, StrIntoBufVisitor}; + +#[cfg(feature = "alloc")] +use ::{ + alloc::vec::Vec, + serde::{Deserialize, Serialize}, +}; #[cfg(feature = "alloc")] -use ::{alloc::vec::Vec, serde::Deserialize}; +use crate::common::{StrIntoVecVisitor, VecVisitor}; #[cfg(feature = "zeroize")] use zeroize::Zeroize; @@ -18,11 +26,7 @@ where S: Serializer, T: AsRef<[u8]>, { - if serializer.is_human_readable() { - crate::serialize_hex::<_, _, false>(value, serializer) - } else { - value.as_ref().serialize(serializer) - } + common::serialize_hex_lower_or_bin(value, serializer) } /// Serialize the given type as upper case hex when using human-readable @@ -32,91 +36,39 @@ where S: Serializer, T: AsRef<[u8]>, { - if serializer.is_human_readable() { - crate::serialize_hex::<_, _, true>(value, serializer) - } else { - value.as_ref().serialize(serializer) + common::serialize_hex_upper_or_bin(value, serializer) +} + +struct UpperBound; + +impl LengthCheck for UpperBound { + fn length_check(buffer_length: usize, data_length: usize) -> bool { + buffer_length >= data_length + } + fn expecting( + formatter: &mut fmt::Formatter<'_>, + data_type: &str, + data_length: usize, + ) -> fmt::Result { + write!( + formatter, + "{} with a maximum length of {}", + data_type, data_length + ) } } /// Deserialize from hex when using human-readable formats or binary if the /// format is binary. Fails if the `buffer` is smaller then the resulting /// slice. -pub fn deserialize_hex_or_bin<'de, D>(buffer: &mut [u8], deserializer: D) -> Result<&[u8], D::Error> +pub fn deserialize_hex_or_bin<'de, D>(buffer: &mut [u8], deserializer: D) -> Result<(), D::Error> where D: Deserializer<'de>, { if deserializer.is_human_readable() { - struct StrVisitor<'b>(&'b mut [u8]); - - impl<'de, 'b> Visitor<'de> for StrVisitor<'b> { - type Value = &'b [u8]; - - fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - formatter, - "a string with a maximum length of {}", - self.0.len() * 2 - ) - } - - fn visit_str(self, v: &str) -> Result - where - E: Error, - { - // TODO: Map `base16ct::Error::InvalidLength` to `Error::invalid_length`. - base16ct::mixed::decode(v, self.0).map_err(E::custom) - } - } - - deserializer.deserialize_str(StrVisitor(buffer)) + deserializer.deserialize_str(StrIntoBufVisitor::(buffer, PhantomData)) } else { - struct SliceVisitor<'b>(&'b mut [u8]); - - impl<'de, 'b> Visitor<'de> for SliceVisitor<'b> { - type Value = &'b [u8]; - - fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - formatter, - "a slice with a maximum length of {}", - self.0.len() - ) - } - - fn visit_bytes(self, v: &[u8]) -> Result - where - E: Error, - { - // Workaround for - // https://github.com/rust-lang/rfcs/blob/b1de05846d9bc5591d753f611ab8ee84a01fa500/text/2094-nll.md#problem-case-3-conditional-control-flow-across-functions - if v.len() <= self.0.len() { - let buffer = &mut self.0[..v.len()]; - buffer.copy_from_slice(v); - return Ok(buffer); - } - - Err(E::invalid_length(v.len(), &self)) - } - - #[cfg(feature = "alloc")] - fn visit_byte_buf(self, mut v: Vec) -> Result - where - E: Error, - { - // Workaround for - // https://github.com/rust-lang/rfcs/blob/b1de05846d9bc5591d753f611ab8ee84a01fa500/text/2094-nll.md#problem-case-3-conditional-control-flow-across-functions - if v.len() <= self.0.len() { - let buffer = &mut self.0[..v.len()]; - buffer.swap_with_slice(&mut v); - return Ok(buffer); - } - - Err(E::invalid_length(v.len(), &self)) - } - } - - deserializer.deserialize_byte_buf(SliceVisitor(buffer)) + deserializer.deserialize_byte_buf(SliceVisitor::(buffer, PhantomData)) } } @@ -128,26 +80,9 @@ where D: Deserializer<'de>, { if deserializer.is_human_readable() { - struct StrVisitor; - - impl<'de> Visitor<'de> for StrVisitor { - type Value = Vec; - - fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(formatter, "a string") - } - - fn visit_str(self, v: &str) -> Result - where - E: Error, - { - base16ct::mixed::decode_vec(v).map_err(E::custom) - } - } - - deserializer.deserialize_str(StrVisitor) + deserializer.deserialize_str(StrIntoVecVisitor) } else { - Vec::deserialize(deserializer) + deserializer.deserialize_byte_buf(VecVisitor) } } diff --git a/serdect/tests/bincode.rs b/serdect/tests/bincode.rs index e4a39ccea..85c0289a7 100644 --- a/serdect/tests/bincode.rs +++ b/serdect/tests/bincode.rs @@ -7,13 +7,11 @@ use proptest::{array::*, collection::vec, prelude::*}; use serdect::{array, slice}; /// Example input to be serialized. -const EXAMPLE_BYTES: [u8; 16] = hex!("000102030405060708090A0B0C0D0E0F"); +/// Last byte is `0xFF` to test that no packing is performed for values under 128. +const EXAMPLE_BYTES: [u8; 16] = hex!("000102030405060708090A0B0C0D0EFF"); /// bincode serialization of [`EXAMPLE_BYTES`] as a slice. -const BINCODE_SLICE: [u8; 24] = hex!("1000000000000000000102030405060708090A0B0C0D0E0F"); - -/// bincode serialization of [`EXAMPLE_BYTES`] as an array. -const BINCODE_ARRAY: [u8; 16] = EXAMPLE_BYTES; +const BINCODE_SLICE: [u8; 24] = hex!("1000000000000000000102030405060708090A0B0C0D0EFF"); #[test] fn deserialize_slice() { @@ -30,14 +28,14 @@ fn deserialize_slice_owned() { #[test] fn deserialize_array() { - let deserialized = bincode::deserialize::>(&BINCODE_ARRAY).unwrap(); + let deserialized = bincode::deserialize::>(&BINCODE_SLICE).unwrap(); assert_eq!(deserialized.0, EXAMPLE_BYTES); } #[test] fn deserialize_array_owned() { let deserialized = - bincode::deserialize_from::<_, array::HexUpperOrBin<16>>(BINCODE_ARRAY.as_ref()).unwrap(); + bincode::deserialize_from::<_, array::HexUpperOrBin<16>>(BINCODE_SLICE.as_ref()).unwrap(); assert_eq!(deserialized.0, EXAMPLE_BYTES); } @@ -51,7 +49,7 @@ fn serialize_slice() { #[test] fn serialize_array() { let serialized = bincode::serialize(&array::HexUpperOrBin::from(EXAMPLE_BYTES)).unwrap(); - assert_eq!(&serialized, &BINCODE_ARRAY); + assert_eq!(&serialized, &BINCODE_SLICE); } proptest! { @@ -67,5 +65,7 @@ proptest! { let serialized = bincode::serialize(&array::HexUpperOrBin::from(bytes)).unwrap(); let deserialized = bincode::deserialize::>(&serialized).unwrap(); prop_assert_eq!(bytes, deserialized.0); + // 8 bytes for the length tag + 32 bytes of data + prop_assert_eq!(serialized.len(), 8 + 32); } } diff --git a/serdect/tests/cbor.rs b/serdect/tests/cbor.rs index 23bb0b693..b6d198d3e 100644 --- a/serdect/tests/cbor.rs +++ b/serdect/tests/cbor.rs @@ -9,13 +9,12 @@ use serde::Serialize; use serdect::{array, slice}; /// Example input to be serialized. -const EXAMPLE_BYTES: [u8; 16] = hex!("000102030405060708090A0B0C0D0E0F"); +/// Last byte is `0xFF` to test that no packing is performed for values under 128. +const EXAMPLE_BYTES: [u8; 16] = hex!("000102030405060708090A0B0C0D0EFF"); /// CBOR serialization of [`EXAMPLE_BYTES`] as a slice. -const CBOR_SLICE: [u8; 17] = hex!("90000102030405060708090A0B0C0D0E0F"); - -/// CBOR serialization of [`EXAMPLE_BYTES`] as an array. -const CBOR_ARRAY: [u8; 17] = CBOR_SLICE; +/// (first three bits, `0b010` denote a byte string, and the last five, `0b10000` denote the length) +const CBOR_SLICE: [u8; 17] = hex!("50000102030405060708090A0B0C0D0EFF"); #[test] fn deserialize_slice() { @@ -47,7 +46,7 @@ fn serialize_slice() { #[test] fn serialize_array() { let serialized = serialize(&array::HexUpperOrBin::from(EXAMPLE_BYTES)); - assert_eq!(&serialized, &CBOR_ARRAY); + assert_eq!(&serialized, &CBOR_SLICE); } proptest! { @@ -63,5 +62,7 @@ proptest! { let serialized = serialize(&array::HexUpperOrBin::from(bytes)); let deserialized = de::from_reader::, _>(serialized.as_slice()).unwrap(); prop_assert_eq!(bytes, deserialized.0); + // 1 byte slice tag + 1 byte length tag + 32 bytes of data + prop_assert_eq!(serialized.len(), 2 + 32); } } diff --git a/serdect/tests/messagepack.rs b/serdect/tests/messagepack.rs new file mode 100644 index 000000000..d55413fc0 --- /dev/null +++ b/serdect/tests/messagepack.rs @@ -0,0 +1,59 @@ +//! messagepack-specific tests. + +#![cfg(feature = "alloc")] + +use hex_literal::hex; +use proptest::{array::*, collection::vec, prelude::*}; +use serdect::{array, slice}; + +/// Example input to be serialized. +/// Last byte is `0xFF` to test that no packing is performed for values under 128. +const EXAMPLE_BYTES: [u8; 16] = hex!("000102030405060708090A0B0C0D0EFF"); + +/// messagepack serialization of [`EXAMPLE_BYTES`] as a slice. +const MESSAGEPACK_SLICE: [u8; 18] = hex!("C410000102030405060708090A0B0C0D0EFF"); + +#[test] +fn deserialize_slice() { + let deserialized = + rmp_serde::decode::from_slice::(&MESSAGEPACK_SLICE).unwrap(); + assert_eq!(deserialized.0, EXAMPLE_BYTES); +} + +#[test] +fn deserialize_array() { + let deserialized = + rmp_serde::decode::from_slice::>(&MESSAGEPACK_SLICE).unwrap(); + assert_eq!(deserialized.0, EXAMPLE_BYTES); +} + +#[test] +fn serialize_slice() { + let serialized = + rmp_serde::encode::to_vec(&slice::HexUpperOrBin::from(EXAMPLE_BYTES.as_ref())).unwrap(); + assert_eq!(&serialized, &MESSAGEPACK_SLICE); +} + +#[test] +fn serialize_array() { + let serialized = rmp_serde::encode::to_vec(&array::HexUpperOrBin::from(EXAMPLE_BYTES)).unwrap(); + assert_eq!(&serialized, &MESSAGEPACK_SLICE); +} + +proptest! { + #[test] + fn round_trip_slice(bytes in vec(any::(), 0..1024)) { + let serialized = rmp_serde::encode::to_vec(&slice::HexUpperOrBin::from(bytes.as_ref())).unwrap(); + let deserialized = rmp_serde::decode::from_slice::(&serialized).unwrap(); + prop_assert_eq!(bytes, deserialized.0); + } + + #[test] + fn round_trip_array(bytes in uniform32(0u8..)) { + let serialized = rmp_serde::encode::to_vec(&array::HexUpperOrBin::from(bytes)).unwrap(); + let deserialized = rmp_serde::decode::from_slice::>(&serialized).unwrap(); + prop_assert_eq!(bytes, deserialized.0); + // 1 byte slice tag + 1 byte length tag + 32 bytes of data + prop_assert_eq!(serialized.len(), 2 + 32); + } +} diff --git a/serdect/tests/serde-json-core.rs b/serdect/tests/serde-json-core.rs index 443573651..55eb1b596 100644 --- a/serdect/tests/serde-json-core.rs +++ b/serdect/tests/serde-json-core.rs @@ -9,13 +9,13 @@ use serde_json_core as json; use serdect::{array, slice}; /// Example input to be serialized. -const EXAMPLE_BYTES: [u8; 16] = hex!("000102030405060708090A0B0C0D0E0F"); +const EXAMPLE_BYTES: [u8; 16] = hex!("000102030405060708090A0B0C0D0EFF"); /// Lower-case hex serialization of [`EXAMPLE_BYTES`]. -const HEX_LOWER: &str = "\"000102030405060708090a0b0c0d0e0f\""; +const HEX_LOWER: &str = "\"000102030405060708090a0b0c0d0eff\""; /// Upper-case hex serialization of [`EXAMPLE_BYTES`]. -const HEX_UPPER: &str = "\"000102030405060708090A0B0C0D0E0F\""; +const HEX_UPPER: &str = "\"000102030405060708090A0B0C0D0EFF\""; fn serialize(value: &T) -> String where diff --git a/serdect/tests/serde_json.rs b/serdect/tests/serde_json.rs index 3ad43a95b..b7fc6b1d2 100644 --- a/serdect/tests/serde_json.rs +++ b/serdect/tests/serde_json.rs @@ -8,13 +8,13 @@ use serde_json as json; use serdect::{array, slice}; /// Example input to be serialized. -const EXAMPLE_BYTES: [u8; 16] = hex!("000102030405060708090A0B0C0D0E0F"); +const EXAMPLE_BYTES: [u8; 16] = hex!("000102030405060708090A0B0C0D0EFF"); /// Lower-case hex serialization of [`EXAMPLE_BYTES`]. -const HEX_LOWER: &str = "\"000102030405060708090a0b0c0d0e0f\""; +const HEX_LOWER: &str = "\"000102030405060708090a0b0c0d0eff\""; /// Upper-case hex serialization of [`EXAMPLE_BYTES`]. -const HEX_UPPER: &str = "\"000102030405060708090A0B0C0D0E0F\""; +const HEX_UPPER: &str = "\"000102030405060708090A0B0C0D0EFF\""; #[test] fn deserialize_slice() { diff --git a/serdect/tests/toml.rs b/serdect/tests/toml.rs index e74ca776f..8a815cb1a 100644 --- a/serdect/tests/toml.rs +++ b/serdect/tests/toml.rs @@ -8,13 +8,13 @@ use serde::{Deserialize, Serialize}; use serdect::{array, slice}; /// Example input to be serialized. -const EXAMPLE_BYTES: [u8; 16] = hex!("000102030405060708090A0B0C0D0E0F"); +const EXAMPLE_BYTES: [u8; 16] = hex!("000102030405060708090A0B0C0D0EFF"); /// Lower-case hex serialization of [`EXAMPLE_BYTES`]. -const HEX_LOWER: &str = "\"000102030405060708090a0b0c0d0e0f\""; +const HEX_LOWER: &str = "\"000102030405060708090a0b0c0d0eff\""; /// Upper-case hex serialization of [`EXAMPLE_BYTES`]. -const HEX_UPPER: &str = "\"000102030405060708090A0B0C0D0E0F\""; +const HEX_UPPER: &str = "\"000102030405060708090A0B0C0D0EFF\""; #[derive(Deserialize, Serialize)] struct SliceTest {