Skip to content

Commit d03de0d

Browse files
committed
Updating test vectors for DuplexSponge.
SHAKE128 and Keccak sponges are passing test vectors.
1 parent 3e7ebf4 commit d03de0d

File tree

8 files changed

+260
-148
lines changed

8 files changed

+260
-148
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ curve25519-dalek = { version = "4", default-features = false, features = ["serde
4545
hex = "0.4"
4646
hex-literal = "0.4"
4747
json = "0.12.4"
48+
libtest-mimic = "0.8.1"
4849
serde = { version = "1.0.219", features = ["derive"] }
4950
serde_json = "1.0.140"
5051
sha2 = "0.10"

src/codec.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub trait Codec {
2626
fn new(protocol_identifier: &[u8], session_identifier: &[u8], instance_label: &[u8]) -> Self;
2727

2828
/// Allows for precomputed initialization of the codec with a specific IV.
29-
fn from_iv(iv: [u8; 32]) -> Self;
29+
fn from_iv(iv: [u8; 64]) -> Self;
3030

3131
/// Absorbs data into the codec.
3232
fn prover_message(&mut self, data: &[u8]);
@@ -68,15 +68,15 @@ pub fn compute_iv<H: DuplexSpongeInterface>(
6868
protocol_id: &[u8],
6969
session_id: &[u8],
7070
instance_label: &[u8],
71-
) -> [u8; 32] {
72-
let mut tmp = H::new([0u8; 32]);
71+
) -> [u8; 64] {
72+
let mut tmp = H::new([0u8; 64]);
7373
tmp.absorb(&length_to_bytes(protocol_id.len()));
7474
tmp.absorb(protocol_id);
7575
tmp.absorb(&length_to_bytes(session_id.len()));
7676
tmp.absorb(session_id);
7777
tmp.absorb(&length_to_bytes(instance_label.len()));
7878
tmp.absorb(instance_label);
79-
tmp.squeeze(32).try_into().unwrap()
79+
tmp.squeeze(64).try_into().unwrap()
8080
}
8181

8282
impl<G, H> Codec for ByteSchnorrCodec<G, H>
@@ -91,7 +91,7 @@ where
9191
Self::from_iv(iv)
9292
}
9393

94-
fn from_iv(iv: [u8; 32]) -> Self {
94+
fn from_iv(iv: [u8; 64]) -> Self {
9595
Self {
9696
hasher: H::new(iv),
9797
_marker: core::marker::PhantomData,

src/duplex_sponge/keccak.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ const LENGTH: usize = 136 + 64;
1515
pub struct KeccakPermutationState([u64; LENGTH / 8]);
1616

1717
impl KeccakPermutationState {
18-
pub fn new(iv: [u8; 32]) -> Self {
18+
pub fn new(iv: [u8; 64]) -> Self {
1919
let mut state = Self::default();
20-
state.as_mut()[RATE..RATE + 32].copy_from_slice(&iv);
20+
state.as_mut()[RATE..RATE + 64].copy_from_slice(&iv);
2121
state
2222
}
2323

@@ -47,7 +47,7 @@ pub struct KeccakDuplexSponge {
4747
}
4848

4949
impl KeccakDuplexSponge {
50-
pub fn new(iv: [u8; 32]) -> Self {
50+
pub fn new(iv: [u8; 64]) -> Self {
5151
let state = KeccakPermutationState::new(iv);
5252
KeccakDuplexSponge {
5353
state,
@@ -58,7 +58,7 @@ impl KeccakDuplexSponge {
5858
}
5959

6060
impl DuplexSpongeInterface for KeccakDuplexSponge {
61-
fn new(iv: [u8; 32]) -> Self {
61+
fn new(iv: [u8; 64]) -> Self {
6262
KeccakDuplexSponge::new(iv)
6363
}
6464

@@ -108,8 +108,8 @@ mod tests {
108108
#[test]
109109
fn test_associativity_of_absorb() {
110110
let expected_output =
111-
hex!("7dfada182d6191e106ce287c2262a443ce2fb695c7cc5037a46626e88889af58");
112-
let tag = *b"absorb-associativity-domain-----";
111+
hex!("efc1c34f94c0d9cfe051561f8206543056ce660fd17834b2eeb9431a4c65bc77");
112+
let tag = *b"absorb-associativity-domain-----absorb-associativity-domain-----";
113113

114114
// Absorb all at once
115115
let mut sponge1 = KeccakDuplexSponge::new(tag);

src/duplex_sponge/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ pub trait DuplexSpongeInterface {
2020
/// Creates a new sponge instance with a given initialization vector (IV).
2121
///
2222
/// The IV enables domain separation and reproducibility between parties.
23-
fn new(iv: [u8; 32]) -> Self;
23+
fn new(iv: [u8; 64]) -> Self
24+
where
25+
Self: Sized;
2426

2527
/// Absorbs input data into the sponge state.
2628
fn absorb(&mut self, input: &[u8]);

src/duplex_sponge/shake.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ use sha3::Shake128;
1313
pub struct ShakeDuplexSponge(Shake128);
1414

1515
impl DuplexSpongeInterface for ShakeDuplexSponge {
16-
fn new(iv: [u8; 32]) -> Self {
16+
fn new(iv: [u8; 64]) -> Self {
1717
let mut hasher = Shake128::default();
18-
hasher.update(&iv);
18+
let initial_block = [iv.to_vec(), vec![0u8; 168 - 64]].concat();
19+
hasher.update(&initial_block);
1920
Self(hasher)
2021
}
2122

src/fiat_shamir.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ where
7878
}
7979
}
8080

81-
pub fn from_iv(iv: [u8; 32], interactive_proof: P) -> Self {
81+
pub fn from_iv(iv: [u8; 64], interactive_proof: P) -> Self {
8282
let hash_state = C::from_iv(iv);
8383
Self {
8484
hash_state,
Lines changed: 31 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use crate::duplex_sponge::{keccak::KeccakDuplexSponge, DuplexSpongeInterface};
1+
use crate::duplex_sponge::{
2+
keccak::KeccakDuplexSponge, shake::ShakeDuplexSponge, DuplexSpongeInterface,
3+
};
4+
use libtest_mimic::{Arguments, Failed, Trial};
25
use serde::{Deserialize, Serialize};
36
use std::collections::HashMap;
47

@@ -10,8 +13,8 @@ struct TestVector {
1013
hash_function: String,
1114
#[serde(rename = "Operations")]
1215
operations: Vec<Operation>,
13-
#[serde(rename = "Tag")]
14-
tag: String,
16+
#[serde(rename = "IV")]
17+
iv: String,
1518
}
1619

1720
#[derive(Debug, Deserialize, Serialize)]
@@ -36,12 +39,15 @@ fn load_test_vectors() -> HashMap<String, TestVector> {
3639
serde_json::from_str(json_data).expect("Failed to parse test vectors JSON")
3740
}
3841

39-
fn run_test_vector(name: &str, test_vector: &TestVector) {
40-
let tag_bytes = hex_decode(&test_vector.tag);
41-
let mut tag_array = [0u8; 32];
42-
tag_array.copy_from_slice(&tag_bytes);
42+
fn run_test_vector(name: &str, test_vector: &TestVector) -> Result<(), Failed> {
43+
let iv_bytes = hex_decode(&test_vector.iv);
44+
let iv_array: [u8; 64] = iv_bytes.try_into().unwrap();
4345

44-
let mut sponge = KeccakDuplexSponge::new(tag_array);
46+
let mut sponge: Box<dyn DuplexSpongeInterface> = match test_vector.hash_function.as_str() {
47+
"Keccak-f[1600] overwrite mode" => Box::new(KeccakDuplexSponge::new(iv_array)),
48+
"SHAKE128" => Box::new(ShakeDuplexSponge::new(iv_array)),
49+
_ => panic!("Unknown hash function: {}", test_vector.hash_function),
50+
};
4551
let mut final_output = Vec::new();
4652

4753
for operation in &test_vector.operations {
@@ -62,88 +68,27 @@ fn run_test_vector(name: &str, test_vector: &TestVector) {
6268
}
6369
}
6470

65-
let expected_output = hex_decode(&test_vector.expected);
66-
assert_eq!(final_output, expected_output, "Test vector '{name}' failed");
71+
assert_eq!(
72+
hex::encode(final_output),
73+
test_vector.expected,
74+
"Test vector '{name}' failed"
75+
);
76+
Ok(())
6777
}
6878

6979
#[test]
7080
fn test_all_duplex_sponge_vectors() {
7181
let test_vectors = load_test_vectors();
7282

73-
for (name, test_vector) in test_vectors {
74-
run_test_vector(&name, &test_vector);
75-
}
76-
}
77-
78-
#[test]
79-
fn test_keccak_duplex_sponge_vector() {
80-
let test_vectors = load_test_vectors();
81-
let test_vector = test_vectors.get("test_keccak_duplex_sponge").unwrap();
82-
run_test_vector("test_keccak_duplex_sponge", test_vector);
83-
}
84-
85-
#[test]
86-
fn test_absorb_empty_before_vector() {
87-
let test_vectors = load_test_vectors();
88-
let test_vector = test_vectors
89-
.get("test_absorb_empty_before_does_not_break")
90-
.unwrap();
91-
run_test_vector("test_absorb_empty_before_does_not_break", test_vector);
92-
}
93-
94-
#[test]
95-
fn test_absorb_empty_after_vector() {
96-
let test_vectors = load_test_vectors();
97-
let test_vector = test_vectors
98-
.get("test_absorb_empty_after_does_not_break")
99-
.unwrap();
100-
run_test_vector("test_absorb_empty_after_does_not_break", test_vector);
101-
}
102-
103-
#[test]
104-
fn test_squeeze_zero_before_vector() {
105-
let test_vectors = load_test_vectors();
106-
let test_vector = test_vectors.get("test_squeeze_zero_behavior").unwrap();
107-
run_test_vector("test_squeeze_zero_behavior", test_vector);
108-
}
109-
110-
#[test]
111-
fn test_squeeze_zero_after_vector() {
112-
let test_vectors = load_test_vectors();
113-
let test_vector = test_vectors
114-
.get("test_squeeze_zero_after_behavior")
115-
.unwrap();
116-
run_test_vector("test_squeeze_zero_after_behavior", test_vector);
117-
}
118-
119-
#[test]
120-
fn test_absorb_squeeze_absorb_consistency_vector() {
121-
let test_vectors = load_test_vectors();
122-
let test_vector = test_vectors
123-
.get("test_absorb_squeeze_absorb_consistency")
124-
.unwrap();
125-
run_test_vector("test_absorb_squeeze_absorb_consistency", test_vector);
126-
}
127-
128-
#[test]
129-
fn test_associativity_of_absorb_vector() {
130-
let test_vectors = load_test_vectors();
131-
let test_vector = test_vectors.get("test_associativity_of_absorb").unwrap();
132-
run_test_vector("test_associativity_of_absorb", test_vector);
133-
}
134-
135-
#[test]
136-
fn test_tag_affects_output_vector() {
137-
let test_vectors = load_test_vectors();
138-
let test_vector = test_vectors.get("test_tag_affects_output").unwrap();
139-
run_test_vector("test_tag_affects_output", test_vector);
140-
}
141-
142-
#[test]
143-
fn test_multiple_blocks_absorb_squeeze_vector() {
144-
let test_vectors = load_test_vectors();
145-
let test_vector = test_vectors
146-
.get("test_multiple_blocks_absorb_squeeze")
147-
.unwrap();
148-
run_test_vector("test_multiple_blocks_absorb_squeeze", test_vector);
83+
let tests = test_vectors
84+
.into_iter()
85+
.map(|(name, test_vector)| {
86+
Trial::test(
87+
format!("tests::spec::test_duplex_sponge::{}", name),
88+
move || run_test_vector(&name, &test_vector),
89+
)
90+
})
91+
.collect();
92+
93+
libtest_mimic::run(&Arguments::from_args(), tests).exit();
14994
}

0 commit comments

Comments
 (0)