//! BIP340 official test vectors (data/vectors/bip340_test_vectors.csv) checked against both //! libsecp256k1 (rust-secp256k1) and k256. use serde::Serialize; #[derive(Debug, Clone)] pub struct Vector { pub index: u32, pub seckey: Option<[u8; 32]>, pub pubkey: [u8; 32], pub aux: Option<[u8; 32]>, pub msg: Vec, pub sig: [u8; 64], pub expected: bool, pub comment: String, } pub fn hex(s: &str) -> Vec { (0..s.len()).step_by(2).map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap()).collect() } pub fn to_hex(b: &[u8]) -> String { b.iter().map(|x| format!("{:02x}", x)).collect() } fn arr32(v: &[u8]) -> [u8; 32] { let mut a = [0u8; 32]; a.copy_from_slice(v); a } pub fn parse_csv(text: &str) -> Vec { let mut out = Vec::new(); for (ln, line) in text.lines().enumerate() { if ln == 0 || line.trim().is_empty() { continue; } let f: Vec<&str> = line.split(',').collect(); assert!(f.len() >= 7, "bad CSV line {}", ln); let sig = hex(f[5]); let mut s64 = [0u8; 64]; s64.copy_from_slice(&sig); out.push(Vector { index: f[0].parse().unwrap(), seckey: if f[1].is_empty() { None } else { Some(arr32(&hex(f[1]))) }, pubkey: arr32(&hex(f[2])), aux: if f[3].is_empty() { None } else { Some(arr32(&hex(f[3]))) }, msg: hex(f[4]), sig: s64, expected: f[6].trim().eq_ignore_ascii_case("TRUE"), comment: f.get(7).map(|s| s.trim().to_string()).unwrap_or_default(), }); } out } #[derive(Serialize, Debug, Clone)] pub struct VectorResult { pub index: u32, pub expected: bool, pub verify_ok: bool, pub sign_checked: bool, pub sign_ok: Option, pub comment: String, } #[derive(Serialize, Debug, Clone)] pub struct VectorSummary { pub library: String, pub n_vectors: usize, pub verify_pass: usize, pub sign_cases: usize, pub sign_pass: usize, pub all_pass: bool, pub details: Vec, } pub fn check_secp(vs: &[Vector]) -> VectorSummary { use secp256k1::{schnorr, Keypair, SecretKey, XOnlyPublicKey}; let mut details = Vec::new(); for v in vs { let sig = schnorr::Signature::from_byte_array(v.sig); let verified = match XOnlyPublicKey::from_byte_array(v.pubkey) { Ok(pk) => schnorr::verify(&sig, &v.msg, &pk).is_ok(), Err(_) => false, }; let mut sign_ok = None; if let (Some(sk), Some(aux)) = (v.seckey, v.aux) { let kp = Keypair::from_secret_key(&SecretKey::from_secret_bytes(sk).unwrap()); let (xo, _) = kp.x_only_public_key(); let s = schnorr::sign_with_aux_rand(&v.msg, &kp, &aux); sign_ok = Some(s.to_byte_array() == v.sig && xo.to_byte_array() == v.pubkey); } details.push(VectorResult { index: v.index, expected: v.expected, verify_ok: verified == v.expected, sign_checked: sign_ok.is_some(), sign_ok, comment: v.comment.clone(), }); } summarize("libsecp256k1 (rust-secp256k1)", details) } pub fn check_k256(vs: &[Vector]) -> VectorSummary { use k256::schnorr::{Signature, SigningKey, VerifyingKey}; use k256::FieldBytes; let mut details = Vec::new(); for v in vs { let verified = match (VerifyingKey::from_bytes(&FieldBytes::from(v.pubkey)), Signature::from_slice(&v.sig)) { (Ok(pk), Ok(sig)) => pk.verify_raw(&v.msg, &sig).is_ok(), _ => false, }; let mut sign_ok = None; if let (Some(sk), Some(aux)) = (v.seckey, v.aux) { let k = SigningKey::from_bytes(&FieldBytes::from(sk)).unwrap(); let pkb: [u8; 32] = k.verifying_key().to_bytes().into(); let s = k.sign_raw(&v.msg, &aux).map(|s| s.to_bytes()); sign_ok = Some(match s { Ok(b) => b.as_slice() == &v.sig[..] && pkb == v.pubkey, Err(_) => false, }); } details.push(VectorResult { index: v.index, expected: v.expected, verify_ok: verified == v.expected, sign_checked: sign_ok.is_some(), sign_ok, comment: v.comment.clone(), }); } summarize("k256 (RustCrypto)", details) } fn summarize(library: &str, details: Vec) -> VectorSummary { let n = details.len(); let verify_pass = details.iter().filter(|d| d.verify_ok).count(); let sign_cases = details.iter().filter(|d| d.sign_checked).count(); let sign_pass = details.iter().filter(|d| d.sign_ok == Some(true)).count(); VectorSummary { library: library.to_string(), n_vectors: n, verify_pass, sign_cases, sign_pass, all_pass: verify_pass == n && sign_pass == sign_cases, details, } }