SPB Git

spb/wp34_uqo

Public
4.9 KB · 152 lines rust
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
Files · main
Menu

Theme