Security: Stop panicking on invalid reserved orchard::Flags bits (#2284)

* stop panicking in deserialize orchard flags

* make the test simpler

Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com>
This commit is contained in:
Alfredo Garcia 2021-06-14 21:04:18 -03:00 committed by GitHub
parent 86f23f7960
commit 3dcd407d66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 52 additions and 4 deletions

View File

@ -416,6 +416,9 @@ bitflags! {
This type is also defined in `orchard/shielded_data.rs`.
Note: A [consensus rule](https://zips.z.cash/protocol/protocol.pdf#txnencodingandconsensus) was added to the protocol specification stating that:
> In a version 5 transaction, the reserved bits 2..7 of the flagsOrchard field MUST be zero.
## Test Plan
[test-plan]: #test-plan

View File

@ -133,6 +133,14 @@ bitflags! {
///
/// The spend and output flags are passed to the `Halo2Proof` verifier, which verifies
/// the relevant note spending and creation consensus rules.
///
/// Consensus rules:
///
/// - "In a version 5 transaction, the reserved bits 2..7 of the flagsOrchard field MUST be zero."
/// ([`bitflags`](https://docs.rs/bitflags/1.2.1/bitflags/index.html) restricts its values to the
/// set of valid flags)
/// - "In a version 5 coinbase transaction, the enableSpendsOrchard flag MUST be 0."
/// (Checked in zebra-consensus)
#[derive(Deserialize, Serialize)]
pub struct Flags: u8 {
/// Enable spending non-zero valued Orchard notes.
@ -141,7 +149,6 @@ bitflags! {
const ENABLE_SPENDS = 0b00000001;
/// Enable creating new non-zero valued Orchard notes.
const ENABLE_OUTPUTS = 0b00000010;
// Reserved, zeros (bits 2 .. 7)
}
}
@ -155,8 +162,10 @@ impl ZcashSerialize for Flags {
impl ZcashDeserialize for Flags {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
let flags = Flags::from_bits(reader.read_u8()?).unwrap();
Ok(flags)
// Consensus rule: "In a version 5 transaction,
// the reserved bits 2..7 of the flagsOrchard field MUST be zero."
// https://zips.z.cash/protocol/protocol.pdf#txnencodingandconsensus
Flags::from_bits(reader.read_u8()?)
.ok_or(SerializationError::Parse("invalid reserved orchard flags"))
}
}

View File

@ -1 +1,2 @@
mod preallocate;
mod prop;

View File

@ -0,0 +1,35 @@
use proptest::prelude::*;
use std::io::Cursor;
use crate::{
orchard,
serialization::{ZcashDeserializeInto, ZcashSerialize},
};
proptest! {
/// Make sure only valid flags deserialize
#[test]
fn flag_roundtrip_bytes(flags in any::<u8>()) {
let mut serialized = Cursor::new(Vec::new());
flags.zcash_serialize(&mut serialized)?;
serialized.set_position(0);
let maybe_deserialized = (&mut serialized).zcash_deserialize_into();
let invalid_bits_mask = !orchard::Flags::all().bits();
match orchard::Flags::from_bits(flags) {
Some(valid_flags) => {
prop_assert_eq!(maybe_deserialized.ok(), Some(valid_flags));
prop_assert_eq!(flags & invalid_bits_mask, 0);
}
None => {
prop_assert_eq!(
maybe_deserialized.err().unwrap().to_string(),
"parse error: invalid reserved orchard flags"
);
prop_assert_ne!(flags & invalid_bits_mask, 0);
}
}
}
}