Skip to content

Commit 41fa258

Browse files
committed
fix: update test vectors, now matching latest spec.
1 parent 8c7e50e commit 41fa258

18 files changed

+405
-485
lines changed

src/codec.rs

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,25 @@ fn length_to_bytes(x: usize) -> [u8; WORD_SIZE] {
5959
(x as u32).to_be_bytes()
6060
}
6161

62+
/// Compute the initialization vector (IV) for a protocol instance.
63+
///
64+
/// This function computes a deterministic IV from the protocol identifier,
65+
/// session identifier, and instance label using the specified duplex sponge.
66+
pub fn compute_iv<H: DuplexSpongeInterface>(
67+
protocol_id: &[u8],
68+
session_id: &[u8],
69+
instance_label: &[u8],
70+
) -> [u8; 32] {
71+
let mut tmp = H::new([0u8; 32]);
72+
tmp.absorb(&length_to_bytes(protocol_id.len()));
73+
tmp.absorb(protocol_id);
74+
tmp.absorb(&length_to_bytes(session_id.len()));
75+
tmp.absorb(session_id);
76+
tmp.absorb(&length_to_bytes(instance_label.len()));
77+
tmp.absorb(instance_label);
78+
tmp.squeeze(32).try_into().unwrap()
79+
}
80+
6281
impl<G, H> Codec for ByteSchnorrCodec<G, H>
6382
where
6483
G: PrimeGroup,
@@ -67,17 +86,7 @@ where
6786
type Challenge = G::Scalar;
6887

6988
fn new(protocol_id: &[u8], session_id: &[u8], instance_label: &[u8]) -> Self {
70-
let iv = {
71-
let mut tmp = H::new([0u8; 32]);
72-
tmp.absorb(&length_to_bytes(protocol_id.len()));
73-
tmp.absorb(protocol_id);
74-
tmp.absorb(&length_to_bytes(session_id.len()));
75-
tmp.absorb(session_id);
76-
tmp.absorb(&length_to_bytes(instance_label.len()));
77-
tmp.absorb(instance_label);
78-
tmp.squeeze(32).try_into().unwrap()
79-
};
80-
89+
let iv = compute_iv::<H>(protocol_id, session_id, instance_label);
8190
Self::from_iv(iv)
8291
}
8392

src/composition.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -234,9 +234,11 @@ impl<G: PrimeGroup> SigmaProtocol for ComposedRelation<G> {
234234
response: &Self::Response,
235235
) -> Result<(), Error> {
236236
match (self, commitment, response) {
237-
(ComposedRelation::Simple(p), ProtocolCommitment::Simple(c), ProtocolResponse::Simple(r)) => {
238-
p.verifier(c, challenge, r)
239-
}
237+
(
238+
ComposedRelation::Simple(p),
239+
ProtocolCommitment::Simple(c),
240+
ProtocolResponse::Simple(r),
241+
) => p.verifier(c, challenge, r),
240242
(
241243
ComposedRelation::And(ps),
242244
ProtocolCommitment::And(commitments),
@@ -267,7 +269,9 @@ impl<G: PrimeGroup> SigmaProtocol for ComposedRelation<G> {
267269

268270
fn serialize_commitment(&self, commitment: &Self::Commitment) -> Vec<u8> {
269271
match (self, commitment) {
270-
(ComposedRelation::Simple(p), ProtocolCommitment::Simple(c)) => p.serialize_commitment(c),
272+
(ComposedRelation::Simple(p), ProtocolCommitment::Simple(c)) => {
273+
p.serialize_commitment(c)
274+
}
271275
(ComposedRelation::And(ps), ProtocolCommitment::And(commitments))
272276
| (ComposedRelation::Or(ps), ProtocolCommitment::Or(commitments)) => ps
273277
.iter()
@@ -432,9 +436,9 @@ impl<G: PrimeGroup> SigmaProtocolSimulator for ComposedRelation<G> {
432436
response: &Self::Response,
433437
) -> Result<Self::Commitment, Error> {
434438
match (self, response) {
435-
(ComposedRelation::Simple(p), ProtocolResponse::Simple(r)) => Ok(ProtocolCommitment::Simple(
436-
p.simulate_commitment(challenge, r)?,
437-
)),
439+
(ComposedRelation::Simple(p), ProtocolResponse::Simple(r)) => Ok(
440+
ProtocolCommitment::Simple(p.simulate_commitment(challenge, r)?),
441+
),
438442
(ComposedRelation::And(ps), ProtocolResponse::And(rs)) => {
439443
let commitments = ps
440444
.iter()

src/errors.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,29 @@
88
//! - Mismatched parameter lengths (e.g., during batch verification),
99
//! - Access to unassigned group variables in constraint systems.
1010
11+
/// Represents an invalid instance error.
12+
#[derive(Debug, thiserror::Error)]
13+
#[error("Invalid instance: {message}")]
14+
pub struct InvalidInstance {
15+
/// The error message describing what's invalid about the instance.
16+
pub message: String,
17+
}
18+
19+
impl InvalidInstance {
20+
/// Create a new InvalidInstance error with the given message.
21+
pub fn new(message: impl Into<String>) -> Self {
22+
Self {
23+
message: message.into(),
24+
}
25+
}
26+
}
27+
28+
impl From<InvalidInstance> for Error {
29+
fn from(_err: InvalidInstance) -> Self {
30+
Error::InvalidInstanceWitnessPair
31+
}
32+
}
33+
1134
/// Represents an error encountered during the execution of a Sigma protocol.
1235
///
1336
/// This may occur during proof generation, response computation, or verification.

src/linear_relation/mod.rs

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,154 @@ impl<G: PrimeGroup> CanonicalLinearRelation<G> {
510510

511511
out
512512
}
513+
514+
/// Parse a canonical linear relation from its label representation
515+
pub fn from_label(data: &[u8]) -> Result<Self, Error> {
516+
use crate::errors::InvalidInstance;
517+
use crate::serialization::group_elt_serialized_len;
518+
519+
let mut offset = 0;
520+
521+
// Read number of equations (4 bytes, little endian)
522+
if data.len() < 4 {
523+
return Err(InvalidInstance::new("Invalid label: too short for equation count").into());
524+
}
525+
let num_equations = u32::from_le_bytes([data[0], data[1], data[2], data[3]]) as usize;
526+
offset += 4;
527+
528+
// Parse constraints and collect unique group element indices
529+
let mut constraint_data = Vec::new();
530+
let mut max_scalar_index = 0u32;
531+
let mut max_group_index = 0u32;
532+
533+
for _ in 0..num_equations {
534+
// Read LHS index (4 bytes)
535+
if offset + 4 > data.len() {
536+
return Err(InvalidInstance::new("Invalid label: truncated LHS index").into());
537+
}
538+
let lhs_index = u32::from_le_bytes([
539+
data[offset],
540+
data[offset + 1],
541+
data[offset + 2],
542+
data[offset + 3],
543+
]);
544+
offset += 4;
545+
max_group_index = max_group_index.max(lhs_index);
546+
547+
// Read number of RHS terms (4 bytes)
548+
if offset + 4 > data.len() {
549+
return Err(InvalidInstance::new("Invalid label: truncated RHS count").into());
550+
}
551+
let num_rhs_terms = u32::from_le_bytes([
552+
data[offset],
553+
data[offset + 1],
554+
data[offset + 2],
555+
data[offset + 3],
556+
]) as usize;
557+
offset += 4;
558+
559+
// Read RHS terms
560+
let mut rhs_terms = Vec::new();
561+
for _ in 0..num_rhs_terms {
562+
// Read scalar index (4 bytes)
563+
if offset + 4 > data.len() {
564+
return Err(
565+
InvalidInstance::new("Invalid label: truncated scalar index").into(),
566+
);
567+
}
568+
let scalar_index = u32::from_le_bytes([
569+
data[offset],
570+
data[offset + 1],
571+
data[offset + 2],
572+
data[offset + 3],
573+
]);
574+
offset += 4;
575+
max_scalar_index = max_scalar_index.max(scalar_index);
576+
577+
// Read group index (4 bytes)
578+
if offset + 4 > data.len() {
579+
return Err(InvalidInstance::new("Invalid label: truncated group index").into());
580+
}
581+
let group_index = u32::from_le_bytes([
582+
data[offset],
583+
data[offset + 1],
584+
data[offset + 2],
585+
data[offset + 3],
586+
]);
587+
offset += 4;
588+
max_group_index = max_group_index.max(group_index);
589+
590+
rhs_terms.push((scalar_index, group_index));
591+
}
592+
593+
constraint_data.push((lhs_index, rhs_terms));
594+
}
595+
596+
// Calculate expected number of group elements
597+
let num_group_elements = (max_group_index + 1) as usize;
598+
let group_element_size = group_elt_serialized_len::<G>();
599+
let expected_remaining = num_group_elements * group_element_size;
600+
601+
if data.len() - offset != expected_remaining {
602+
return Err(InvalidInstance::new(format!(
603+
"Invalid label: expected {} bytes for {} group elements, got {}",
604+
expected_remaining,
605+
num_group_elements,
606+
data.len() - offset
607+
))
608+
.into());
609+
}
610+
611+
// Parse group elements
612+
let mut group_elements_ordered = Vec::new();
613+
for i in 0..num_group_elements {
614+
let start = offset + i * group_element_size;
615+
let end = start + group_element_size;
616+
let elem_bytes = &data[start..end];
617+
618+
let mut repr = G::Repr::default();
619+
repr.as_mut().copy_from_slice(elem_bytes);
620+
621+
let elem = Option::<G>::from(G::from_bytes(&repr)).ok_or_else(|| {
622+
Error::from(InvalidInstance::new(format!(
623+
"Invalid group element at index {}",
624+
i
625+
)))
626+
})?;
627+
628+
group_elements_ordered.push(elem);
629+
}
630+
631+
// Build the canonical relation
632+
let mut canonical = Self::new();
633+
canonical.num_scalars = (max_scalar_index + 1) as usize;
634+
635+
// Add all group elements to the map
636+
let mut group_var_map = Vec::new();
637+
for elem in &group_elements_ordered {
638+
let var = canonical.group_elements.push(*elem);
639+
group_var_map.push(var);
640+
}
641+
642+
// Build constraints
643+
for (lhs_index, rhs_terms) in constraint_data {
644+
// Add image element
645+
canonical
646+
.image
647+
.push(group_elements_ordered[lhs_index as usize]);
648+
649+
// Build linear combination
650+
let mut linear_combination = Vec::new();
651+
for (scalar_index, group_index) in rhs_terms {
652+
let scalar_var = ScalarVar(scalar_index as usize, PhantomData);
653+
let group_var = group_var_map[group_index as usize];
654+
linear_combination.push((scalar_var, group_var));
655+
}
656+
canonical.linear_combinations.push(linear_combination);
657+
}
658+
659+
Ok(canonical)
660+
}
513661
}
514662

515663
impl<G: PrimeGroup> TryFrom<&LinearRelation<G>> for CanonicalLinearRelation<G> {

src/schnorr_protocol.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ where
311311
}
312312

313313
fn protocol_identifier(&self) -> impl AsRef<[u8]> {
314-
b"SchnorrProof"
314+
b"draft-zkproof-fiat-shamir"
315315
}
316316
}
317317

src/serialization.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@
66
use ff::PrimeField;
77
use group::prime::PrimeGroup;
88

9+
/// Get the serialized length of a group element in bytes.
10+
///
11+
/// # Returns
12+
/// The number of bytes required to serialize a group element.
13+
pub fn group_elt_serialized_len<G: PrimeGroup>() -> usize {
14+
G::Repr::default().as_ref().len()
15+
}
16+
917
/// Serialize a slice of group elements into a byte vector.
1018
///
1119
/// # Parameters
@@ -31,7 +39,7 @@ pub fn serialize_elements<G: PrimeGroup>(elements: &[G]) -> Vec<u8> {
3139
/// - `Some(Vec<G>)`: The deserialized group elements if all are valid.
3240
/// - `None`: If the byte slice length is incorrect or any element is invalid.
3341
pub fn deserialize_elements<G: PrimeGroup>(data: &[u8], count: usize) -> Option<Vec<G>> {
34-
let element_len = G::Repr::default().as_ref().len();
42+
let element_len = group_elt_serialized_len::<G>();
3543
let expected_len = count * element_len;
3644

3745
if data.len() < expected_len {

src/tests/composition.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ use curve25519_dalek::ristretto::RistrettoPoint;
22
use rand::rngs::OsRng;
33

44
use super::test_utils::{
5-
bbs_blind_commitment_computation, discrete_logarithm, dleq, pedersen_commitment,
6-
pedersen_commitment_dleq,
5+
bbs_blind_commitment, discrete_logarithm, dleq, pedersen_commitment, pedersen_commitment_dleq,
76
};
87
use crate::codec::Shake128DuplexSponge;
98
use crate::composition::{ComposedRelation, ProtocolWitness};
@@ -30,7 +29,7 @@ fn composition_proof_correct() {
3029
let (relation2, _) = pedersen_commitment::<G, _>(&mut rng);
3130
let (relation3, witness3) = discrete_logarithm::<G, _>(&mut rng);
3231
let (relation4, witness4) = pedersen_commitment_dleq::<G, _>(&mut rng);
33-
let (relation5, witness5) = bbs_blind_commitment_computation::<G, _>(&mut rng);
32+
let (relation5, witness5) = bbs_blind_commitment::<G, _>(&mut rng);
3433

3534
// second layer protocol definitions
3635
let or_protocol1 = ComposedRelation::Or(vec![
@@ -55,7 +54,9 @@ fn composition_proof_correct() {
5554
let protocol = ComposedRelation::And(vec![or_protocol1, simple_protocol1, and_protocol1]);
5655
let witness = ProtocolWitness::And(vec![or_witness1, simple_witness1, and_witness1]);
5756

58-
let nizk = Nizk::<ComposedRelation<RistrettoPoint>, Shake128DuplexSponge<G>>::new(domain_sep, protocol);
57+
let nizk = Nizk::<ComposedRelation<RistrettoPoint>, Shake128DuplexSponge<G>>::new(
58+
domain_sep, protocol,
59+
);
5960

6061
// Batchable and compact proofs
6162
let proof_batchable_bytes = nizk.prove_batchable(&witness, &mut OsRng).unwrap();

src/tests/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
mod composition;
22
mod relations;
33
mod spec;
4-
pub mod test_utils;
4+
pub mod test_utils;

src/tests/relations.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@ use rand::rngs::OsRng;
33

44
use crate::fiat_shamir::Nizk;
55
use crate::tests::test_utils::{
6-
bbs_blind_commitment_computation, discrete_logarithm, dleq, pedersen_commitment,
7-
pedersen_commitment_dleq, shifted_discrete_logarithm, shifted_dleq,
8-
user_specific_linear_combination,
6+
bbs_blind_commitment, discrete_logarithm, dleq, pedersen_commitment, pedersen_commitment_dleq,
7+
shifted_discrete_logarithm, shifted_dleq, user_specific_linear_combination,
98
};
109
use crate::{
1110
codec::Shake128DuplexSponge, linear_relation::CanonicalLinearRelation,
@@ -99,7 +98,7 @@ fn test_pedersen_commitment_dleq() {
9998
#[test]
10099
fn test_bbs_blind_commitment_computation() {
101100
test_relation_and_nizk(
102-
|rng| bbs_blind_commitment_computation::<G, _>(rng),
101+
|rng| bbs_blind_commitment::<G, _>(rng),
103102
"bbs-blind-commitment-computation",
104103
);
105104
}

src/tests/spec/bls12_381.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,4 @@ impl SRandom for G1Projective {
6161
}
6262
G1Projective::scalar_from_hex_be(&hex_string).unwrap()
6363
}
64-
6564
}

0 commit comments

Comments
 (0)