Skip to content

Commit

Permalink
v1.17: [zk-token-sdk] Restrict range proof generator length and preve…
Browse files Browse the repository at this point in the history
…nt 0-bit range proof (backport of #34166) (#34183)

* [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

(cherry picked from commit 0e6dd54)

# Conflicts:
#	zk-token-sdk/src/range_proof/errors.rs
#	zk-token-sdk/src/range_proof/generators.rs
#	zk-token-sdk/src/range_proof/mod.rs

* resolve conflict

---------

Co-authored-by: samkim-crypto <skim13@cs.stanford.edu>
  • Loading branch information
mergify[bot] and samkim-crypto committed Nov 21, 2023
1 parent 7800f0e commit 6bc02d5
Show file tree
Hide file tree
Showing 12 changed files with 70 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ impl BatchedRangeProofU128Data {
BatchedRangeProofContext::new(&commitments, &amounts, &bit_lengths, &openings)?;

let mut transcript = context.new_transcript();
let proof = RangeProof::new(amounts, bit_lengths, openings, &mut transcript).try_into()?;
let proof = RangeProof::new(amounts, bit_lengths, openings, &mut transcript)
.map_err(|_| ProofError::Generation)?
.try_into()?;

Ok(Self { context, proof })
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ impl BatchedRangeProofU256Data {
BatchedRangeProofContext::new(&commitments, &amounts, &bit_lengths, &openings)?;

let mut transcript = context.new_transcript();
let proof = RangeProof::new(amounts, bit_lengths, openings, &mut transcript).try_into()?;
let proof = RangeProof::new(amounts, bit_lengths, openings, &mut transcript)
.map_err(|_| ProofError::Generation)?
.try_into()?;

Ok(Self { context, proof })
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ impl BatchedRangeProofU64Data {
BatchedRangeProofContext::new(&commitments, &amounts, &bit_lengths, &openings)?;

let mut transcript = context.new_transcript();
let proof = RangeProof::new(amounts, bit_lengths, openings, &mut transcript).try_into()?;
let proof = RangeProof::new(amounts, bit_lengths, openings, &mut transcript)
.map_err(|_| ProofError::Generation)?
.try_into()?;

Ok(Self { context, proof })
}
Expand Down
1 change: 1 addition & 0 deletions zk-token-sdk/src/instruction/range_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ impl RangeProofU64Data {
let bit_size = usize::try_from(u64::BITS).unwrap();

let proof = RangeProof::new(vec![amount], vec![bit_size], vec![opening], &mut transcript)
.map_err(|_| ProofError::Generation)?
.try_into()?;

Ok(Self { context, proof })
Expand Down
3 changes: 2 additions & 1 deletion zk-token-sdk/src/instruction/transfer/with_fee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,8 @@ impl TransferWithFeeProof {
opening_fee_hi,
],
transcript,
);
)
.expect("range proof: generator");

Self {
new_source_commitment: pod_new_source_commitment,
Expand Down
2 changes: 2 additions & 0 deletions zk-token-sdk/src/instruction/transfer/without_fee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ impl TransferProof {
vec![&source_opening, opening_lo, opening_hi],
transcript,
)
.expect("range proof: generator")
} else {
let transfer_amount_lo_negated =
(1 << TRANSFER_AMOUNT_LO_NEGATED_BITS) - 1 - transfer_amount_lo;
Expand All @@ -354,6 +355,7 @@ impl TransferProof {
vec![&source_opening, opening_lo, &opening_lo_negated, opening_hi],
transcript,
)
.expect("range proof: generator")
};

Self {
Expand Down
3 changes: 2 additions & 1 deletion zk-token-sdk/src/instruction/withdraw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@ impl WithdrawProof {
);

let range_proof =
RangeProof::new(vec![final_balance], vec![64], vec![&opening], transcript);
RangeProof::new(vec![final_balance], vec![64], vec![&opening], transcript)
.expect("range proof: generator");

Self {
commitment: pod_commitment,
Expand Down
12 changes: 12 additions & 0 deletions zk-token-sdk/src/range_proof/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,15 @@ use {
#[error("range proof verification failed: {0}")]
pub struct RangeProofError(#[from] pub(crate) ProofVerificationError);
impl_from_transcript_error!(RangeProofError);

#[derive(Error, Clone, Debug, Eq, PartialEq)]
pub enum RangeProofGenerationError {
#[error("maximum generator length exceeded")]
MaximumGeneratorLengthExceeded,
}

#[derive(Error, Clone, Debug, Eq, PartialEq)]
pub enum RangeProofGeneratorError {
#[error("maximum generator length exceeded")]
MaximumGeneratorLengthExceeded,
}
28 changes: 19 additions & 9 deletions zk-token-sdk/src/range_proof/generators.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
use {
crate::range_proof::errors::RangeProofGeneratorError,
curve25519_dalek::{
digest::{ExtendableOutput, Update, XofReader},
ristretto::RistrettoPoint,
},
sha3::{Sha3XofReader, Shake256},
};

#[cfg(not(target_os = "solana"))]
const MAX_GENERATOR_LENGTH: usize = u32::MAX as usize;

/// Generators for Pedersen vector commitments.
///
/// The code is copied from https://github.com/dalek-cryptography/bulletproofs for now...

struct GeneratorsChain {
reader: Sha3XofReader,
}
Expand Down Expand Up @@ -70,14 +73,14 @@ pub struct BulletproofGens {
}

impl BulletproofGens {
pub fn new(gens_capacity: usize) -> Self {
pub fn new(gens_capacity: usize) -> Result<Self, RangeProofGeneratorError> {
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)
}

// pub fn new_aggregate(gens_capacities: Vec<usize>) -> Vec<BulletproofGens> {
Expand All @@ -90,25 +93,32 @@ impl BulletproofGens {

/// 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)]
Expand Down
4 changes: 2 additions & 2 deletions zk-token-sdk/src/range_proof/inner_product.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ impl InnerProductProof {
transcript: &mut Transcript,
) -> Result<(Vec<Scalar>, Vec<Scalar>, Vec<Scalar>), RangeProofError> {
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<<lg_n below.
return Err(ProofVerificationError::InvalidBitSize.into());
Expand Down Expand Up @@ -411,7 +411,7 @@ mod tests {
fn test_basic_correctness() {
let n = 32;

let bp_gens = BulletproofGens::new(n);
let bp_gens = BulletproofGens::new(n).unwrap();
let G: Vec<RistrettoPoint> = bp_gens.G(n).cloned().collect();
let H: Vec<RistrettoPoint> = bp_gens.H(n).cloned().collect();

Expand Down
24 changes: 14 additions & 10 deletions zk-token-sdk/src/range_proof/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ use {
encryption::pedersen::{G, H},
errors::ProofVerificationError,
range_proof::{
errors::RangeProofError, generators::BulletproofGens, inner_product::InnerProductProof,
errors::{RangeProofError, RangeProofGenerationError},
generators::BulletproofGens,
inner_product::InnerProductProof,
},
transcript::TranscriptProtocol,
},
Expand Down Expand Up @@ -59,7 +61,7 @@ impl RangeProof {
bit_lengths: Vec<usize>,
openings: Vec<&PedersenOpening>,
transcript: &mut Transcript,
) -> Self {
) -> Result<Self, RangeProofGenerationError> {
// amounts, bit-lengths, openings must be same length vectors
let m = amounts.len();
assert_eq!(bit_lengths.len(), m);
Expand All @@ -69,9 +71,8 @@ impl RangeProof {
let nm: usize = bit_lengths.iter().sum();
assert!(nm.is_power_of_two());

// TODO: precompute generators
// TODO: double check Pedersen generators and range proof generators does not interfere
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);
Expand Down Expand Up @@ -206,7 +207,7 @@ impl RangeProof {
transcript,
);

RangeProof {
Ok(RangeProof {
A,
S,
T_1,
Expand All @@ -215,7 +216,7 @@ impl RangeProof {
t_x_blinding,
e_blinding,
ipp_proof,
}
})
}

#[allow(clippy::many_single_char_names)]
Expand All @@ -230,7 +231,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(|_| ProofVerificationError::InvalidGeneratorsLength)?;

if !nm.is_power_of_two() {
return Err(ProofVerificationError::InvalidBitSize.into());
Expand Down Expand Up @@ -400,7 +402,8 @@ mod tests {
let mut transcript_create = Transcript::new(b"Test");
let mut transcript_verify = Transcript::new(b"Test");

let proof = RangeProof::new(vec![55], vec![32], vec![&open], &mut transcript_create);
let proof =
RangeProof::new(vec![55], vec![32], vec![&open], &mut transcript_create).unwrap();

assert!(proof
.verify(vec![&comm], vec![32], &mut transcript_verify)
Expand All @@ -421,7 +424,8 @@ mod tests {
vec![64, 32, 32],
vec![&open_1, &open_2, &open_3],
&mut transcript_create,
);
)
.unwrap();

assert!(proof
.verify(
Expand Down
11 changes: 7 additions & 4 deletions zk-token-sdk/src/zk_token_elgamal/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,15 +104,16 @@ mod tests {

let proof = RangeProof::new(vec![55], vec![64], vec![&open], &mut transcript_create);

let proof_serialized: pod::RangeProofU64 = proof.try_into().unwrap();
let proof_serialized: pod::RangeProofU64 = proof.unwrap().try_into().unwrap();
let proof_deserialized: RangeProof = proof_serialized.try_into().unwrap();

assert!(proof_deserialized
.verify(vec![&comm], vec![64], &mut transcript_verify)
.is_ok());

// should fail to serialize to pod::RangeProof128
let proof = RangeProof::new(vec![55], vec![64], vec![&open], &mut transcript_create);
let proof =
RangeProof::new(vec![55], vec![64], vec![&open], &mut transcript_create).unwrap();

assert!(TryInto::<pod::RangeProofU128>::try_into(proof).is_err());
}
Expand All @@ -131,7 +132,8 @@ mod tests {
vec![64, 32, 32],
vec![&open_1, &open_2, &open_3],
&mut transcript_create,
);
)
.unwrap();

let proof_serialized: pod::RangeProofU128 = proof.try_into().unwrap();
let proof_deserialized: RangeProof = proof_serialized.try_into().unwrap();
Expand All @@ -150,7 +152,8 @@ mod tests {
vec![64, 32, 32],
vec![&open_1, &open_2, &open_3],
&mut transcript_create,
);
)
.unwrap();

assert!(TryInto::<pod::RangeProofU64>::try_into(proof).is_err());
}
Expand Down

0 comments on commit 6bc02d5

Please sign in to comment.