Skip to content

Commit 33793c8

Browse files
Merge pull request #15 from Chausseaumoine/feat/absorb-morphism-transcript
feat(fiat-shamir): absorb morphism structure into transcript codec
2 parents 0e2476c + b7c200f commit 33793c8

File tree

5 files changed

+104
-6
lines changed

5 files changed

+104
-6
lines changed

src/codec/shake_codec.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use crate::codec::r#trait::Codec;
2727
///
2828
/// The codec is initialized with a domain separator and absorbs serialized
2929
/// group elements. It outputs challenges compatible with the group’s scalar field.
30-
#[derive(Clone)]
30+
#[derive(Clone, Debug)]
3131
pub struct ShakeCodec<G: Group> {
3232
/// Internal SHAKE128 hasher state.
3333
hasher: Shake128,

src/fiat_shamir.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414
1515
use crate::codec::Codec;
1616
use crate::errors::Error;
17+
use crate::group_morphism::HasGroupMorphism;
1718
use crate::traits::{CompactProtocol, SigmaProtocol};
1819

20+
use group::{Group, GroupEncoding};
1921
use rand::{CryptoRng, RngCore};
2022

2123
pub trait FiatShamir<C: Codec>: SigmaProtocol {
@@ -41,6 +43,7 @@ type Transcript<P> = (
4143
/// # Type Parameters
4244
/// - `P`: the Sigma protocol implementation.
4345
/// - `C`: the codec used for Fiat-Shamir.
46+
#[derive(Debug)]
4447
pub struct NISigmaProtocol<P, C>
4548
where
4649
P: SigmaProtocol<Challenge: PartialEq> + FiatShamir<C>,
@@ -243,3 +246,16 @@ where
243246
self.verify(&commitment, &challenge, &response)
244247
}
245248
}
249+
250+
impl<P, C> NISigmaProtocol<P, C>
251+
where
252+
P: SigmaProtocol<Challenge = <P::Group as Group>::Scalar> + HasGroupMorphism + FiatShamir<C>,
253+
P::Challenge: PartialEq,
254+
P::Group: Group + GroupEncoding,
255+
C: Codec<Challenge = P::Challenge> + Clone,
256+
{
257+
/// Absorbs the morphism structure into the transcript codec.
258+
pub fn absorb_morphism(&self, codec: &mut C) -> Result<(), Error> {
259+
self.sigmap.absorb_morphism_structure(codec)
260+
}
261+
}

src/group_morphism.rs

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
1111
use std::iter;
1212

13+
use crate::codec::Codec;
1314
use crate::errors::Error;
1415
use group::{Group, GroupEncoding};
1516

@@ -47,6 +48,15 @@ pub struct Term {
4748
elem: GroupVar,
4849
}
4950

51+
impl Term {
52+
pub fn scalar(&self) -> ScalarVar {
53+
self.scalar
54+
}
55+
pub fn elem(&self) -> GroupVar {
56+
self.elem
57+
}
58+
}
59+
5060
impl From<(ScalarVar, GroupVar)> for Term {
5161
fn from((scalar, elem): (ScalarVar, GroupVar)) -> Self {
5262
Self { scalar, elem }
@@ -61,9 +71,15 @@ impl From<(ScalarVar, GroupVar)> for Term {
6171
/// where `s_i` are scalars (referenced by `scalar_vars`) and `P_i` are group elements (referenced by `element_vars`).
6272
///
6373
/// The indices refer to external lists managed by the containing Morphism.
64-
#[derive(Clone)]
74+
#[derive(Clone, Debug)]
6575
pub struct LinearCombination(Vec<Term>);
6676

77+
impl LinearCombination {
78+
pub fn terms(&self) -> &[Term] {
79+
&self.0
80+
}
81+
}
82+
6783
impl<T: Into<Term>> From<T> for LinearCombination {
6884
fn from(term: T) -> Self {
6985
Self(vec![term.into()])
@@ -169,7 +185,7 @@ impl<G: Group> FromIterator<(GroupVar, G)> for GroupMap<G> {
169185
///
170186
/// It supports dynamic allocation of scalars and elements,
171187
/// and evaluates by performing multi-scalar multiplications.
172-
#[derive(Clone, Default)]
188+
#[derive(Clone, Default, Debug)]
173189
pub struct Morphism<G: Group> {
174190
/// The set of linear combination constraints (equations).
175191
pub constraints: Vec<LinearCombination>,
@@ -260,7 +276,7 @@ impl<G: Group> Morphism<G> {
260276
/// Internally, the constraint system is defined through:
261277
/// - A list of group elements and linear equations (held in the [`Morphism`] field),
262278
/// - A list of [`GroupVar`] indices (`image`) that specify the expected output for each constraint.
263-
#[derive(Clone, Default)]
279+
#[derive(Clone, Default, Debug)]
264280
pub struct GroupMorphismPreimage<G>
265281
where
266282
G: Group + GroupEncoding,
@@ -444,3 +460,24 @@ where
444460
.collect()
445461
}
446462
}
463+
464+
/// Trait for accessing the underlying group morphism in a Sigma protocol.
465+
pub trait HasGroupMorphism {
466+
type Group: Group + GroupEncoding;
467+
fn group_morphism(&self) -> &GroupMorphismPreimage<Self::Group>;
468+
469+
/// Absorbs the morphism structure into a codec.
470+
/// Only compatible with 64-bit platforms
471+
fn absorb_morphism_structure<C: Codec>(&self, codec: &mut C) -> Result<(), Error> {
472+
let morphism = self.group_morphism();
473+
for lc in &morphism.morphism.constraints {
474+
for term in lc.terms() {
475+
let mut buf = [0u8; 16];
476+
buf[..8].copy_from_slice(&(term.scalar().index() as u64).to_le_bytes());
477+
buf[8..].copy_from_slice(&(term.elem().index() as u64).to_le_bytes());
478+
codec.prover_message(&buf);
479+
}
480+
}
481+
Ok(())
482+
}
483+
}

src/schnorr_protocol.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
use crate::codec::Codec;
88
use crate::errors::Error;
99
use crate::fiat_shamir::FiatShamir;
10-
use crate::group_morphism::GroupMorphismPreimage;
10+
use crate::group_morphism::{GroupMorphismPreimage, HasGroupMorphism};
1111
use crate::{
1212
group_serialization::*,
1313
traits::{CompactProtocol, SigmaProtocol, SigmaProtocolSimulator},
@@ -24,7 +24,7 @@ use rand::{CryptoRng, RngCore};
2424
///
2525
/// # Type Parameters
2626
/// - `G`: A cryptographic group implementing [`Group`] and [`GroupEncoding`].
27-
#[derive(Clone, Default)]
27+
#[derive(Clone, Default, Debug)]
2828
pub struct SchnorrProtocol<G: Group + GroupEncoding>(pub GroupMorphismPreimage<G>);
2929

3030
impl<G: Group + GroupEncoding> SchnorrProtocol<G> {
@@ -429,3 +429,10 @@ where
429429
Ok(codec.verifier_challenge())
430430
}
431431
}
432+
433+
impl<G: Group + GroupEncoding> HasGroupMorphism for SchnorrProtocol<G> {
434+
type Group = G;
435+
fn group_morphism(&self) -> &GroupMorphismPreimage<G> {
436+
&self.0
437+
}
438+
}

tests/morphism_preimage.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use group::{Group, ff::Field};
33
use rand::rngs::OsRng;
44

55
use sigma_rs::fiat_shamir::NISigmaProtocol;
6+
use sigma_rs::group_morphism::HasGroupMorphism;
67
use sigma_rs::test_utils::{
78
bbs_blind_commitment_computation, discrete_logarithm, dleq, pedersen_commitment,
89
pedersen_commitment_dleq,
@@ -97,6 +98,43 @@ fn noninteractive_discrete_logarithm() {
9798
);
9899
}
99100

101+
/// This part tests the implementation of the SigmaProtocol trait for the
102+
/// SchnorrProtocol structure as well as the Fiat-Shamir NISigmaProtocol transform,
103+
/// with additional morphism structure absorption into the transcript.
104+
#[test]
105+
fn noninteractive_discrete_logarithm_with_morphism_transcript() {
106+
let mut rng = OsRng;
107+
let (morphismp, witness) = discrete_logarithm(Scalar::random(&mut rng));
108+
109+
// The SigmaProtocol induced by morphismp
110+
let protocol = SchnorrProtocol::from(morphismp);
111+
112+
// Fiat-Shamir wrapper
113+
let domain_sep = b"test-fiat-shamir-schnorr";
114+
let mut nizk = NISigmaProtocol::<SchnorrProtocol<G>, ShakeCodec<G>>::new(domain_sep, protocol);
115+
116+
// Morphism absorption
117+
nizk.sigmap
118+
.absorb_morphism_structure(&mut nizk.hash_state)
119+
.unwrap();
120+
121+
// Generate and verify proofs
122+
let proof_batchable_bytes = nizk.prove_batchable(&witness, &mut rng).unwrap();
123+
let proof_compact_bytes = nizk.prove_compact(&witness, &mut rng).unwrap();
124+
125+
let verified_batchable = nizk.verify_batchable(&proof_batchable_bytes).is_ok();
126+
let verified_compact = nizk.verify_compact(&proof_compact_bytes).is_ok();
127+
128+
assert!(
129+
verified_batchable,
130+
"Fiat-Shamir Schnorr proof with morphism absorption failed (batchable)"
131+
);
132+
assert!(
133+
verified_compact,
134+
"Fiat-Shamir Schnorr proof with morphism absorption failed (compact)"
135+
);
136+
}
137+
100138
#[test]
101139
fn noninteractive_dleq() {
102140
let mut rng = OsRng;

0 commit comments

Comments
 (0)