Skip to content

Commit 38d67ce

Browse files
authored
chore: implement ConditionallySelectable for ComposedCommitment and ComposedResponse (#83)
Signed-off-by: Michele Orrù <[email protected]>
1 parent 3e7ebf4 commit 38d67ce

File tree

1 file changed

+116
-39
lines changed

1 file changed

+116
-39
lines changed

src/composition.rs

Lines changed: 116 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ pub enum ComposedRelation<G: PrimeGroup> {
5151
Or(Vec<ComposedRelation<G>>),
5252
}
5353

54-
impl<G: PrimeGroup + ConstantTimeEq> ComposedRelation<G> {
54+
impl<G: PrimeGroup + ConstantTimeEq + ConditionallySelectable> ComposedRelation<G> {
5555
/// Create a [ComposedRelation] for an AND relation from the given list of relations.
5656
pub fn and<T: Into<ComposedRelation<G>>>(witness: impl IntoIterator<Item = T>) -> Self {
5757
Self::And(witness.into_iter().map(|x| x.into()).collect())
@@ -85,6 +85,49 @@ pub enum ComposedCommitment<G: PrimeGroup> {
8585
Or(Vec<ComposedCommitment<G>>),
8686
}
8787

88+
impl<G: PrimeGroup> ComposedCommitment<G>
89+
where
90+
G: ConditionallySelectable,
91+
{
92+
/// Conditionally select between two ComposedCommitment values.
93+
/// This function performs constant-time selection of the commitment values.
94+
pub fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
95+
match (a, b) {
96+
(ComposedCommitment::Simple(a_elements), ComposedCommitment::Simple(b_elements)) => {
97+
// Both vectors must have the same length for this to work
98+
debug_assert_eq!(a_elements.len(), b_elements.len());
99+
let selected: Vec<G> = a_elements
100+
.iter()
101+
.zip(b_elements.iter())
102+
.map(|(a, b)| G::conditional_select(a, b, choice))
103+
.collect();
104+
ComposedCommitment::Simple(selected)
105+
}
106+
(ComposedCommitment::And(a_commitments), ComposedCommitment::And(b_commitments)) => {
107+
debug_assert_eq!(a_commitments.len(), b_commitments.len());
108+
let selected: Vec<ComposedCommitment<G>> = a_commitments
109+
.iter()
110+
.zip(b_commitments.iter())
111+
.map(|(a, b)| ComposedCommitment::conditional_select(a, b, choice))
112+
.collect();
113+
ComposedCommitment::And(selected)
114+
}
115+
(ComposedCommitment::Or(a_commitments), ComposedCommitment::Or(b_commitments)) => {
116+
debug_assert_eq!(a_commitments.len(), b_commitments.len());
117+
let selected: Vec<ComposedCommitment<G>> = a_commitments
118+
.iter()
119+
.zip(b_commitments.iter())
120+
.map(|(a, b)| ComposedCommitment::conditional_select(a, b, choice))
121+
.collect();
122+
ComposedCommitment::Or(selected)
123+
}
124+
_ => {
125+
unreachable!("Mismatched ComposedCommitment variants in conditional_select");
126+
}
127+
}
128+
}
129+
}
130+
88131
// Structure representing the ProverState type of Protocol as SigmaProtocol
89132
pub enum ComposedProverState<G: PrimeGroup + ConstantTimeEq> {
90133
Simple(<CanonicalLinearRelation<G> as SigmaProtocol>::ProverState),
@@ -108,6 +151,58 @@ pub enum ComposedResponse<G: PrimeGroup> {
108151
Or(Vec<ComposedChallenge<G>>, Vec<ComposedResponse<G>>),
109152
}
110153

154+
impl<G: PrimeGroup> ComposedResponse<G> {
155+
/// Conditionally select between two ComposedResponse values.
156+
/// This function performs constant-time selection of the response values.
157+
pub fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
158+
match (a, b) {
159+
(ComposedResponse::Simple(a_scalars), ComposedResponse::Simple(b_scalars)) => {
160+
// Both vectors must have the same length for this to work
161+
debug_assert_eq!(a_scalars.len(), b_scalars.len());
162+
let selected: Vec<G::Scalar> = a_scalars
163+
.iter()
164+
.zip(b_scalars.iter())
165+
.map(|(a, b)| G::Scalar::conditional_select(a, b, choice))
166+
.collect();
167+
ComposedResponse::Simple(selected)
168+
}
169+
(ComposedResponse::And(a_responses), ComposedResponse::And(b_responses)) => {
170+
debug_assert_eq!(a_responses.len(), b_responses.len());
171+
let selected: Vec<ComposedResponse<G>> = a_responses
172+
.iter()
173+
.zip(b_responses.iter())
174+
.map(|(a, b)| ComposedResponse::conditional_select(a, b, choice))
175+
.collect();
176+
ComposedResponse::And(selected)
177+
}
178+
(
179+
ComposedResponse::Or(a_challenges, a_responses),
180+
ComposedResponse::Or(b_challenges, b_responses),
181+
) => {
182+
debug_assert_eq!(a_challenges.len(), b_challenges.len());
183+
debug_assert_eq!(a_responses.len(), b_responses.len());
184+
185+
let selected_challenges: Vec<ComposedChallenge<G>> = a_challenges
186+
.iter()
187+
.zip(b_challenges.iter())
188+
.map(|(a, b)| G::Scalar::conditional_select(a, b, choice))
189+
.collect();
190+
191+
let selected_responses: Vec<ComposedResponse<G>> = a_responses
192+
.iter()
193+
.zip(b_responses.iter())
194+
.map(|(a, b)| ComposedResponse::conditional_select(a, b, choice))
195+
.collect();
196+
197+
ComposedResponse::Or(selected_challenges, selected_responses)
198+
}
199+
_ => {
200+
unreachable!("Mismatched ComposedResponse variants in conditional_select");
201+
}
202+
}
203+
}
204+
}
205+
111206
// Structure representing the Witness type of Protocol as SigmaProtocol
112207
#[derive(Clone)]
113208
pub enum ComposedWitness<G: PrimeGroup> {
@@ -142,7 +237,7 @@ const fn composed_challenge_size<G: PrimeGroup>() -> usize {
142237
(G::Scalar::NUM_BITS as usize).div_ceil(8)
143238
}
144239

145-
impl<G: PrimeGroup + ConstantTimeEq> ComposedRelation<G> {
240+
impl<G: PrimeGroup + ConstantTimeEq + ConditionallySelectable> ComposedRelation<G> {
146241
fn is_witness_valid(&self, witness: &ComposedWitness<G>) -> Choice {
147242
match (self, witness) {
148243
(ComposedRelation::Simple(instance), ComposedWitness::Simple(witness)) => {
@@ -233,7 +328,10 @@ impl<G: PrimeGroup + ConstantTimeEq> ComposedRelation<G> {
233328
instances: &[ComposedRelation<G>],
234329
witnesses: &[ComposedWitness<G>],
235330
rng: &mut (impl Rng + CryptoRng),
236-
) -> Result<(ComposedCommitment<G>, ComposedProverState<G>), Error> {
331+
) -> Result<(ComposedCommitment<G>, ComposedProverState<G>), Error>
332+
where
333+
G: ConditionallySelectable,
334+
{
237335
if instances.len() != witnesses.len() {
238336
return Err(Error::InvalidInstanceWitnessPair);
239337
}
@@ -248,28 +346,16 @@ impl<G: PrimeGroup + ConstantTimeEq> ComposedRelation<G> {
248346
let (simulated_commitment, simulated_challenge, simulated_response) =
249347
instances[i].simulate_transcript(rng)?;
250348

251-
let valid_witness = instances[i].is_witness_valid(w);
252-
let select_witness = valid_witness & !valid_witness_found;
253-
254-
let simulated_commitment_ptr =
255-
&simulated_commitment as *const ComposedCommitment<G> as u64;
256-
let commitment_ptr = &commitment as *const ComposedCommitment<G> as u64;
349+
let valid_witness = instances[i].is_witness_valid(w) & !valid_witness_found;
350+
let select_witness = valid_witness;
257351

258-
let selected_commitment_ptr = ConditionallySelectable::conditional_select(
259-
&simulated_commitment_ptr,
260-
&commitment_ptr,
352+
let commitment = ComposedCommitment::conditional_select(
353+
&simulated_commitment,
354+
&commitment,
261355
select_witness,
262356
);
263-
let discarded_commitment_ptr = ConditionallySelectable::conditional_select(
264-
&simulated_commitment_ptr,
265-
&commitment_ptr,
266-
!select_witness,
267-
);
268-
let commitment = unsafe { &*(selected_commitment_ptr as *const ComposedCommitment<G>) };
269-
let _discarded =
270-
unsafe { &*(discarded_commitment_ptr as *const ComposedCommitment<G>) };
271357

272-
commitments.push(commitment.clone());
358+
commitments.push(commitment);
273359
prover_states.push(ComposedOrProverStateEntry(
274360
select_witness,
275361
prover_state,
@@ -330,21 +416,8 @@ impl<G: PrimeGroup + ConstantTimeEq> ComposedRelation<G> {
330416
);
331417

332418
let response = instance.prover_response(prover_state, &challenge_i)?;
333-
let response_ptr = &response as *const ComposedResponse<G> as u64;
334-
let simulated_response_ptr = &simulated_response as *const ComposedResponse<G> as u64;
335-
let selected_response_ptr = ConditionallySelectable::conditional_select(
336-
&simulated_response_ptr,
337-
&response_ptr,
338-
valid_witness,
339-
);
340-
let _discarded_response_ptr = ConditionallySelectable::conditional_select(
341-
&simulated_response_ptr,
342-
&response_ptr,
343-
!valid_witness,
344-
);
345-
let response = unsafe { &*(selected_response_ptr as *const ComposedResponse<G>) };
346-
let _discarded_response =
347-
unsafe { &*(_discarded_response_ptr as *const ComposedResponse<G>) };
419+
let response =
420+
ComposedResponse::conditional_select(&simulated_response, &response, valid_witness);
348421

349422
result_challenges.push(challenge_i);
350423
result_responses.push(response.clone());
@@ -355,7 +428,9 @@ impl<G: PrimeGroup + ConstantTimeEq> ComposedRelation<G> {
355428
}
356429
}
357430

358-
impl<G: PrimeGroup + ConstantTimeEq> SigmaProtocol for ComposedRelation<G> {
431+
impl<G: PrimeGroup + ConstantTimeEq + ConditionallySelectable> SigmaProtocol
432+
for ComposedRelation<G>
433+
{
359434
type Commitment = ComposedCommitment<G>;
360435
type ProverState = ComposedProverState<G>;
361436
type Response = ComposedResponse<G>;
@@ -607,7 +682,9 @@ impl<G: PrimeGroup + ConstantTimeEq> SigmaProtocol for ComposedRelation<G> {
607682
}
608683
}
609684

610-
impl<G: PrimeGroup + ConstantTimeEq> SigmaProtocolSimulator for ComposedRelation<G> {
685+
impl<G: PrimeGroup + ConstantTimeEq + ConditionallySelectable> SigmaProtocolSimulator
686+
for ComposedRelation<G>
687+
{
611688
fn simulate_commitment(
612689
&self,
613690
challenge: &Self::Challenge,
@@ -712,7 +789,7 @@ impl<G: PrimeGroup + ConstantTimeEq> SigmaProtocolSimulator for ComposedRelation
712789
}
713790
}
714791

715-
impl<G: PrimeGroup + ConstantTimeEq> ComposedRelation<G> {
792+
impl<G: PrimeGroup + ConstantTimeEq + ConditionallySelectable> ComposedRelation<G> {
716793
/// Convert this Protocol into a non-interactive zero-knowledge proof
717794
/// using the Shake128DuplexSponge codec and a specified session identifier.
718795
///

0 commit comments

Comments
 (0)