use crate::aes::aes_h;
use crate::ec::{
scalar_add, scalar_inv, scalar_mul, scalar_reduce, scalar_reduce_wide, Point, N, U160,
};
pub const SHA1_LEN: usize = 20;
pub type Signature = [u8; 40];
pub fn sha1(data: &[u8]) -> [u8; 20] {
let mut h: [u32; 5] = [
0x6745_2301,
0xEFCD_AB89,
0x98BA_DCFE,
0x1032_5476,
0xC3D2_E1F0,
];
let bit_len = (data.len() as u64).wrapping_mul(8);
let mut msg = data.to_vec();
msg.push(0x80);
while msg.len() % 64 != 56 {
msg.push(0x00);
}
msg.extend_from_slice(&bit_len.to_be_bytes());
for block in msg.chunks_exact(64) {
let mut w = [0u32; 80];
for (i, word) in w.iter_mut().take(16).enumerate() {
*word = u32::from_be_bytes([
block[4 * i],
block[4 * i + 1],
block[4 * i + 2],
block[4 * i + 3],
]);
}
for i in 16..80 {
w[i] = (w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]).rotate_left(1);
}
let [mut a, mut b, mut c, mut d, mut e] = h;
for (i, &wi) in w.iter().enumerate() {
let (f, k) = match i {
0..=19 => ((b & c) | ((!b) & d), 0x5A82_7999u32),
20..=39 => (b ^ c ^ d, 0x6ED9_EBA1),
40..=59 => ((b & c) | (b & d) | (c & d), 0x8F1B_BCDC),
_ => (b ^ c ^ d, 0xCA62_C1D6),
};
let tmp = a
.rotate_left(5)
.wrapping_add(f)
.wrapping_add(e)
.wrapping_add(k)
.wrapping_add(wi);
e = d;
d = c;
c = b.rotate_left(30);
b = a;
a = tmp;
}
h[0] = h[0].wrapping_add(a);
h[1] = h[1].wrapping_add(b);
h[2] = h[2].wrapping_add(c);
h[3] = h[3].wrapping_add(d);
h[4] = h[4].wrapping_add(e);
}
let mut out = [0u8; 20];
for (i, word) in h.iter().enumerate() {
out[4 * i..4 * i + 4].copy_from_slice(&word.to_be_bytes());
}
out
}
fn digest_to_scalar(digest: &[u8; 20]) -> U160 {
scalar_reduce(U160::from_be_bytes(digest))
}
pub fn sign_with_k(priv_key: &U160, data: &[u8], k: &U160) -> Option<Signature> {
let e = digest_to_scalar(&sha1(data));
let mut k = scalar_reduce(*k);
for attempt in 0..8u8 {
if k.is_zero() {
k = rederive_k(&k, attempt);
continue;
}
let r_point = Point::generator().mul_scalar(&k);
if r_point.is_infinity() {
k = rederive_k(&k, attempt);
continue;
}
let r = scalar_reduce(r_point.x_u160());
if r.is_zero() {
k = rederive_k(&k, attempt);
continue;
}
let kinv = scalar_inv(&k);
let rd = scalar_mul(&r, priv_key);
let s = scalar_mul(&kinv, &scalar_add(&e, &rd));
if s.is_zero() {
k = rederive_k(&k, attempt);
continue;
}
let mut sig = [0u8; 40];
sig[..20].copy_from_slice(&r.to_be_bytes());
sig[20..].copy_from_slice(&s.to_be_bytes());
return Some(sig);
}
None
}
pub fn derive_k(priv_key: &U160, data: &[u8]) -> U160 {
let mut counter = 0u8;
loop {
let mut buf = Vec::with_capacity(20 + data.len() + 1);
buf.extend_from_slice(&priv_key.to_be_bytes());
buf.extend_from_slice(data);
buf.push(counter);
let h = aes_h(&buf);
let extra = sha1(&h);
let mut wide = [0u8; 20];
wide[..16].copy_from_slice(&h);
wide[16..].copy_from_slice(&extra[..4]);
let k = scalar_reduce(U160::from_be_bytes(&wide));
if !k.is_zero() {
return k;
}
counter = counter.wrapping_add(1);
}
}
fn rederive_k(prev: &U160, attempt: u8) -> U160 {
let mut buf = Vec::with_capacity(21);
buf.extend_from_slice(&prev.to_be_bytes());
buf.push(attempt.wrapping_add(1));
let h = aes_h(&buf);
let extra = sha1(&h);
let mut wide = [0u8; 20];
wide[..16].copy_from_slice(&h);
wide[16..].copy_from_slice(&extra[..4]);
scalar_reduce(U160::from_be_bytes(&wide))
}
pub fn sign(priv_key: &U160, data: &[u8]) -> Signature {
let k = derive_k(priv_key, data);
sign_with_k(priv_key, data, &k).expect("ECDSA sign retries exhausted")
}
pub fn verify(pub_key: &Point, sig: &Signature, data: &[u8]) -> bool {
let mut r_bytes = [0u8; 20];
let mut s_bytes = [0u8; 20];
r_bytes.copy_from_slice(&sig[..20]);
s_bytes.copy_from_slice(&sig[20..]);
let r = U160::from_be_bytes(&r_bytes);
let s = U160::from_be_bytes(&s_bytes);
if r.is_zero() || s.is_zero() {
return false;
}
if r.cmp(&N) != core::cmp::Ordering::Less || s.cmp(&N) != core::cmp::Ordering::Less {
return false;
}
if pub_key.is_infinity() || !pub_key.is_on_curve() {
return false;
}
let e = digest_to_scalar(&sha1(data));
let w = scalar_inv(&s);
let u1 = scalar_mul(&e, &w);
let u2 = scalar_mul(&r, &w);
let x = Point::generator()
.mul_scalar(&u1)
.add(&pub_key.mul_scalar(&u2));
if x.is_infinity() {
return false;
}
let v = scalar_reduce(x.x_u160());
v == r
}
pub fn scalar_from_wide_digest(wide: &[u32; 10]) -> U160 {
scalar_reduce_wide(wide)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ec::Point;
#[test]
fn sha1_abc_vector() {
assert_eq!(
sha1(b"abc"),
[
0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, 0xba, 0x3e, 0x25, 0x71, 0x78, 0x50,
0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d
]
);
}
#[test]
fn sha1_empty_vector() {
assert_eq!(
sha1(b""),
[
0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, 0xbf, 0xef, 0x95, 0x60,
0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09
]
);
}
#[test]
fn sha1_two_block_vector() {
let msg = b"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
assert_eq!(
sha1(msg),
[
0x84, 0x98, 0x3e, 0x44, 0x1c, 0x3b, 0xd2, 0x6e, 0xba, 0xae, 0x4a, 0xa1, 0xf9, 0x51,
0x29, 0xe5, 0xe5, 0x46, 0x70, 0xf1
]
);
}
fn small_scalar(v: u32) -> U160 {
U160 {
limbs: [v, 0, 0, 0, 0],
}
}
#[test]
fn sign_then_verify_roundtrips() {
let d = small_scalar(0x1357_9bdf);
let q = Point::generator().mul_scalar(&d);
let msg = b"AACS drive authentication test message";
let sig = sign(&d, msg);
assert!(verify(&q, &sig, msg));
}
#[test]
fn verify_rejects_tampered_message() {
let d = small_scalar(0x2468_ace0);
let q = Point::generator().mul_scalar(&d);
let sig = sign(&d, b"original");
assert!(!verify(&q, &sig, b"tampered"));
}
#[test]
fn verify_rejects_wrong_key() {
let d = small_scalar(99);
let other = small_scalar(100);
let wrong_q = Point::generator().mul_scalar(&other);
let sig = sign(&d, b"msg");
assert!(!verify(&wrong_q, &sig, b"msg"));
}
#[test]
fn verify_rejects_zero_components() {
let d = small_scalar(7);
let q = Point::generator().mul_scalar(&d);
let zero_sig = [0u8; 40];
assert!(!verify(&q, &zero_sig, b"msg"));
}
#[test]
fn explicit_k_matches_textbook_equations() {
let d = small_scalar(0x0011_2233);
let q = Point::generator().mul_scalar(&d);
let k = small_scalar(0x00aa_bbcc);
let sig = sign_with_k(&d, b"vector", &k).unwrap();
assert!(verify(&q, &sig, b"vector"));
}
#[test]
fn matches_independent_reference_vector() {
let d = small_scalar(0x0011_2233);
let k = small_scalar(0x00aa_bbcc);
let q = Point::generator().mul_scalar(&d);
let qx: [u8; 20] = [
0x74, 0x02, 0x9e, 0x29, 0x07, 0xa5, 0x98, 0x0d, 0x4d, 0x5d, 0x09, 0x11, 0xbc, 0x3c,
0x6a, 0x6d, 0x5d, 0xe5, 0x94, 0x71,
];
let qy: [u8; 20] = [
0x5b, 0xde, 0xf9, 0x76, 0xe4, 0xb9, 0xe0, 0xf7, 0xac, 0xbf, 0xf6, 0xed, 0xae, 0x55,
0xaf, 0x8f, 0x88, 0x80, 0xab, 0x5e,
];
assert_eq!(
q,
Point::from_coords(&qx, &qy).expect("reference Q must be on curve")
);
let expected: [u8; 40] = [
0x27, 0xdd, 0x46, 0xeb, 0x6c, 0x9d, 0x11, 0x39, 0xdf, 0x0f, 0xaa, 0xe2, 0x32, 0xe9,
0xc1, 0x04, 0x6e, 0xcb, 0x82, 0x4b, 0x81, 0x11, 0x0c, 0x40, 0x4d, 0xe4, 0x59, 0xc5,
0xcb, 0x6e, 0x43, 0x3e, 0x91, 0x99, 0xb5, 0x9c, 0x3e, 0x1f, 0xe3, 0x2c,
];
let sig = sign_with_k(&d, b"vector", &k).unwrap();
assert_eq!(sig, expected, "signature must match independent reference");
assert!(verify(&q, &sig, b"vector"));
}
}