Skip to content

Commit 347060f

Browse files
MariaGhandourmmakernougzarm
authored
fix(keccak): follow duplex sponge implementation. (#46)
Closes #46. Adds tests for inter-operability with other implementations. Co-authored-by: Michele Orrù <[email protected]> Co-authored-by: nougzarm <[email protected]>
1 parent cf8ba18 commit 347060f

File tree

2 files changed

+104
-8
lines changed

2 files changed

+104
-8
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ zeroize = "1.8.1"
3535
bls12_381 = "0.8.0"
3636
curve25519-dalek = { version = "4", default-features = false, features = ["serde", "rand_core", "alloc", "digest", "precomputed-tables", "group"] }
3737
hex = "0.4"
38+
hex-literal = "0.4"
3839
json = "0.12.4"
3940
sha2 = "0.10"
4041
subtle = "2.6.1"

src/duplex_sponge/keccak.rs

Lines changed: 103 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,12 @@ impl DuplexSpongeInterface for KeccakDuplexSponge {
7979
}
8080

8181
fn squeeze(&mut self, mut length: usize) -> Vec<u8> {
82-
self.absorb_index = RATE;
83-
8482
let mut output = Vec::new();
8583
while length != 0 {
8684
if self.squeeze_index == RATE {
8785
self.state.permute();
8886
self.squeeze_index = 0;
87+
self.absorb_index = 0;
8988
}
9089

9190
let chunk_size = usize::min(RATE - self.squeeze_index, length);
@@ -95,7 +94,6 @@ impl DuplexSpongeInterface for KeccakDuplexSponge {
9594
self.squeeze_index += chunk_size;
9695
length -= chunk_size;
9796
}
98-
9997
output
10098
}
10199
}
@@ -104,15 +102,112 @@ impl DuplexSpongeInterface for KeccakDuplexSponge {
104102
mod tests {
105103
use super::*;
106104
use crate::duplex_sponge::DuplexSpongeInterface;
105+
use hex_literal::hex;
106+
107+
fn assert_sponge_output(tag: &[u8; 32], input: &[u8], expected: &[u8]) {
108+
let mut sponge = KeccakDuplexSponge::new(*tag);
109+
sponge.absorb(input);
110+
let output = sponge.squeeze(expected.len());
111+
assert_eq!(output, expected);
112+
}
113+
114+
const TEST_TAG: &[u8; 32] = b"unit_tests_keccak_tag___________";
115+
const HELLO_WORLD_OUTPUT: &[u8] = &hex!("73e4a040a956f57693fb2b2dde8a8ea2c14d39ff8830060cd0301d6de25b2097ba858efedeeb89368eaf7c94a68f62835f932b5f0dd0ba376c48a0fdb5e21f0c");
107116

108117
#[test]
109118
fn test_keccak_duplex_sponge() {
110-
let mut sponge = KeccakDuplexSponge::new([0u8; 32]);
119+
assert_sponge_output(TEST_TAG, b"Hello, World!", HELLO_WORLD_OUTPUT);
120+
}
111121

112-
let input = b"Hello, World!";
113-
sponge.absorb(input);
114-
let output = sponge.squeeze(64);
122+
#[test]
123+
fn test_absorb_empty_before_does_not_break() {
124+
let mut sponge = KeccakDuplexSponge::new(*TEST_TAG);
125+
sponge.absorb(b"");
126+
sponge.absorb(b"Hello, World!");
127+
sponge.squeeze(0);
128+
assert_eq!(sponge.squeeze(64), HELLO_WORLD_OUTPUT);
129+
}
130+
#[test]
131+
fn test_absorb_empty_after_does_not_break() {
132+
let mut sponge = KeccakDuplexSponge::new(*TEST_TAG);
133+
sponge.absorb(b"Hello, World!");
134+
sponge.absorb(b"");
135+
sponge.squeeze(0);
136+
assert_eq!(sponge.squeeze(64), HELLO_WORLD_OUTPUT);
137+
}
138+
139+
#[test]
140+
fn test_squeeze_zero_before_behavior() {
141+
let mut sponge = KeccakDuplexSponge::new(*TEST_TAG);
142+
sponge.squeeze(0);
143+
sponge.absorb(b"Hello, World!");
144+
assert_eq!(sponge.squeeze(64), HELLO_WORLD_OUTPUT);
145+
}
146+
147+
#[test]
148+
fn test_squeeze_zero_after_behavior() {
149+
let mut sponge = KeccakDuplexSponge::new(*TEST_TAG);
150+
sponge.absorb(b"Hello, World!");
151+
sponge.squeeze(0);
152+
assert_eq!(sponge.squeeze(64), HELLO_WORLD_OUTPUT);
153+
}
154+
155+
#[test]
156+
fn test_absorb_squeeze_absorb_consistency() {
157+
let tag = *b"edge-case-test-domain-absorb0000";
158+
159+
let mut sponge = KeccakDuplexSponge::new(tag);
160+
sponge.absorb(b"first");
161+
sponge.squeeze(32);
162+
sponge.absorb(b"second");
163+
let output = sponge.squeeze(32);
164+
165+
assert_eq!(
166+
output,
167+
hex!("20ce6da64ffc09df8de254222c068358da39d23ec43e522ceaaa1b82b90c8b9a")
168+
);
169+
}
170+
#[test]
171+
fn test_associativity_of_absorb() {
172+
let expected_output =
173+
hex!("7dfada182d6191e106ce287c2262a443ce2fb695c7cc5037a46626e88889af58");
174+
let tag = *b"absorb-associativity-domain-----";
175+
176+
// Absorb all at once
177+
let mut sponge1 = KeccakDuplexSponge::new(tag);
178+
sponge1.absorb(b"hello world");
179+
let out1 = sponge1.squeeze(32);
180+
181+
// Absorb in two parts
182+
let mut sponge2 = KeccakDuplexSponge::new(tag);
183+
sponge2.absorb(b"hello");
184+
sponge2.absorb(b" world");
185+
let out2 = sponge2.squeeze(32);
186+
187+
assert_eq!(out1, expected_output);
188+
assert_eq!(out2, expected_output);
189+
}
115190

116-
assert_eq!(output, hex::decode("30b74a98221dd643d0814095c212d663a67945c6a582ef8f71bd2a14607ebade3f16e5975ad13d313d9aa0aa97ad29f7df5cff249fa633d3a7ac70d8587bec90").unwrap());
191+
#[test]
192+
fn test_tag_affects_output() {
193+
assert_sponge_output(
194+
b"domain-one-differs-here-00000000",
195+
b"input",
196+
&hex!("2ecad63584ec0ff7f31edb822530762e5cb4b7dc1a62b1ffe02c43f3073a61b8"),
197+
);
198+
assert_sponge_output(
199+
b"domain-two-differs-here-00000000",
200+
b"input",
201+
&hex!("6310fa0356e1bab0442fa19958e1c4a6d1dcc565b2b139b6044d1a809f531825"),
202+
);
203+
}
204+
205+
#[test]
206+
fn test_multiple_blocks_absorb_squeeze() {
207+
assert_sponge_output(
208+
b"multi-block-absorb-test_________",
209+
&vec![0xABu8; 3 * 200],
210+
&hex!("606310f839e763f4f37ce4c9730da92d4d293109de06abee8a7b40577125bcbfca331b97aee104d03139247e801d8b1a5f6b028b8e51fd643de790416819780a1235357db153462f78c150e34f29a303288f07f854e229aed41c786313119a1cee87402006ab5102271576542e5580be1927af773b0f1b46ce5c78c15267d3729928909192ea0115fcb9475b38a1ff5004477bbbb1b1f5c6a5c90c29b245a83324cb108133efc82216d33da9866051d93baab3bdf0fe02b007d4eb94885a42fcd02a9acdd47b71b6eeac17f5946367d6c69c95cbb80ac91d75e22c9862cf5fe10c7e121368e8a8cd9ff8eebe21071ff014e053725bcc624cd9f31818c4d049e70c14a22e5d3062a553ceca6157315ef2bdb3619c970c9c3d60817ee68291dcd17a282ed1b33cb3afb79c8247cd46de13add88da4418278c8b6b919914be5379daa823b036da008718c1d2a4a0768ecdf032e2b93c344ff65768c8a383a8747a1dcc13b5569b4e15cab9cc8f233fb28b13168284c8a998be6f8fa05389ff9c1d90c5845060d2df3fe0a923be8603abbd2b6f6dd6a5c09c81afe7c06bec789db87185297d6f7261f1e5637f2d140ff3b306df77f42cceffe769545ea8b011022387cd9e3d4f2c97feff5099139715f72301799fcfd59aa30f997e26da9eb7d86ee934a3f9c116d4a9e1012d795db35e1c61d27cd74bb6002f463fc129c1f9c4f25bc8e79c051ac2f1686e393d670f8d1e4cea12acfbff5a135623615d69a88f390569f17a0fc65f5886e2df491615155d5c3eb871209a5c7b0439585ad1a0acbede2e1a8d5aad1d8f3a033267e12185c5f2bbab0f2f1769247"),
211+
);
117212
}
118213
}

0 commit comments

Comments
 (0)