From 1c385e4710058d779b615ae9894bd8f944ca6f8e Mon Sep 17 00:00:00 2001 From: Guillaume Potier Date: Fri, 21 Jan 2022 11:45:17 +0100 Subject: [PATCH] Fix encoding size constraints for BigInt and BigUint not enforced (#1367) Co-authored-by: David Himmelstrup --- Cargo.lock | 1 + utils/bigint/Cargo.toml | 6 +++ utils/bigint/src/bigint_ser.rs | 67 ++++++++++++++++++++++++++++++++ utils/bigint/src/biguint_ser.rs | 69 +++++++++++++++++++++++++++++++++ utils/bigint/src/lib.rs | 4 ++ 5 files changed, 147 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 7b96883a81f..200a6a3ab3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2357,6 +2357,7 @@ name = "forest_bigint" version = "0.1.4" dependencies = [ "cs_serde_bytes", + "cs_serde_cbor", "num-bigint 0.3.3", "num-integer", "serde", diff --git a/utils/bigint/Cargo.toml b/utils/bigint/Cargo.toml index 5844dd3fa66..9251e064142 100644 --- a/utils/bigint/Cargo.toml +++ b/utils/bigint/Cargo.toml @@ -16,5 +16,11 @@ serde = { version = "1.0", features = ["derive"] } serde_bytes = { package = "cs_serde_bytes", version = "0.12" } num-integer = "0.1" +[dev-dependencies] +# TODO remove fork in future (allowing non utf8 strings to be cbor deserialized) +serde_cbor = { package = "cs_serde_cbor", version = "0.12", features = [ + "tags" +] } + [features] json = [] diff --git a/utils/bigint/src/bigint_ser.rs b/utils/bigint/src/bigint_ser.rs index bf9b872f621..7ebdf244e01 100644 --- a/utils/bigint/src/bigint_ser.rs +++ b/utils/bigint/src/bigint_ser.rs @@ -1,6 +1,7 @@ // Copyright 2019-2022 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT +use crate::MAX_ENCODED_SIZE; use num_bigint::{BigInt, Sign}; use serde::{Deserialize, Serialize}; use std::borrow::Cow; @@ -28,6 +29,12 @@ where Sign::Plus => bz.insert(0, 0), Sign::NoSign => bz = Vec::new(), } + if bz.len() > MAX_ENCODED_SIZE { + return Err(serde::ser::Error::custom(format!( + "encoded big int was too large ({} bytes)", + bz.len() + ))); + } // Serialize as bytes serde_bytes::Serialize::serialize(&bz, serializer) @@ -42,6 +49,12 @@ where if bz.is_empty() { return Ok(BigInt::default()); } + if bz.len() > MAX_ENCODED_SIZE { + return Err(serde::de::Error::custom(format!( + "decoded big int was too large ({} bytes)", + bz.len() + ))); + } let sign_byte = bz[0]; let sign: Sign = match sign_byte { 1 => Sign::Minus, @@ -102,3 +115,57 @@ pub mod json { } } } + +#[cfg(test)] +mod tests { + use crate::bigint_ser::{deserialize, serialize}; + use num_bigint::{BigInt, Sign}; + use serde_cbor::de::Deserializer; + use serde_cbor::ser::Serializer; + + #[test] + fn serialize_bigint_test() { + // Create too large BigInt + let mut digits: Vec = Vec::new(); + for _ in 0..32 { + digits.push(u32::MAX); + } + let bi = BigInt::new(Sign::Plus, digits); + + // Serialize should fail + let mut cbor = Vec::new(); + let res = serialize(&bi, &mut Serializer::new(&mut cbor)); + assert!(res.is_err()); + } + + #[test] + fn deserialize_bigint_test() { + // Create a 129 bytes large BigInt + let mut bytes: Vec = Vec::new(); + bytes.push(0); + for _ in 0..128 { + bytes.push(u8::MAX); + } + // Serialize manually + let mut cbor = Vec::new(); + serde_bytes::serialize(&bytes, &mut Serializer::new(&mut cbor)).unwrap(); + + // Deserialize should fail + let res = deserialize(&mut Deserializer::from_slice(&cbor)); + assert!(res.is_err()); + + // Create a 128 bytes BigInt + let mut bytes: Vec = Vec::new(); + bytes.push(0); + for _ in 0..127 { + bytes.push(u8::MAX); + } + // Serialize manually + let mut cbor = Vec::new(); + serde_bytes::serialize(&bytes, &mut Serializer::new(&mut cbor)).unwrap(); + + // Deserialize should work + let res = deserialize(&mut Deserializer::from_slice(&cbor)); + assert!(res.is_ok()); + } +} diff --git a/utils/bigint/src/biguint_ser.rs b/utils/bigint/src/biguint_ser.rs index 851653349b3..187fb831b23 100644 --- a/utils/bigint/src/biguint_ser.rs +++ b/utils/bigint/src/biguint_ser.rs @@ -1,6 +1,7 @@ // Copyright 2019-2022 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT +use crate::MAX_ENCODED_SIZE; use num_bigint::BigUint; use serde::{Deserialize, Serialize}; use std::borrow::Cow; @@ -15,6 +16,7 @@ pub struct BigUintSer<'a>(#[serde(with = "self")] pub &'a BigUint); #[serde(transparent)] pub struct BigUintDe(#[serde(with = "self")] pub BigUint); +/// Serializes big uint as bytes. pub fn serialize(int: &BigUint, serializer: S) -> Result where S: serde::Serializer, @@ -27,11 +29,18 @@ where } else { bz.insert(0, 0); } + if bz.len() > MAX_ENCODED_SIZE { + return Err(serde::ser::Error::custom(format!( + "encoded big int was too large ({} bytes)", + bz.len() + ))); + } // Serialize as bytes serde_bytes::Serialize::serialize(&bz, serializer) } +/// Deserializes bytes into big uint. pub fn deserialize<'de, D>(deserializer: D) -> Result where D: serde::Deserializer<'de>, @@ -40,6 +49,12 @@ where if bz.is_empty() { return Ok(BigUint::default()); } + if bz.len() > MAX_ENCODED_SIZE { + return Err(serde::de::Error::custom(format!( + "decoded big uint was too large ({} bytes)", + bz.len() + ))); + } if bz.get(0) != Some(&0) { return Err(serde::de::Error::custom( @@ -49,3 +64,57 @@ where Ok(BigUint::from_bytes_be(&bz[1..])) } + +#[cfg(test)] +mod tests { + use crate::biguint_ser::{deserialize, serialize}; + use num_bigint::BigUint; + use serde_cbor::de::Deserializer; + use serde_cbor::ser::Serializer; + + #[test] + fn serialize_biguint_test() { + // Create too large BigUint + let mut digits: Vec = Vec::new(); + for _ in 0..32 { + digits.push(u32::MAX); + } + let bi = BigUint::new(digits); + + // Serialize should fail + let mut cbor = Vec::new(); + let res = serialize(&bi, &mut Serializer::new(&mut cbor)); + assert!(res.is_err()); + } + + #[test] + fn deserialize_biguint_test() { + // Create a 129 bytes large BigUint + let mut bytes: Vec = Vec::new(); + bytes.push(0); + for _ in 0..128 { + bytes.push(u8::MAX); + } + // Serialize manually + let mut cbor = Vec::new(); + serde_bytes::serialize(&bytes, &mut Serializer::new(&mut cbor)).unwrap(); + + // Deserialize should fail + let res = deserialize(&mut Deserializer::from_slice(&cbor)); + assert!(res.is_err()); + + // Create a 128 bytes BigUint + let mut bytes: Vec = Vec::new(); + bytes.push(0); + for _ in 0..127 { + bytes.push(u8::MAX); + } + // Serialize manually + let mut cbor = Vec::new(); + serde_bytes::serialize(&bytes, &mut Serializer::new(&mut cbor)).unwrap(); + + // Deserialize should work + let res = deserialize(&mut Deserializer::from_slice(&cbor)); + assert!(res.is_ok()); + } +} diff --git a/utils/bigint/src/lib.rs b/utils/bigint/src/lib.rs index 8132cf50ee4..a77261d14fc 100644 --- a/utils/bigint/src/lib.rs +++ b/utils/bigint/src/lib.rs @@ -6,3 +6,7 @@ pub mod biguint_ser; pub use num_bigint::*; pub use num_integer::{self, Integer}; + +/// MAX_ENCODED_SIZE is the max length of a byte slice representing a +/// CBOR serialized BigInt or BigUint. +const MAX_ENCODED_SIZE: usize = 128;