1//! BIP340 official test vectors (data/vectors/bip340_test_vectors.csv) checked against both2//! libsecp256k1 (rust-secp256k1) and k256.34use serde::Serialize;56#[derive(Debug, Clone)]7pub struct Vector {8 pub index: u32,9 pub seckey: Option<[u8; 32]>,10 pub pubkey: [u8; 32],11 pub aux: Option<[u8; 32]>,12 pub msg: Vec<u8>,13 pub sig: [u8; 64],14 pub expected: bool,15 pub comment: String,16}1718pub fn hex(s: &str) -> Vec<u8> {19 (0..s.len()).step_by(2).map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap()).collect()20}2122pub fn to_hex(b: &[u8]) -> String {23 b.iter().map(|x| format!("{:02x}", x)).collect()24}2526fn arr32(v: &[u8]) -> [u8; 32] {27 let mut a = [0u8; 32];28 a.copy_from_slice(v);29 a30}3132pub fn parse_csv(text: &str) -> Vec<Vector> {33 let mut out = Vec::new();34 for (ln, line) in text.lines().enumerate() {35 if ln == 0 || line.trim().is_empty() {36 continue;37 }38 let f: Vec<&str> = line.split(',').collect();39 assert!(f.len() >= 7, "bad CSV line {}", ln);40 let sig = hex(f[5]);41 let mut s64 = [0u8; 64];42 s64.copy_from_slice(&sig);43 out.push(Vector {44 index: f[0].parse().unwrap(),45 seckey: if f[1].is_empty() { None } else { Some(arr32(&hex(f[1]))) },46 pubkey: arr32(&hex(f[2])),47 aux: if f[3].is_empty() { None } else { Some(arr32(&hex(f[3]))) },48 msg: hex(f[4]),49 sig: s64,50 expected: f[6].trim().eq_ignore_ascii_case("TRUE"),51 comment: f.get(7).map(|s| s.trim().to_string()).unwrap_or_default(),52 });53 }54 out55}5657#[derive(Serialize, Debug, Clone)]58pub struct VectorResult {59 pub index: u32,60 pub expected: bool,61 pub verify_ok: bool,62 pub sign_checked: bool,63 pub sign_ok: Option<bool>,64 pub comment: String,65}6667#[derive(Serialize, Debug, Clone)]68pub struct VectorSummary {69 pub library: String,70 pub n_vectors: usize,71 pub verify_pass: usize,72 pub sign_cases: usize,73 pub sign_pass: usize,74 pub all_pass: bool,75 pub details: Vec<VectorResult>,76}7778pub fn check_secp(vs: &[Vector]) -> VectorSummary {79 use secp256k1::{schnorr, Keypair, SecretKey, XOnlyPublicKey};80 let mut details = Vec::new();81 for v in vs {82 let sig = schnorr::Signature::from_byte_array(v.sig);83 let verified = match XOnlyPublicKey::from_byte_array(v.pubkey) {84 Ok(pk) => schnorr::verify(&sig, &v.msg, &pk).is_ok(),85 Err(_) => false,86 };87 let mut sign_ok = None;88 if let (Some(sk), Some(aux)) = (v.seckey, v.aux) {89 let kp = Keypair::from_secret_key(&SecretKey::from_secret_bytes(sk).unwrap());90 let (xo, _) = kp.x_only_public_key();91 let s = schnorr::sign_with_aux_rand(&v.msg, &kp, &aux);92 sign_ok = Some(s.to_byte_array() == v.sig && xo.to_byte_array() == v.pubkey);93 }94 details.push(VectorResult {95 index: v.index,96 expected: v.expected,97 verify_ok: verified == v.expected,98 sign_checked: sign_ok.is_some(),99 sign_ok,100 comment: v.comment.clone(),101 });102 }103 summarize("libsecp256k1 (rust-secp256k1)", details)104}105106pub fn check_k256(vs: &[Vector]) -> VectorSummary {107 use k256::schnorr::{Signature, SigningKey, VerifyingKey};108 use k256::FieldBytes;109 let mut details = Vec::new();110 for v in vs {111 let verified = match (VerifyingKey::from_bytes(&FieldBytes::from(v.pubkey)), Signature::from_slice(&v.sig)) {112 (Ok(pk), Ok(sig)) => pk.verify_raw(&v.msg, &sig).is_ok(),113 _ => false,114 };115 let mut sign_ok = None;116 if let (Some(sk), Some(aux)) = (v.seckey, v.aux) {117 let k = SigningKey::from_bytes(&FieldBytes::from(sk)).unwrap();118 let pkb: [u8; 32] = k.verifying_key().to_bytes().into();119 let s = k.sign_raw(&v.msg, &aux).map(|s| s.to_bytes());120 sign_ok = Some(match s {121 Ok(b) => b.as_slice() == &v.sig[..] && pkb == v.pubkey,122 Err(_) => false,123 });124 }125 details.push(VectorResult {126 index: v.index,127 expected: v.expected,128 verify_ok: verified == v.expected,129 sign_checked: sign_ok.is_some(),130 sign_ok,131 comment: v.comment.clone(),132 });133 }134 summarize("k256 (RustCrypto)", details)135}136137fn summarize(library: &str, details: Vec<VectorResult>) -> VectorSummary {138 let n = details.len();139 let verify_pass = details.iter().filter(|d| d.verify_ok).count();140 let sign_cases = details.iter().filter(|d| d.sign_checked).count();141 let sign_pass = details.iter().filter(|d| d.sign_ok == Some(true)).count();142 VectorSummary {143 library: library.to_string(),144 n_vectors: n,145 verify_pass,146 sign_cases,147 sign_pass,148 all_pass: verify_pass == n && sign_pass == sign_cases,149 details,150 }151}152