Commit 2bb98bb8 authored by cheme's avatar cheme Committed by Bastian Köcher
Browse files

Avoid some malleability in compact integers (#67)



* mallibility in encoding, do not know if it is acceptable.

* even on u8

* Add no malleability.

* test all lower bounds

* Test Error.

* No old school compare.

* Test for some case

* Update src/codec.rs

Co-Authored-By: cheme's avatarcheme <emericchevalier.pro@gmail.com>

* Update src/codec.rs

Co-Authored-By: cheme's avatarcheme <emericchevalier.pro@gmail.com>
parent d7ece0cd
......@@ -674,19 +674,25 @@ impl Decode for Compact<()> {
}
}
const U8_OUT_OF_RANGE: &'static str = "out of range decoding Compact<u8>";
const U16_OUT_OF_RANGE: &'static str = "out of range decoding Compact<u16>";
const U32_OUT_OF_RANGE: &'static str = "out of range decoding Compact<u32>";
const U64_OUT_OF_RANGE: &'static str = "out of range decoding Compact<u64>";
const U128_OUT_OF_RANGE: &'static str = "out of range decoding Compact<u128>";
impl Decode for Compact<u8> {
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
let prefix = input.read_byte()?;
Ok(Compact(match prefix % 4 {
0 => prefix as u8 >> 2,
0 => prefix >> 2,
1 => {
let x = u16::decode(&mut PrefixInput{prefix: Some(prefix), input})? >> 2;
if x < 256 {
if x > 0b00111111 && x <= 255 {
x as u8
} else {
return Err("out of range decoding Compact<u8>".into());
return Err(U8_OUT_OF_RANGE.into());
}
}
},
_ => return Err("unexpected prefix decoding Compact<u8>".into()),
}))
}
......@@ -697,15 +703,22 @@ impl Decode for Compact<u16> {
let prefix = input.read_byte()?;
Ok(Compact(match prefix % 4 {
0 => u16::from(prefix) >> 2,
1 => u16::decode(&mut PrefixInput{prefix: Some(prefix), input})? as u16 >> 2,
1 => {
let x = u16::decode(&mut PrefixInput{prefix: Some(prefix), input})? >> 2;
if x > 0b00111111 && x <= 0b00111111_11111111 {
u16::from(x)
} else {
return Err(U16_OUT_OF_RANGE.into());
}
},
2 => {
let x = u32::decode(&mut PrefixInput{prefix: Some(prefix), input})? >> 2;
if x < 65536 {
if x > 0b00111111_11111111 && x < 65536 {
x as u16
} else {
return Err("out of range decoding Compact<u16>".into());
return Err(U16_OUT_OF_RANGE.into());
}
}
},
_ => return Err("unexpected prefix decoding Compact<u16>".into()),
}))
}
......@@ -716,15 +729,34 @@ impl Decode for Compact<u32> {
let prefix = input.read_byte()?;
Ok(Compact(match prefix % 4 {
0 => u32::from(prefix) >> 2,
1 => u32::from(u16::decode(&mut PrefixInput{prefix: Some(prefix), input})?) >> 2,
2 => u32::decode(&mut PrefixInput{prefix: Some(prefix), input})? >> 2,
1 => {
let x = u16::decode(&mut PrefixInput{prefix: Some(prefix), input})? >> 2;
if x > 0b00111111 && x <= 0b00111111_11111111 {
u32::from(x)
} else {
return Err(U32_OUT_OF_RANGE.into());
}
},
2 => {
let x = u32::decode(&mut PrefixInput{prefix: Some(prefix), input})? >> 2;
if x > 0b00111111_11111111 && x <= u32::max_value() >> 2 {
u32::from(x)
} else {
return Err(U32_OUT_OF_RANGE.into());
}
},
3|_ => { // |_. yeah, i know.
if prefix >> 2 == 0 {
// just 4 bytes. ok.
u32::decode(input)?
let x = u32::decode(input)?;
if x > u32::max_value() >> 2 {
u32::from(x)
} else {
return Err(U32_OUT_OF_RANGE.into());
}
} else {
// Out of range for a 32-bit quantity.
return Err("out of range decoding Compact<u32>".into());
return Err(U32_OUT_OF_RANGE.into());
}
}
}))
......@@ -736,19 +768,51 @@ impl Decode for Compact<u64> {
let prefix = input.read_byte()?;
Ok(Compact(match prefix % 4 {
0 => u64::from(prefix) >> 2,
1 => u64::from(u16::decode(&mut PrefixInput{prefix: Some(prefix), input})?) >> 2,
2 => u64::from(u32::decode(&mut PrefixInput{prefix: Some(prefix), input})?) >> 2,
1 => {
let x = u16::decode(&mut PrefixInput{prefix: Some(prefix), input})? >> 2;
if x > 0b00111111 && x <= 0b00111111_11111111 {
u64::from(x)
} else {
return Err(U64_OUT_OF_RANGE.into());
}
},
2 => {
let x = u32::decode(&mut PrefixInput{prefix: Some(prefix), input})? >> 2;
if x > 0b00111111_11111111 && x <= u32::max_value() >> 2 {
u64::from(x)
} else {
return Err(U64_OUT_OF_RANGE.into());
}
},
3|_ => match (prefix >> 2) + 4 {
4 => u64::from(u32::decode(input)?),
8 => u64::decode(input)?,
4 => {
let x = u32::decode(input)?;
if x > u32::max_value() >> 2 {
u64::from(x)
} else {
return Err(U64_OUT_OF_RANGE.into());
}
},
8 => {
let x = u64::decode(input)?;
if x > u64::max_value() >> 8 {
x
} else {
return Err(U64_OUT_OF_RANGE.into());
}
},
x if x > 8 => return Err("unexpected prefix decoding Compact<u64>".into()),
bytes_needed => {
let mut res = 0;
for i in 0..bytes_needed {
res |= u64::from(input.read_byte()?) << (i * 8);
}
res
}
if res > u64::max_value() >> (8 - bytes_needed + 1) * 8 {
res
} else {
return Err(U64_OUT_OF_RANGE.into());
}
},
},
}))
}
......@@ -759,21 +823,60 @@ impl Decode for Compact<u128> {
let prefix = input.read_byte()?;
Ok(Compact(match prefix % 4 {
0 => u128::from(prefix) >> 2,
1 => u128::from(u16::decode(&mut PrefixInput{prefix: Some(prefix), input})?) >> 2,
2 => u128::from(u32::decode(&mut PrefixInput{prefix: Some(prefix), input})?) >> 2,
1 => {
let x = u16::decode(&mut PrefixInput{prefix: Some(prefix), input})? >> 2;
if x > 0b00111111 && x <= 0b00111111_11111111 {
u128::from(x)
} else {
return Err(U128_OUT_OF_RANGE.into());
}
},
2 => {
let x = u32::decode(&mut PrefixInput{prefix: Some(prefix), input})? >> 2;
if x > 0b00111111_11111111 && x <= u32::max_value() >> 2 {
u128::from(x)
} else {
return Err(U128_OUT_OF_RANGE.into());
}
},
3|_ => match (prefix >> 2) + 4 {
4 => u128::from(u32::decode(input)?),
8 => u128::from(u64::decode(input)?),
16 => u128::decode(input)?,
4 => {
let x = u32::decode(input)?;
if x > u32::max_value() >> 2 {
u128::from(x)
} else {
return Err(U128_OUT_OF_RANGE.into());
}
},
8 => {
let x = u64::decode(input)?;
if x > u64::max_value() >> 8 {
u128::from(x)
} else {
return Err(U128_OUT_OF_RANGE.into());
}
},
16 => {
let x = u128::decode(input)?;
if x > u128::max_value() >> 8 {
x
} else {
return Err(U128_OUT_OF_RANGE.into());
}
},
x if x > 16 => return Err("unexpected prefix decoding Compact<u128>".into()),
bytes_needed => {
let mut res = 0;
for i in 0..bytes_needed {
res |= u128::from(input.read_byte()?) << (i * 8);
}
res
}
}
if res > u128::max_value() >> (16 - bytes_needed + 1) * 8 {
res
} else {
return Err(U128_OUT_OF_RANGE.into());
}
},
},
}))
}
}
......@@ -1655,4 +1758,98 @@ mod tests {
assert_eq!(MyWrapper(3u32.into()).encode(), result);
assert_eq!(MyWrapper::decode(&mut &*result).unwrap(), MyWrapper(3_u32.into()));
}
macro_rules! check_bound {
( $m:expr, $ty:ty, $typ1:ty, [ $(($ty2:ty, $ty2_err:expr)),* ]) => {
$(
check_bound!($m, $ty, $typ1, $ty2, $ty2_err);
)*
};
( $m:expr, $ty:ty, $typ1:ty, $ty2:ty, $ty2_err:expr) => {
let enc = ((<$ty>::max_value() >> 2) as $typ1 << 2) | $m;
assert_eq!(Compact::<$ty2>::decode(&mut &enc.to_le_bytes()[..]),
Err($ty2_err.into()));
};
}
macro_rules! check_bound_u32 {
( [ $(($ty2:ty, $ty2_err:expr)),* ]) => {
$(
check_bound_u32!($ty2, $ty2_err);
)*
};
( $ty2:ty, $ty2_err:expr ) => {
assert_eq!(Compact::<$ty2>::decode(&mut &[0b11, 0xff, 0xff, 0xff, 0xff >> 2][..]),
Err($ty2_err.into()));
};
}
macro_rules! check_bound_high {
( $m:expr, [ $(($ty2:ty, $ty2_err:expr)),* ]) => {
$(
check_bound_high!($m, $ty2, $ty2_err);
)*
};
( $s:expr, $ty2:ty, $ty2_err:expr) => {
let mut dest = Vec::new();
dest.push(0b11 + (($s - 4) << 2) as u8);
for _ in 0..($s - 1) {
dest.push(u8::max_value());
}
dest.push(0);
assert_eq!(Compact::<$ty2>::decode(&mut &dest[..]),
Err($ty2_err.into()));
};
}
#[test]
fn compact_u64_test() {
for a in [
u64::max_value(),
u64::max_value() - 1,
u64::max_value() << 8,
(u64::max_value() << 8) - 1,
u64::max_value() << 16,
(u64::max_value() << 16) - 1,
].into_iter() {
let e = Compact::<u64>::encode(&Compact(*a));
let d = Compact::<u64>::decode(&mut &e[..]).unwrap().0;
assert_eq!(*a, d);
}
}
#[test]
fn compact_u128_test() {
for a in [
u64::max_value() as u128,
(u64::max_value() - 10) as u128,
u128::max_value(),
u128::max_value() - 10,
].into_iter() {
let e = Compact::<u128>::encode(&Compact(*a));
let d = Compact::<u128>::decode(&mut &e[..]).unwrap().0;
assert_eq!(*a, d);
}
}
#[test]
fn should_avoid_overlapping_definition() {
check_bound!(
0b01, u8, u16, [ (u8, U8_OUT_OF_RANGE), (u16, U16_OUT_OF_RANGE),
(u32, U32_OUT_OF_RANGE), (u64, U64_OUT_OF_RANGE), (u128, U128_OUT_OF_RANGE)]
);
check_bound!(
0b10, u16, u32, [ (u16, U16_OUT_OF_RANGE),
(u32, U32_OUT_OF_RANGE), (u64, U64_OUT_OF_RANGE), (u128, U128_OUT_OF_RANGE)]
);
check_bound_u32!(
[(u32, U32_OUT_OF_RANGE), (u64, U64_OUT_OF_RANGE), (u128, U128_OUT_OF_RANGE)]
);
for i in 5..=8 {
check_bound_high!(i, [(u64, U64_OUT_OF_RANGE), (u128, U128_OUT_OF_RANGE)]);
}
for i in 8..=16 {
check_bound_high!(i, [(u128, U128_OUT_OF_RANGE)]);
}
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment