Skip to content

Commit a0de725

Browse files
authored
feat(encoding): add max length check for bytes (#1399)
* feat(encoding): add constants MaxLength and ByteArrayLength in encoding * feat(encoding): add encoding for byte array and generic array * feat(beacon): refactor BeaconEntry with ByteArray * feat(serde): add module checked_serde_bytes * chore(lint): fix lint * feat(serde): apply serde_byte_array to ProofBytes and VRFBytes * chore(serde): clean files * feat(encoding): add tests for serde_byte_array * chore(lint): make clippy happy * doc(encoding): fix typo
1 parent f96876c commit a0de725

File tree

6 files changed

+140
-8
lines changed

6 files changed

+140
-8
lines changed

blockchain/beacon/src/beacon_entries.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
// Copyright 2019-2022 ChainSafe Systems
22
// SPDX-License-Identifier: Apache-2.0, MIT
33

4-
use encoding::{serde_bytes, tuple::*};
4+
use encoding::{serde_byte_array, tuple::*};
55

66
/// The result from getting an entry from Drand.
77
/// The entry contains the round, or epoch as well as the BLS signature for that round of
88
/// randomness.
99
/// This beacon entry is stored on chain in the block header.
10-
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize_tuple, Deserialize_tuple)]
10+
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize_tuple, Serialize_tuple)]
1111
pub struct BeaconEntry {
1212
round: u64,
13-
#[serde(with = "serde_bytes")]
13+
#[serde(with = "serde_byte_array")]
1414
data: Vec<u8>,
1515
}
1616

crypto/src/vrf.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33

44
use crate::signature::verify_bls_sig;
55
use address::Address;
6-
use encoding::{blake2b_256, serde_bytes};
6+
use encoding::{blake2b_256, serde_byte_array};
77
use serde::{Deserialize, Serialize};
88

99
/// The output from running a VRF proof.
1010
#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Default, Serialize, Deserialize)]
1111
#[serde(transparent)]
12-
pub struct VRFProof(#[serde(with = "serde_bytes")] pub Vec<u8>);
12+
pub struct VRFProof(#[serde(with = "serde_byte_array")] pub Vec<u8>);
1313

1414
impl VRFProof {
1515
/// Creates a VRFProof from a raw vector.

encoding/src/checked_serde_bytes.rs

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// Copyright 2019-2022 ChainSafe Systems
2+
// SPDX-License-Identifier: Apache-2.0, MIT
3+
4+
use crate::BYTE_ARRAY_MAX_LEN;
5+
use serde::{de, ser, Deserializer, Serializer};
6+
use serde_bytes::{Deserialize, Serialize};
7+
8+
/// serde_bytes with max length check
9+
pub mod serde_byte_array {
10+
use super::*;
11+
12+
/// checked if input > `crate::BYTE_ARRAY_MAX_LEN`
13+
pub fn serialize<T, S>(bytes: &T, serializer: S) -> Result<S::Ok, S::Error>
14+
where
15+
T: ?Sized + Serialize + AsRef<[u8]>,
16+
S: Serializer,
17+
{
18+
let len = bytes.as_ref().len();
19+
if len > BYTE_ARRAY_MAX_LEN {
20+
return Err(ser::Error::custom::<String>(
21+
"Array exceed max length".into(),
22+
));
23+
}
24+
25+
Serialize::serialize(bytes, serializer)
26+
}
27+
28+
/// checked if output > `crate::ByteArrayMaxLen`
29+
pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
30+
where
31+
T: Deserialize<'de> + AsRef<[u8]>,
32+
D: Deserializer<'de>,
33+
{
34+
Deserialize::deserialize(deserializer).and_then(|bytes: T| {
35+
if bytes.as_ref().len() > BYTE_ARRAY_MAX_LEN {
36+
Err(de::Error::custom::<String>(
37+
"Array exceed max length".into(),
38+
))
39+
} else {
40+
Ok(bytes)
41+
}
42+
})
43+
}
44+
}
45+
46+
#[cfg(test)]
47+
mod tests {
48+
use super::serde_byte_array;
49+
use crate::BYTE_ARRAY_MAX_LEN;
50+
use serde::{Deserialize, Serialize};
51+
52+
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
53+
struct ByteArray {
54+
#[serde(with = "serde_byte_array")]
55+
pub inner: Vec<u8>,
56+
}
57+
58+
#[test]
59+
fn can_serialize_byte_array() {
60+
for len in [0, 1, BYTE_ARRAY_MAX_LEN] {
61+
let bytes = ByteArray {
62+
inner: vec![0; len],
63+
};
64+
65+
assert!(serde_cbor::to_vec(&bytes).is_ok());
66+
}
67+
}
68+
69+
#[test]
70+
fn cannot_serialize_byte_array_overflow() {
71+
let bytes = ByteArray {
72+
inner: vec![0; BYTE_ARRAY_MAX_LEN + 1],
73+
};
74+
75+
assert_eq!(
76+
format!("{}", serde_cbor::to_vec(&bytes).err().unwrap()),
77+
"Array exceed max length".to_string()
78+
);
79+
}
80+
81+
#[test]
82+
fn can_deserialize_byte_array() {
83+
for len in [0, 1, BYTE_ARRAY_MAX_LEN] {
84+
let bytes = ByteArray {
85+
inner: vec![0; len],
86+
};
87+
88+
let encoding = serde_cbor::to_vec(&bytes).unwrap();
89+
assert_eq!(
90+
serde_cbor::from_slice::<ByteArray>(&encoding).unwrap(),
91+
bytes
92+
);
93+
}
94+
}
95+
96+
#[test]
97+
fn cannot_deserialize_byte_array_overflow() {
98+
let max_length_bytes = ByteArray {
99+
inner: vec![0; BYTE_ARRAY_MAX_LEN],
100+
};
101+
102+
// prefix: 2 ^ 21 -> 2 ^ 21 + 1
103+
let mut overflow_encoding = serde_cbor::to_vec(&max_length_bytes).unwrap();
104+
let encoding_len = overflow_encoding.len();
105+
overflow_encoding[encoding_len - BYTE_ARRAY_MAX_LEN - 1] = 1;
106+
overflow_encoding.push(0);
107+
108+
assert_eq!(
109+
format!(
110+
"{}",
111+
serde_cbor::from_slice::<ByteArray>(&overflow_encoding)
112+
.err()
113+
.unwrap()
114+
),
115+
"Array exceed max length".to_string()
116+
);
117+
}
118+
}

encoding/src/lib.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
mod bytes;
55
mod cbor;
6+
mod checked_serde_bytes;
67
mod errors;
78
mod hash;
89

@@ -12,6 +13,7 @@ pub use serde_cbor::{error, from_reader, from_slice, tags, to_vec, to_writer};
1213

1314
pub use self::bytes::*;
1415
pub use self::cbor::*;
16+
pub use self::checked_serde_bytes::serde_byte_array;
1517
pub use self::errors::*;
1618
pub use self::hash::*;
1719

@@ -22,3 +24,15 @@ pub mod tuple {
2224
pub mod repr {
2325
pub use serde_repr::{Deserialize_repr, Serialize_repr};
2426
}
27+
28+
/// lotus use cbor-gen for generating codec for types, it has a length limit of generic array
29+
/// for `8192`
30+
///
31+
/// https://github.com/whyrusleeping/cbor-gen/blob/f57984553008dd4285df16d4ec2760f97977d713/gen.go#L14
32+
pub const GENERIC_ARRAY_MAX_LEN: usize = 8192;
33+
34+
/// lotus use cbor-gen for generating codec for types, it has a length limit for byte
35+
/// array as `2 << 20`
36+
///
37+
/// https://github.com/whyrusleeping/cbor-gen/blob/f57984553008dd4285df16d4ec2760f97977d713/gen.go#L16
38+
pub const BYTE_ARRAY_MAX_LEN: usize = 2097152;

types/src/sector/post.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
use crate::{ActorID, Randomness, RegisteredPoStProof, RegisteredSealProof, SectorNumber};
55
use cid::Cid;
6-
use encoding::{serde_bytes, tuple::*};
6+
use encoding::{serde_byte_array, tuple::*};
77

88
/// Randomness type used for generating PoSt proof randomness.
99
pub type PoStRandomness = Randomness;
@@ -21,7 +21,7 @@ pub struct SectorInfo {
2121
#[derive(Debug, PartialEq, Clone, Eq, Serialize_tuple, Deserialize_tuple)]
2222
pub struct PoStProof {
2323
pub post_proof: RegisteredPoStProof,
24-
#[serde(with = "serde_bytes")]
24+
#[serde(with = "serde_byte_array")]
2525
pub proof_bytes: Vec<u8>,
2626
}
2727

utils/bitfield/src/rleplus/writer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0, MIT
33

44
/// The maximum varint that can be serialized on 9 bytes.
5-
const MAX_VARINT: u64 = 2u64.pow(63)-1;
5+
const MAX_VARINT: u64 = 2u64.pow(63) - 1;
66

77
#[derive(Default, Clone, Debug)]
88
/// A `BitWriter` allows for efficiently writing bits to a byte buffer, up to a byte at a time.

0 commit comments

Comments
 (0)