···223223 let big_data = [0x34u8; BIG_N];
224224 let small_data = [0x35u8; SMALL_N];
225225226226+ // meta_ad won't stream unintentionally as we've reset the op state during initialisation
226227 s.meta_ad(&big_data[..]);
227228 s.ad(&big_data[..]);
228229 s.meta_key(&big_data[..]);
-4
src/herd_of_kats/mod.rs
···11-//! ## Herding KATs
22-//!
33-//! This module contains a bigger test suite to evaluate the implementation of `StrobeState`
44-//! fully.
+187
src/herding_kats/harness.rs
···11+extern crate std;
22+33+use std::{boxed::Box, string::String, vec::Vec};
44+55+use hex;
66+use serde::{Deserialize, Deserializer, de};
77+88+use crate::strobe::{SecurityParameter, StrobeState};
99+1010+/// The harness we will put on our KATs so we can herd them and make them do tests.
1111+/// (This is the top-level structure of the JSON we find in the test vectors)
1212+#[derive(Deserialize)]
1313+struct KatHarness {
1414+ proto_string: String,
1515+ #[serde(deserialize_with = "security_param_from_bits")]
1616+ security: SecurityParameter,
1717+ operations: Vec<KatOperation>,
1818+}
1919+2020+/// The format of the tests our KATs will perform for us while leashed.
2121+#[derive(Deserialize)]
2222+struct KatOperation {
2323+ name: String,
2424+ meta: bool,
2525+ #[serde(deserialize_with = "bytes_from_hex")]
2626+ input_data: Vec<u8>,
2727+ stream: bool,
2828+ #[serde(default, rename = "output", deserialize_with = "bytes_from_hex_opt")]
2929+ expected_output: Option<Vec<u8>>,
3030+ #[serde(default, rename = "state_after", deserialize_with = "bytes_from_hex")]
3131+ expected_state_after: Vec<u8>,
3232+}
3333+3434+// Recall that `ratchet` can take a length argument, so this is the most general type that
3535+// represents the input to a STROBE operation
3636+pub enum DataOrLength<'a> {
3737+ Data(&'a mut [u8]),
3838+ Length(usize),
3939+}
4040+4141+// Given the name of the operation and meta flag, returns a closure that performs this operation.
4242+// The types are kind of a mess, because the input and output types of the closure have to fit all
4343+// possible STROBE operations.
4444+fn get_operation<'s>(
4545+ op_name: &'s str,
4646+ meta: bool,
4747+) -> Box<dyn for<'a> Fn(&mut StrobeState, DataOrLength<'a>, bool) + 's> {
4848+ let f = move |s: &mut StrobeState, dol: DataOrLength, more: bool| {
4949+ let data = match dol {
5050+ DataOrLength::Length(len) => {
5151+ if !meta {
5252+ assert_eq!(op_name, "RATCHET", "Got length input without RATCHET op");
5353+ s.ratchet(len);
5454+ return;
5555+ } else {
5656+ assert_eq!(op_name, "RATCHET", "Got length input without RATCHET op");
5757+ s.meta_ratchet(len);
5858+ return;
5959+ }
6060+ }
6161+ DataOrLength::Data(data) => data,
6262+ };
6363+6464+ // To prevent streaming, reset ops *before* executing the operation
6565+ // in order to ensure the output state matches the KAT assertion
6666+ if !more {
6767+ s.reset_ops();
6868+ }
6969+7070+ // Note: we don't expect recv_MAC to work on random inputs. We test recv_MAC's
7171+ // correctness in basic_kats.rs. Also MAC sizes are 14 bytes in the KAT.
7272+ if !meta {
7373+ match op_name {
7474+ "AD" => s.ad(data),
7575+ "KEY" => s.key(data),
7676+ "PRF" => s.prf(data),
7777+ "send_CLR" => s.send_clr(data),
7878+ "recv_CLR" => s.recv_clr(data),
7979+ "send_ENC" => s.send_enc(data),
8080+ "recv_ENC" => s.recv_enc(data),
8181+ "send_MAC" => s.send_mac(data),
8282+ "recv_MAC" => s
8383+ .recv_mac::<14>(data.as_ref().try_into().unwrap())
8484+ .unwrap_or(()),
8585+ "RATCHET" => panic!("Got RATCHET op without length input"),
8686+ _ => panic!("Unexpected op name: {}", op_name),
8787+ }
8888+ } else {
8989+ match op_name {
9090+ "AD" => s.meta_ad(data),
9191+ "KEY" => s.meta_key(data),
9292+ "PRF" => s.meta_prf(data),
9393+ "send_CLR" => s.meta_send_clr(data),
9494+ "recv_CLR" => s.meta_recv_clr(data),
9595+ "send_ENC" => s.meta_send_enc(data),
9696+ "recv_ENC" => s.meta_recv_enc(data),
9797+ "send_MAC" => s.meta_send_mac(data),
9898+ "recv_MAC" => s
9999+ .meta_recv_mac::<14>(data.as_ref().try_into().unwrap())
100100+ .unwrap_or(()),
101101+ "RATCHET" => panic!("Got RATCHET op without length input"),
102102+ _ => panic!("Unexpected op name: {}", op_name),
103103+ }
104104+ }
105105+ };
106106+107107+ Box::new(f)
108108+}
109109+110110+// Runs the test vector and compares to the expected output at each step of the way
111111+pub fn test_against_kat<P: AsRef<std::path::Path>>(filename: P) {
112112+ let file = std::fs::File::open(filename).unwrap();
113113+114114+ let KatHarness {
115115+ proto_string,
116116+ security,
117117+ operations,
118118+ } = serde_json::from_reader(file).unwrap();
119119+120120+ let mut strobe = StrobeState::new(proto_string.as_bytes(), security);
121121+122122+ operations.into_iter().for_each(
123123+ |KatOperation {
124124+ name,
125125+ meta,
126126+ mut input_data,
127127+ stream,
128128+ expected_output,
129129+ expected_state_after,
130130+ }| {
131131+ if name != "init" {
132132+ // RATCHET inputs are given as strings of zeros instead of lengths. So just take the
133133+ // length of the string of zeros.
134134+ let input = if &name == "RATCHET" {
135135+ DataOrLength::Length(input_data.len())
136136+ } else {
137137+ DataOrLength::Data(input_data.as_mut_slice())
138138+ };
139139+140140+ get_operation(&name, meta)(&mut strobe, input, stream);
141141+142142+ assert_eq!(&strobe.state.0[..], expected_state_after.as_slice());
143143+144144+ // Only test expected output if the test vector has output to test against
145145+ if let Some(expected) = expected_output {
146146+ // The input was presumably mutated;
147147+ assert_eq!(input_data.as_slice(), expected.as_slice());
148148+ }
149149+ }
150150+ },
151151+ );
152152+}
153153+154154+fn security_param_from_bits<'de, D: Deserializer<'de>>(
155155+ deserializer: D,
156156+) -> Result<SecurityParameter, D::Error>
157157+where
158158+ D: Deserializer<'de>,
159159+{
160160+ match u64::deserialize(deserializer)? {
161161+ 128 => Ok(SecurityParameter::B128),
162162+ 256 => Ok(SecurityParameter::B256),
163163+ n => Err(de::Error::custom(std::format!(
164164+ "Invalid security parameter: {}",
165165+ n
166166+ ))),
167167+ }
168168+}
169169+170170+fn bytes_from_hex<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
171171+where
172172+ D: Deserializer<'de>,
173173+{
174174+ let mut hex_str = String::deserialize(deserializer)?;
175175+ // Prepend a 0 if it's not even length
176176+ if hex_str.len() % 2 == 1 {
177177+ hex_str.insert(0, '0');
178178+ }
179179+ hex::decode(hex_str).map_err(|e| de::Error::custom(std::format!("{:?}", e)))
180180+}
181181+182182+fn bytes_from_hex_opt<'de, D>(deserializer: D) -> Result<Option<Vec<u8>>, D::Error>
183183+where
184184+ D: Deserializer<'de>,
185185+{
186186+ bytes_from_hex(deserializer).map(Some)
187187+}
+28
src/herding_kats/mod.rs
···11+//! ## Herding KATs
22+//!
33+//! This module contains a bigger test suite to evaluate the implementation of `StrobeState`
44+//! fully. A lot of KATs, thus we must herd them all with a suitable harness.
55+66+use crate::herding_kats::harness::test_against_kat;
77+88+mod harness;
99+1010+#[test]
1111+fn simple_kats() {
1212+ test_against_kat("katties/simple_test_vector.json");
1313+}
1414+1515+#[test]
1616+fn meta_kats() {
1717+ test_against_kat("katties/meta_test_vector.json");
1818+}
1919+2020+#[test]
2121+fn streaming_kats() {
2222+ test_against_kat("katties/streaming_test_vector.json");
2323+}
2424+2525+#[test]
2626+fn boundary_kats() {
2727+ test_against_kat("katties/boundary_test_vector.json");
2828+}