diff --git a/networks/monero/generators/src/lib.rs b/networks/monero/generators/src/lib.rs index bc78a1e5..ebff93cc 100644 --- a/networks/monero/generators/src/lib.rs +++ b/networks/monero/generators/src/lib.rs @@ -51,8 +51,6 @@ pub fn H_pow_2() -> &'static [EdwardsPoint; 64] { pub const MAX_COMMITMENTS: usize = 16; /// The amount of bits a value within a commitment may use. pub const COMMITMENT_BITS: usize = 64; -/// The logarithm (over 2) of the amount of bits a value within a commitment may use. -pub const LOG_COMMITMENT_BITS: usize = 6; // 2 ** 6 == N /// Container struct for Bulletproofs(+) generators. #[allow(non_snake_case)] diff --git a/networks/monero/io/src/lib.rs b/networks/monero/io/src/lib.rs index 68acbe80..4e43b77e 100644 --- a/networks/monero/io/src/lib.rs +++ b/networks/monero/io/src/lib.rs @@ -214,6 +214,20 @@ pub fn read_array io::Result, const N: us } /// Read a length-prefixed variable-length list of elements. -pub fn read_vec io::Result>(f: F, r: &mut R) -> io::Result> { - read_raw_vec(f, read_varint(r)?, r) +/// +/// An optional bound on the length of the result may be provided. If `None`, the returned `Vec` +/// will be of the length read off the reader, if successfully read. If `Some(_)`, an error will be +/// raised if the length read off the read is greater than the bound. +pub fn read_vec io::Result>( + f: F, + length_bound: Option, + r: &mut R, +) -> io::Result> { + let declared_length: usize = read_varint(r)?; + if let Some(length_bound) = length_bound { + if declared_length > length_bound { + Err(io::Error::other("vector exceeds bound on length"))?; + } + } + read_raw_vec(f, declared_length, r) } diff --git a/networks/monero/primitives/src/lib.rs b/networks/monero/primitives/src/lib.rs index 543f385e..f3e50a01 100644 --- a/networks/monero/primitives/src/lib.rs +++ b/networks/monero/primitives/src/lib.rs @@ -213,7 +213,7 @@ impl Decoys { pub fn write(&self, w: &mut impl io::Write) -> io::Result<()> { write_vec(write_varint, &self.offsets, w)?; w.write_all(&[self.signer_index])?; - write_vec( + write_raw_vec( |pair, w| { write_point(&pair[0], w)?; write_point(&pair[1], w) @@ -239,10 +239,12 @@ impl Decoys { /// This is not a Monero protocol defined struct, and this is accordingly not a Monero protocol /// defined serialization. pub fn read(r: &mut impl io::Read) -> io::Result { + let offsets = read_vec(read_varint, None, r)?; + let len = offsets.len(); Decoys::new( - read_vec(read_varint, r)?, + offsets, read_byte(r)?, - read_vec(|r| Ok([read_point(r)?, read_point(r)?]), r)?, + read_raw_vec(|r| Ok([read_point(r)?, read_point(r)?]), len, r)?, ) .ok_or_else(|| io::Error::other("invalid Decoys")) } diff --git a/networks/monero/ringct/bulletproofs/src/core.rs b/networks/monero/ringct/bulletproofs/src/core.rs index 09112670..9dc86fcd 100644 --- a/networks/monero/ringct/bulletproofs/src/core.rs +++ b/networks/monero/ringct/bulletproofs/src/core.rs @@ -6,7 +6,7 @@ use curve25519_dalek::{ edwards::EdwardsPoint, }; -pub(crate) use monero_generators::{MAX_COMMITMENTS, COMMITMENT_BITS, LOG_COMMITMENT_BITS}; +pub(crate) use monero_generators::{MAX_COMMITMENTS, COMMITMENT_BITS}; pub(crate) fn multiexp(pairs: &[(Scalar, EdwardsPoint)]) -> EdwardsPoint { let mut buf_scalars = Vec::with_capacity(pairs.len()); diff --git a/networks/monero/ringct/bulletproofs/src/lib.rs b/networks/monero/ringct/bulletproofs/src/lib.rs index 2a789575..61a5dbc9 100644 --- a/networks/monero/ringct/bulletproofs/src/lib.rs +++ b/networks/monero/ringct/bulletproofs/src/lib.rs @@ -17,13 +17,13 @@ use curve25519_dalek::edwards::EdwardsPoint; use monero_io::*; pub use monero_generators::MAX_COMMITMENTS; +use monero_generators::COMMITMENT_BITS; use monero_primitives::Commitment; pub(crate) mod scalar_vector; pub(crate) mod point_vector; pub(crate) mod core; -use crate::core::LOG_COMMITMENT_BITS; pub(crate) mod batch_verifier; use batch_verifier::{BulletproofsBatchVerifier, BulletproofsPlusBatchVerifier}; @@ -44,6 +44,11 @@ use crate::plus::{ #[cfg(test)] mod tests; +// The logarithm (over 2) of the amount of bits a value within a commitment may use. +const LOG_COMMITMENT_BITS: usize = COMMITMENT_BITS.ilog2() as usize; +// The maximum length of L/R `Vec`s. +const MAX_LR: usize = (MAX_COMMITMENTS.ilog2() as usize) + LOG_COMMITMENT_BITS; + /// An error from proving/verifying Bulletproofs(+). #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[cfg_attr(feature = "std", derive(thiserror::Error))] @@ -265,8 +270,8 @@ impl Bulletproof { tau_x: read_scalar(r)?, mu: read_scalar(r)?, ip: IpProof { - L: read_vec(read_point, r)?, - R: read_vec(read_point, r)?, + L: read_vec(read_point, Some(MAX_LR), r)?, + R: read_vec(read_point, Some(MAX_LR), r)?, a: read_scalar(r)?, b: read_scalar(r)?, }, @@ -284,8 +289,8 @@ impl Bulletproof { r_answer: read_scalar(r)?, s_answer: read_scalar(r)?, delta_answer: read_scalar(r)?, - L: read_vec(read_point, r)?.into_iter().collect(), - R: read_vec(read_point, r)?.into_iter().collect(), + L: read_vec(read_point, Some(MAX_LR), r)?.into_iter().collect(), + R: read_vec(read_point, Some(MAX_LR), r)?.into_iter().collect(), }, })) } diff --git a/networks/monero/src/transaction.rs b/networks/monero/src/transaction.rs index 54b1b80f..8d998ec6 100644 --- a/networks/monero/src/transaction.rs +++ b/networks/monero/src/transaction.rs @@ -71,7 +71,7 @@ impl Input { let amount = if amount == 0 { None } else { Some(amount) }; Input::ToKey { amount, - key_offsets: read_vec(read_varint, r)?, + key_offsets: read_vec(read_varint, None, r)?, key_image: read_torsion_free_point(r)?, } } @@ -241,7 +241,7 @@ impl TransactionPrefix { pub fn read(r: &mut R, version: u64) -> io::Result { let additional_timelock = Timelock::read(r)?; - let inputs = read_vec(|r| Input::read(r), r)?; + let inputs = read_vec(|r| Input::read(r), None, r)?; if inputs.is_empty() { Err(io::Error::other("transaction had no inputs"))?; } @@ -250,10 +250,10 @@ impl TransactionPrefix { let mut prefix = TransactionPrefix { additional_timelock, inputs, - outputs: read_vec(|r| Output::read((!is_miner_tx) && (version == 2), r), r)?, + outputs: read_vec(|r| Output::read((!is_miner_tx) && (version == 2), r), None, r)?, extra: vec![], }; - prefix.extra = read_vec(read_byte, r)?; + prefix.extra = read_vec(read_byte, None, r)?; Ok(prefix) } diff --git a/networks/monero/wallet/src/extra.rs b/networks/monero/wallet/src/extra.rs index 537e595a..2c0706fd 100644 --- a/networks/monero/wallet/src/extra.rs +++ b/networks/monero/wallet/src/extra.rs @@ -181,16 +181,10 @@ impl ExtraField { size }), 1 => ExtraField::PublicKey(read_point(r)?), - 2 => ExtraField::Nonce({ - let nonce = read_vec(read_byte, r)?; - if nonce.len() > MAX_TX_EXTRA_NONCE_SIZE { - Err(io::Error::other("too long nonce"))?; - } - nonce - }), + 2 => ExtraField::Nonce(read_vec(read_byte, Some(MAX_TX_EXTRA_NONCE_SIZE), r)?), 3 => ExtraField::MergeMining(read_varint(r)?, read_bytes(r)?), - 4 => ExtraField::PublicKeys(read_vec(read_point, r)?), - 0xDE => ExtraField::MysteriousMinergate(read_vec(read_byte, r)?), + 4 => ExtraField::PublicKeys(read_vec(read_point, None, r)?), + 0xDE => ExtraField::MysteriousMinergate(read_vec(read_byte, None, r)?), _ => Err(io::Error::other("unknown extra field"))?, }) } diff --git a/networks/monero/wallet/src/send/mod.rs b/networks/monero/wallet/src/send/mod.rs index ca92961f..d3adb166 100644 --- a/networks/monero/wallet/src/send/mod.rs +++ b/networks/monero/wallet/src/send/mod.rs @@ -456,7 +456,7 @@ impl SignableTransaction { /// defined serialization. pub fn read(r: &mut R) -> io::Result { fn read_address(r: &mut R) -> io::Result { - String::from_utf8(read_vec(read_byte, r)?) + String::from_utf8(read_vec(read_byte, None, r)?) .ok() .and_then(|str| MoneroAddress::from_str_with_unchecked_network(&str).ok()) .ok_or_else(|| io::Error::other("invalid address")) @@ -484,9 +484,9 @@ impl SignableTransaction { rct_type: RctType::try_from(read_byte(r)?) .map_err(|()| io::Error::other("unsupported/invalid RctType"))?, outgoing_view_key: Zeroizing::new(read_bytes(r)?), - inputs: read_vec(OutputWithDecoys::read, r)?, - payments: read_vec(read_payment, r)?, - data: read_vec(|r| read_vec(read_byte, r), r)?, + inputs: read_vec(OutputWithDecoys::read, None, r)?, + payments: read_vec(read_payment, None, r)?, + data: read_vec(|r| read_vec(read_byte, None, r), None, r)?, fee_rate: FeeRate::read(r)?, }; match res.validate() {