Skip to content

Commit 8395a75

Browse files
authored
feat(tests): add labeled non-interactive vector tests (#60)
1 parent 8c7752c commit 8395a75

File tree

8 files changed

+172
-4
lines changed

8 files changed

+172
-4
lines changed

src/codec.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,15 @@ where
5353
_marker: core::marker::PhantomData<G>,
5454
}
5555

56+
const WORD_SIZE: usize = 32;
57+
58+
fn length_to_bytes(x: usize) -> [u8; WORD_SIZE] {
59+
let mut bytes = [0u8; WORD_SIZE];
60+
let x_bytes = x.to_be_bytes();
61+
bytes[WORD_SIZE - x_bytes.len()..].copy_from_slice(&x_bytes);
62+
bytes
63+
}
64+
5665
impl<G, H> Codec for ByteSchnorrCodec<G, H>
5766
where
5867
G: Group + GroupEncoding,
@@ -63,10 +72,11 @@ where
6372
fn new(protocol_id: &[u8], session_id: &[u8], instance_label: &[u8]) -> Self {
6473
let iv = {
6574
let mut tmp = H::new([0u8; 32]);
75+
tmp.absorb(&length_to_bytes(protocol_id.len()));
6676
tmp.absorb(protocol_id);
67-
tmp.ratchet();
77+
tmp.absorb(&length_to_bytes(session_id.len()));
6878
tmp.absorb(session_id);
69-
tmp.ratchet();
79+
tmp.absorb(&length_to_bytes(instance_label.len()));
7080
tmp.absorb(instance_label);
7181
tmp.squeeze(32).try_into().unwrap()
7282
};

src/tests/spec/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ mod rng;
55

66
mod test_duplex_sponge;
77
mod test_vectors;
8+
mod test_vectors_with_fixed_label;

src/tests/spec/test_duplex_sponge.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ fn hex_decode(hex_str: &str) -> Vec<u8> {
3232
}
3333

3434
fn load_test_vectors() -> HashMap<String, TestVector> {
35-
let json_data = include_str!("./duplexSpongeVectors.json");
35+
let json_data = include_str!("./vectors/duplexSpongeVectors.json");
3636
serde_json::from_str(json_data).expect("Failed to parse test vectors JSON")
3737
}
3838

src/tests/spec/test_vectors.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ generate_ni_function!(
7171
fn test_spec_testvectors() {
7272
let seed = b"hello world";
7373
let iv = *b"yellow submarineyellow submarine";
74-
let vectors = extract_vectors("src/tests/spec/allVectors.json").unwrap();
74+
let vectors = extract_vectors("src/tests/spec/vectors/allVectors.json").unwrap();
7575

7676
let functions: [fn(&[u8], [u8; 32]) -> (Vec<Scalar>, Vec<u8>); 5] = [
7777
NI_discrete_logarithm,
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
use bls12_381::{G1Projective as G, Scalar};
2+
use core::str;
3+
use hex::FromHex;
4+
use json::JsonValue;
5+
use std::fs;
6+
7+
use crate::codec::KeccakByteSchnorrCodec;
8+
use crate::fiat_shamir::NISigmaProtocol;
9+
use crate::tests::spec::{
10+
custom_schnorr_protocol::SchnorrProtocolCustom, random::SRandom, rng::TestDRNG,
11+
};
12+
use crate::tests::test_utils::{
13+
bbs_blind_commitment_computation, discrete_logarithm, dleq, pedersen_commitment,
14+
pedersen_commitment_dleq,
15+
};
16+
17+
type NIProtocol = NISigmaProtocol<SchnorrProtocolCustom<G>, KeccakByteSchnorrCodec<G>>;
18+
19+
/// Macro to generate non-interactive sigma protocols test functions
20+
macro_rules! generate_ni_function {
21+
($name:ident, $test_fn:ident, $($param:tt),*) => {
22+
#[allow(non_snake_case)]
23+
fn $name(seed: &[u8], session_id: &[u8]) -> (Vec<Scalar>, Vec<u8>, Vec<u8>) {
24+
let mut rng = TestDRNG::new(seed);
25+
let (instance, witness) = $test_fn($(generate_ni_function!(@arg rng, $param)),*);
26+
27+
let statement = instance.label();
28+
let protocol = SchnorrProtocolCustom(instance);
29+
let nizk = NIProtocol::new(session_id, protocol);
30+
31+
let proof_bytes = nizk.prove_batchable(&witness, &mut rng).unwrap();
32+
let verified = nizk.verify_batchable(&proof_bytes).is_ok();
33+
assert!(verified, "Fiat-Shamir Schnorr proof verification failed");
34+
(witness, proof_bytes, statement)
35+
}
36+
};
37+
38+
(@arg $rng:ident, $type:ident) => {
39+
G::$type(&mut $rng)
40+
};
41+
(@arg $rng:ident, [$type:ident; $count:expr]) => {
42+
(0..$count).map(|_| G::$type(&mut $rng)).collect::<Vec<_>>().try_into().unwrap()
43+
};
44+
}
45+
46+
generate_ni_function!(NI_discrete_logarithm, discrete_logarithm, srandom);
47+
generate_ni_function!(NI_dleq, dleq, prandom, srandom);
48+
generate_ni_function!(
49+
NI_pedersen_commitment,
50+
pedersen_commitment,
51+
prandom,
52+
srandom,
53+
srandom
54+
);
55+
generate_ni_function!(
56+
NI_pedersen_commitment_dleq,
57+
pedersen_commitment_dleq,
58+
[prandom; 4],
59+
[srandom; 2]
60+
);
61+
generate_ni_function!(
62+
NI_bbs_blind_commitment_computation,
63+
bbs_blind_commitment_computation,
64+
[prandom; 4],
65+
[srandom; 3],
66+
srandom
67+
);
68+
69+
#[allow(clippy::type_complexity)]
70+
#[allow(non_snake_case)]
71+
#[test]
72+
fn test_spec_testvectors() {
73+
let seed = b"hello world";
74+
let session_id = b"yellow submarineyellow submarine";
75+
let vectors = extract_vectors("src/tests/spec/vectors/fixedLabelVectors.json").unwrap();
76+
77+
let functions: [fn(&[u8], &[u8]) -> (Vec<Scalar>, Vec<u8>, Vec<u8>); 5] = [
78+
NI_discrete_logarithm,
79+
NI_dleq,
80+
NI_pedersen_commitment,
81+
NI_pedersen_commitment_dleq,
82+
NI_bbs_blind_commitment_computation,
83+
];
84+
85+
for (i, f) in functions.iter().enumerate() {
86+
let (_, proof_bytes, statement) = f(seed, session_id);
87+
assert_eq!(
88+
session_id.as_slice(),
89+
vectors[i].0.as_slice(),
90+
"context for test vector {i} does not match"
91+
);
92+
assert_eq!(
93+
proof_bytes, vectors[i].1,
94+
"proof bytes for test vector {i} does not match"
95+
);
96+
assert_eq!(
97+
statement, vectors[i].2,
98+
"statement for test vector {i} does not match"
99+
);
100+
}
101+
}
102+
103+
#[allow(clippy::type_complexity)]
104+
fn extract_vectors(path: &str) -> json::Result<Vec<(Vec<u8>, Vec<u8>, Vec<u8>)>> {
105+
let content = fs::read_to_string(path).expect("Unable to read JSON file");
106+
let root: JsonValue = json::parse(&content).expect("JSON parsing error");
107+
root.entries()
108+
.map(|(_, obj)| {
109+
let context_hex = obj["Context"]
110+
.as_str()
111+
.expect("Context field not found or not a string");
112+
let proof_hex = obj["Proof"]
113+
.as_str()
114+
.expect("Proof field not found or not a string");
115+
let statement_hex = obj["Statement"]
116+
.as_str()
117+
.expect("Statement field not found or not a string");
118+
Ok((
119+
Vec::from_hex(context_hex).unwrap(),
120+
Vec::from_hex(proof_hex).unwrap(),
121+
Vec::from_hex(statement_hex).unwrap(),
122+
))
123+
})
124+
.collect()
125+
}
File renamed without changes.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"discrete_logarithm_with_session_ID": {
3+
"Ciphersuite": "sigma/OWKeccak1600+Bls12381",
4+
"Context": "79656c6c6f77207375626d6172696e6579656c6c6f77207375626d6172696e65",
5+
"Proof": "80c96c2822d816de609d4b72dd0b2a9409a3402338c977467225e7f506a60f3153a7f447450d7336c0ef15e4151349d95936c8894ec7803ab9e2642bea06ff1cd8e1311a853260eb081610d57d7dccf7",
6+
"Statement": "010000000100000001000000000000000000000097f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb8e6e5ac1f85d0bfd5123a9f7eef4030590497392023dde2cd1df43995f90839a480cd6dc33f857469586855d7face415"
7+
},
8+
"dleq_with_session_ID": {
9+
"Ciphersuite": "sigma/OWKeccak1600+Bls12381",
10+
"Context": "79656c6c6f77207375626d6172696e6579656c6c6f77207375626d6172696e65",
11+
"Proof": "a01abd54895b7df2d476b2371e1796278a114f7dd1514e05cc1c0c07d40957268684c8887aa3f8cee33856ca325412f5859a8bb9d31747dafffcfe70acd32bcb30b45db8333cd157f561039e654e2f8314ee648604afdb4c2a4c30ae964911926139d5d2b2e515d7a71d0907e472a6f7e647a6474ca22e5eb14895324a98b967",
12+
"Statement": "02000000010000000100000000000000000000000300000001000000000000000200000097f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb80c96c2822d816de609d4b72dd0b2a9409a3402338c977467225e7f506a60f3153a7f447450d7336c0ef15e4151349d98e6e5ac1f85d0bfd5123a9f7eef4030590497392023dde2cd1df43995f90839a480cd6dc33f857469586855d7face4158f145e1c22e60229fbfa9a754244a51935ec5422d4aabbb83427cb213ff81a48a49471f74c371564afd334522d56f4b4"
13+
},
14+
"pedersen_commitment_with_session_ID": {
15+
"Ciphersuite": "sigma/OWKeccak1600+Bls12381",
16+
"Context": "79656c6c6f77207375626d6172696e6579656c6c6f77207375626d6172696e65",
17+
"Proof": "91c620e60e68502ab1e0f0fa6b9f7e3225f678596da80c0e950e4149078562518ad37ed6177c71ebd6e2ca5fc32457d87182c691d77c2352c6b7742c2e538711742158c8851c7e00873b45f1ae5a49060524ec782635e94be60f3100297a2cb946f51cfbc9980f5f661c8be4190b2cee",
18+
"Statement": "0100000002000000020000000000000000000000010000000100000097f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb8e6e5ac1f85d0bfd5123a9f7eef4030590497392023dde2cd1df43995f90839a480cd6dc33f857469586855d7face415b4054b3c7e864c3dc66fb10b967d874a871451d2b949e27c02f6ee74f96d68daadea1a917dba760114bb67100f74060d"
19+
},
20+
"pedersen_commitment_dleq_with_session_ID": {
21+
"Ciphersuite": "sigma/OWKeccak1600+Bls12381",
22+
"Context": "79656c6c6f77207375626d6172696e6579656c6c6f77207375626d6172696e65",
23+
"Proof": "b3e0e2f9f68405774ae13cc7271ffa66199c079a332977a0170ff134739feccc674ce0be72e236312ecf3194cfca25eb8253ccdd07c6b67dd79f3a27f214bfcb42d2b0f34432ec795ab286f099bb19ed011b0b10828e58a10d5ef8fb4f615bf84c2c0331fd2de990e9958b4899582a8ceab68529e7771a0a275ab4374ee18cbe1c212b766f327efe9e5be3462ae767583208938915738036650542c6c1916571",
24+
"Statement": "020000000200000002000000000000000000000001000000010000000500000002000000000000000300000001000000040000008e6e5ac1f85d0bfd5123a9f7eef4030590497392023dde2cd1df43995f90839a480cd6dc33f857469586855d7face41580c96c2822d816de609d4b72dd0b2a9409a3402338c977467225e7f506a60f3153a7f447450d7336c0ef15e4151349d992a2d40d620847feca5cb85e5276e72d051568faaf0eab836ba92407dd7a0b7c7cac7b11770403fa368c76b3d01fee23a01abd54895b7df2d476b2371e1796278a114f7dd1514e05cc1c0c07d40957268684c8887aa3f8cee33856ca325412f581a513b89ce4a0e3213ba6dbb8af71898c6c6b4ad96a6d83de7c280fa91065f705b029f0c4db29521190cd576ff5d49a954d82081d55a0f02505bd29a97e5fcd079f2f5dc6901fdf9df178fc0ae4f47b8c2d13bfb6ac3adc06c5d7c69dd3c7f4"
25+
},
26+
"bbs_blind_commitment_computation_with_session_ID": {
27+
"Ciphersuite": "sigma/OWKeccak1600+Bls12381",
28+
"Context": "79656c6c6f77207375626d6172696e6579656c6c6f77207375626d6172696e65",
29+
"Proof": "803d5d4fdb311967832758ae7402d03304b570f97c0756e5385a50622d0ac7b5de87fe14d15041b1564ba4893a1187306c55832724d087dfccf36f4541b4e4449cd7acbadceb4844ade304aca0007fb92be0e35119da7e7b597b96f888517ada2425b33068baa921bc671652367a8e3e5b18c4072e20c532197f804b35499402fd78fd04e82be56ac235349b0ae61b2f0afd8494ffe5f987824e3029798eee96e15d2e7e870f250878ac5110f27e41d9",
30+
"Statement": "01000000040000000400000000000000000000000100000001000000020000000200000003000000030000008e6e5ac1f85d0bfd5123a9f7eef4030590497392023dde2cd1df43995f90839a480cd6dc33f857469586855d7face41580c96c2822d816de609d4b72dd0b2a9409a3402338c977467225e7f506a60f3153a7f447450d7336c0ef15e4151349d9a01abd54895b7df2d476b2371e1796278a114f7dd1514e05cc1c0c07d40957268684c8887aa3f8cee33856ca325412f581a513b89ce4a0e3213ba6dbb8af71898c6c6b4ad96a6d83de7c280fa91065f705b029f0c4db29521190cd576ff5d49a831cb36c8ff357b10806173cf5ad3d7e9a404fe66d2675138086a43b2c8627526e155e6195f55164187c8f4a101a6208"
31+
}
32+
}

0 commit comments

Comments
 (0)