From 0e6dd54f81af6af3c4e708a1c14e088cf0e574cf Mon Sep 17 00:00:00 2001 From: samkim-crypto Date: Tue, 21 Nov 2023 08:48:41 +0900 Subject: [PATCH] [zk-token-sdk] Restrict range proof generator length and prevent 0-bit range proof (#34166) * limit range proof generator length * forbid 0-bit range proof verification --- zk-token-sdk/src/range_proof/errors.rs | 13 ++++++++- zk-token-sdk/src/range_proof/generators.rs | 27 +++++++++++++------ zk-token-sdk/src/range_proof/inner_product.rs | 4 +-- zk-token-sdk/src/range_proof/mod.rs | 6 +++-- 4 files changed, 37 insertions(+), 13 deletions(-) diff --git a/zk-token-sdk/src/range_proof/errors.rs b/zk-token-sdk/src/range_proof/errors.rs index fb08cb754..25ae0ed87 100644 --- a/zk-token-sdk/src/range_proof/errors.rs +++ b/zk-token-sdk/src/range_proof/errors.rs @@ -2,7 +2,10 @@ use {crate::errors::TranscriptError, thiserror::Error}; #[derive(Error, Clone, Debug, Eq, PartialEq)] -pub enum RangeProofGenerationError {} +pub enum RangeProofGenerationError { + #[error("maximum generator length exceeded")] + MaximumGeneratorLengthExceeded, +} #[derive(Error, Clone, Debug, Eq, PartialEq)] pub enum RangeProofVerificationError { @@ -20,4 +23,12 @@ pub enum RangeProofVerificationError { InvalidBitSize, #[error("insufficient generators for the proof")] InvalidGeneratorsLength, + #[error("maximum generator length exceeded")] + MaximumGeneratorLengthExceeded, +} + +#[derive(Error, Clone, Debug, Eq, PartialEq)] +pub enum RangeProofGeneratorError { + #[error("maximum generator length exceeded")] + MaximumGeneratorLengthExceeded, } diff --git a/zk-token-sdk/src/range_proof/generators.rs b/zk-token-sdk/src/range_proof/generators.rs index bc0ce24fc..a993d753d 100644 --- a/zk-token-sdk/src/range_proof/generators.rs +++ b/zk-token-sdk/src/range_proof/generators.rs @@ -1,4 +1,5 @@ use { + crate::range_proof::errors::RangeProofGeneratorError, curve25519_dalek::{ digest::{ExtendableOutput, Update, XofReader}, ristretto::RistrettoPoint, @@ -6,6 +7,9 @@ use { sha3::{Sha3XofReader, Shake256}, }; +#[cfg(not(target_os = "solana"))] +const MAX_GENERATOR_LENGTH: usize = u32::MAX as usize; + /// Generators for Pedersen vector commitments that are used for inner-product proofs. struct GeneratorsChain { reader: Sha3XofReader, @@ -67,37 +71,44 @@ pub struct BulletproofGens { } impl BulletproofGens { - pub fn new(gens_capacity: usize) -> Self { + pub fn new(gens_capacity: usize) -> Result { let mut gens = BulletproofGens { gens_capacity: 0, G_vec: Vec::new(), H_vec: Vec::new(), }; - gens.increase_capacity(gens_capacity); - gens + gens.increase_capacity(gens_capacity)?; + Ok(gens) } /// Increases the generators' capacity to the amount specified. /// If less than or equal to the current capacity, does nothing. - pub fn increase_capacity(&mut self, new_capacity: usize) { + pub fn increase_capacity( + &mut self, + new_capacity: usize, + ) -> Result<(), RangeProofGeneratorError> { if self.gens_capacity >= new_capacity { - return; + return Ok(()); + } + + if new_capacity > MAX_GENERATOR_LENGTH { + return Err(RangeProofGeneratorError::MaximumGeneratorLengthExceeded); } - let label = [b'G']; self.G_vec.extend( - &mut GeneratorsChain::new(&[label, [b'G']].concat()) + &mut GeneratorsChain::new(&[b'G']) .fast_forward(self.gens_capacity) .take(new_capacity - self.gens_capacity), ); self.H_vec.extend( - &mut GeneratorsChain::new(&[label, [b'H']].concat()) + &mut GeneratorsChain::new(&[b'H']) .fast_forward(self.gens_capacity) .take(new_capacity - self.gens_capacity), ); self.gens_capacity = new_capacity; + Ok(()) } #[allow(non_snake_case)] diff --git a/zk-token-sdk/src/range_proof/inner_product.rs b/zk-token-sdk/src/range_proof/inner_product.rs index 0a648e3ea..baecef78d 100644 --- a/zk-token-sdk/src/range_proof/inner_product.rs +++ b/zk-token-sdk/src/range_proof/inner_product.rs @@ -205,7 +205,7 @@ impl InnerProductProof { transcript: &mut Transcript, ) -> Result<(Vec, Vec, Vec), RangeProofVerificationError> { let lg_n = self.L_vec.len(); - if lg_n >= 32 { + if lg_n == 0 || lg_n >= 32 { // 4 billion multiplications should be enough for anyone // and this check prevents overflow in 1< = bp_gens.G(n).cloned().collect(); let H: Vec = bp_gens.H(n).cloned().collect(); diff --git a/zk-token-sdk/src/range_proof/mod.rs b/zk-token-sdk/src/range_proof/mod.rs index 9022ccc31..86754dbf6 100644 --- a/zk-token-sdk/src/range_proof/mod.rs +++ b/zk-token-sdk/src/range_proof/mod.rs @@ -82,7 +82,8 @@ impl RangeProof { let nm: usize = bit_lengths.iter().sum(); assert!(nm.is_power_of_two()); - let bp_gens = BulletproofGens::new(nm); + let bp_gens = BulletproofGens::new(nm) + .map_err(|_| RangeProofGenerationError::MaximumGeneratorLengthExceeded)?; // bit-decompose values and generate their Pedersen vector commitment let a_blinding = Scalar::random(&mut OsRng); @@ -241,7 +242,8 @@ impl RangeProof { let m = bit_lengths.len(); let nm: usize = bit_lengths.iter().sum(); - let bp_gens = BulletproofGens::new(nm); + let bp_gens = BulletproofGens::new(nm) + .map_err(|_| RangeProofVerificationError::MaximumGeneratorLengthExceeded)?; if !nm.is_power_of_two() { return Err(RangeProofVerificationError::InvalidBitSize);