use super::curve::Point;
use super::scalar::Scalar;
pub fn verify(pubkey: &[u8; 33], r: &[u8; 32], s: &[u8; 32], z: &[u8; 32]) -> bool {
let r_s = Scalar::from_bytes(r);
let s_s = Scalar::from_bytes(s);
if r_s.is_zero() || s_s.is_zero() {
return false;
}
let Some(q) = Point::from_sec1(pubkey) else {
return false;
};
let w = s_s.inv();
let u1 = Scalar::from_bytes(z).mul(&w);
let u2 = r_s.mul(&w);
let big_r = Point::generator().scalar_mul(&u1.to_bytes()).add(&q.scalar_mul(&u2.to_bytes()));
let Some((rx, _)) = big_r.to_affine() else {
return false; };
Scalar::from_bytes(&rx.to_bytes()) == r_s
}
#[cfg(test)]
mod tests {
use super::*;
use k256::ecdsa::signature::hazmat::PrehashSigner;
use k256::ecdsa::{Signature, SigningKey};
fn sign(key_bytes: &[u8; 32], z: &[u8; 32]) -> ([u8; 33], [u8; 32], [u8; 32]) {
let sk = SigningKey::from_slice(key_bytes).unwrap();
let sig: Signature = sk.sign_prehash(z).unwrap();
let raw = sig.to_bytes(); let mut r = [0u8; 32];
let mut s = [0u8; 32];
r.copy_from_slice(&raw[..32]);
s.copy_from_slice(&raw[32..]);
let mut pubkey = [0u8; 33];
pubkey.copy_from_slice(sk.verifying_key().to_encoded_point(true).as_bytes());
(pubkey, r, s)
}
#[test]
fn accepts_valid_k256_signatures() {
let keys: [[u8; 32]; 3] = [[1u8; 32], [7u8; 32], [0x9Au8; 32]];
let msgs: [[u8; 32]; 3] = [[0x11u8; 32], [0xABu8; 32], [0x42u8; 32]];
for k in &keys {
for z in &msgs {
let (pk, r, s) = sign(k, z);
assert!(verify(&pk, &r, &s, z), "valid signature must verify");
}
}
}
#[test]
fn rejects_wrong_message() {
let (pk, r, s) = sign(&[3u8; 32], &[0x55u8; 32]);
let other = [0x56u8; 32];
assert!(!verify(&pk, &r, &s, &other), "signature must not verify a different digest");
}
#[test]
fn rejects_tampered_signature() {
let z = [0x77u8; 32];
let (pk, mut r, s) = sign(&[5u8; 32], &z);
r[31] ^= 0x01;
assert!(!verify(&pk, &r, &s, &z), "flipped r must fail");
}
#[test]
fn rejects_wrong_pubkey() {
let z = [0x33u8; 32];
let (_pk, r, s) = sign(&[9u8; 32], &z);
let (other_pk, _, _) = sign(&[10u8; 32], &z);
assert!(!verify(&other_pk, &r, &s, &z), "another key must not verify");
}
#[test]
fn differential_against_k256_on_random_inputs() {
let mut state = 0x1234_5678_9ABC_DEF0u64;
let mut next = || {
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;
state
};
let mut checked = 0;
for _ in 0..60 {
let mut kb = [0u8; 32];
let mut zb = [0u8; 32];
for c in kb.chunks_mut(8) {
c.copy_from_slice(&next().to_le_bytes());
}
for c in zb.chunks_mut(8) {
c.copy_from_slice(&next().to_le_bytes());
}
if SigningKey::from_slice(&kb).is_err() {
continue;
}
let (pk, r, s) = sign(&kb, &zb);
assert!(verify(&pk, &r, &s, &zb), "must accept a genuine k256 signature");
let mut z_bad = zb;
z_bad[(next() as usize) % 32] ^= 1;
if z_bad != zb {
assert!(!verify(&pk, &r, &s, &z_bad), "must reject a corrupted digest");
}
checked += 1;
}
assert!(checked >= 40, "exercised enough random signatures ({checked})");
}
#[test]
fn rejects_zero_scalars() {
let (pk, _r, s) = sign(&[2u8; 32], &[0x01u8; 32]);
assert!(!verify(&pk, &[0u8; 32], &s, &[0x01u8; 32]), "r = 0 rejected");
}
}