Skip to content

Commit

Permalink
Fix encoding size constraints for BigInt and BigUint not enforced (#1367
Browse files Browse the repository at this point in the history
)

Co-authored-by: David Himmelstrup <[email protected]>
  • Loading branch information
elmattic and lemmih authored Jan 21, 2022
1 parent 243c411 commit 1c385e4
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions utils/bigint/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []
67 changes: 67 additions & 0 deletions utils/bigint/src/bigint_ser.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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)
Expand All @@ -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,
Expand Down Expand Up @@ -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<u32> = 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<u8> = 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<u8> = 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());
}
}
69 changes: 69 additions & 0 deletions utils/bigint/src/biguint_ser.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<S>(int: &BigUint, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
Expand All @@ -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<BigUint, D::Error>
where
D: serde::Deserializer<'de>,
Expand All @@ -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(
Expand All @@ -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<u32> = 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<u8> = 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<u8> = 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());
}
}
4 changes: 4 additions & 0 deletions utils/bigint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

0 comments on commit 1c385e4

Please sign in to comment.