//! Hybrid signatures (extensions/pq-signatures.md): the committed corpus is the generated one, //! every case reaches the verdict it states, and §8 lists exactly the reasons the reference //! reports, each of which the corpus gives. #![cfg(feature = "pq_signatures")] use wai::pq_sig::{corpus, Reason}; const CORPUS: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../pq-conformance/vectors.json"); /// Runs `f` on a thread with 1 MiB of stack, what Windows gives a program's main thread, on /// which `wai_pq_vectors` generates and verifies this corpus. An unoptimised build needed 3.4 MiB /// while each hybrid key held its expanded ML-DSA key by value (`pq_sig::HybridKey`), and about /// 0.75 MiB since: one that outgrows 1 MiB again fails here, on every platform. fn windows_main_stack(f: impl FnOnce() -> T + Send + 'static) -> T { std::thread::Builder::new().stack_size(1 << 20).spawn(f).expect("a thread").join().expect("the thread's result") } const EXTENSION: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../extensions/pq-signatures.md"); #[test] fn the_committed_corpus_is_the_generated_one() { let committed = std::fs::read_to_string(CORPUS).expect("pq-conformance/vectors.json is committed"); let generated = windows_main_stack(corpus::generate); assert_eq!(generated.len(), 1); assert_eq!(committed, generated[0].1, "pq-conformance/vectors.json is not what wai_pq_vectors gen writes"); } #[test] fn every_case_reaches_its_verdict() { let doc: serde_json::Value = serde_json::from_str(&std::fs::read_to_string(CORPUS).expect("the corpus")).expect("JSON"); let n = doc["cases"].as_array().map_or(0, Vec::len); assert!(n >= 40, "{n} cases: a truncated corpus passes on nothing"); let fails = windows_main_stack(move || corpus::run(&doc)); assert!(fails.is_empty(), "{fails:#?}"); } /// §8's table names exactly the reference's codes, in its order, and every one is given by a /// case of the corpus. #[test] fn section_8_lists_the_codes() { let text = std::fs::read_to_string(EXTENSION).expect("the extension"); let sec = text.split("\n## 8. ").nth(1).expect("§8").split("\n## ").next().expect("§8's end"); let listed: Vec<&str> = sec .lines() .filter_map(|l| l.strip_prefix("| `")) .filter_map(|l| l.split('`').next()) .collect(); let codes: Vec<&str> = Reason::all().iter().map(|r| r.label()).collect(); assert_eq!(listed, codes, "§8's table against the reference's codes"); let corpus = std::fs::read_to_string(CORPUS).expect("the corpus"); for c in codes { assert!(corpus.contains(&format!("reason={c}")), "no case gives {c}"); } }