use crate::{checksum, coef, LdwmParams, Signature, Winternitz, SHA256_LEN};
use sha2::{Digest, Sha256};
pub(crate) fn gen_ots_candidate<const N: usize>(
w: Winternitz,
m: usize,
msg_hash: &[u8],
sig: &[u8],
) -> [u8; N] {
let mut v = [0u8; N];
v.copy_from_slice(msg_hash);
let ck = checksum::<N>(w, &v);
let mask = (1 << usize::from(w)) - 1;
let mut hasher = Sha256::new();
let z = sig
.chunks_exact(m)
.enumerate()
.fold(Sha256::new(), |mut z, (i, xi)| {
let a = mask - coef::<N>(i, w, &v, &ck);
let mut buf = [0u8; SHA256_LEN];
buf[0..m].copy_from_slice(xi);
for _ in 0..a {
Digest::update(&mut hasher, &buf[0..m]);
buf = Digest::finalize_reset(&mut hasher).into();
}
Digest::update(&mut z, &buf[0..m]);
z
});
v.copy_from_slice(&Digest::finalize(z));
v
}
fn gen_mts_candidate<const N: usize>(
h: usize,
k: usize,
node_num: usize,
ots_result: &[u8],
auth_path: &[u8],
) -> [u8; N] {
let mut v: [u8; N] = [0u8; N];
v.copy_from_slice(ots_result);
(0..h).fold(v, |mut v, lvl| {
let posn = (node_num / (k.pow(lvl as u32))).rem_euclid(k);
let mut hasher = Sha256::new();
let start = N * (lvl * (k - 1));
let end = start + (N * posn);
Digest::update(&mut hasher, &auth_path[start..end]);
Digest::update(&mut hasher, v);
let start = start + N * posn;
let end = start + N * (k - posn - 1);
Digest::update(&mut hasher, &auth_path[start..end]);
v.copy_from_slice(&Digest::finalize(hasher));
v
})
}
pub fn verify(params: &LdwmParams, sig: &Signature, key: &[u8], msg: &[u8]) -> bool {
let hash: [u8; SHA256_LEN] = <Sha256 as Digest>::digest(msg).into();
let ots_key: [u8; SHA256_LEN] = gen_ots_candidate(params.w, params.m, &hash, sig.ots);
let mts_key: [u8; SHA256_LEN] =
gen_mts_candidate(params.h, params.k, sig.node_num, &ots_key, sig.auth_path);
mts_key == key
}
pub fn verify_from_hash(params: &LdwmParams, sig: &Signature, key: &[u8], msg_hash: &[u8]) -> bool {
let ots_key: [u8; SHA256_LEN] = gen_ots_candidate(params.w, params.m, msg_hash, sig.ots);
let mts_key: [u8; SHA256_LEN] =
gen_mts_candidate(params.h, params.k, sig.node_num, &ots_key, sig.auth_path);
mts_key == key
}
#[cfg(test)]
mod tests {
use super::{gen_mts_candidate, gen_ots_candidate, verify, verify_from_hash, Winternitz};
use crate::{LdwmParams, Signature, SHA256_LEN};
use sha2::{Digest, Sha256};
#[test]
fn test_rfc_ots() {
let w = Winternitz::W4;
let m = 20;
let key: &'static [u8] = include_bytes!("../resources/example_ots_pub.bin");
let sig = include_bytes!("../resources/example_ots.bin");
let message = "Hello world!\n".as_bytes();
let hash = <Sha256 as Digest>::digest(message);
let c: [u8; SHA256_LEN] = gen_ots_candidate(w, m, &hash, sig);
assert_eq!(&c, key);
}
#[test]
fn test_rfc_mts() {
let ots: &'static [u8] = include_bytes!("../resources/example_ots_pub.bin");
let auth_path: &'static [u8] = include_bytes!("../resources/example_auth_path.bin");
let key: &'static [u8] = include_bytes!("../resources/example_mts_pub.bin");
let c: [u8; SHA256_LEN] = gen_mts_candidate(2, 4, 0, ots, auth_path);
assert_eq!(key, c);
}
#[test]
fn test_rfc() {
let params = LdwmParams {
w: Winternitz::W4,
m: 20,
h: 2,
k: 4,
};
let msg = "Hello world!\n".as_bytes();
let key: &'static [u8; SHA256_LEN] = include_bytes!("../resources/example_mts_pub.bin");
let ots = include_bytes!("../resources/example_ots.bin");
let auth_path: &'static [u8] = include_bytes!("../resources/example_auth_path.bin");
let node_num = 0;
let sig = Signature {
auth_path,
ots,
node_num,
};
assert!(verify(¶ms, &sig, key, msg));
}
#[test]
fn test_rfc_with_hash() {
let params = LdwmParams {
w: Winternitz::W4,
m: 20,
h: 2,
k: 4,
};
let msg = "Hello world!\n".as_bytes();
let hash: [u8; SHA256_LEN] = <Sha256 as Digest>::digest(msg).into();
let key: &'static [u8; SHA256_LEN] = include_bytes!("../resources/example_mts_pub.bin");
let ots = include_bytes!("../resources/example_ots.bin");
let auth_path: &'static [u8] = include_bytes!("../resources/example_auth_path.bin");
let node_num = 0;
let sig = Signature {
auth_path,
ots,
node_num,
};
assert!(verify_from_hash(¶ms, &sig, key, &hash));
}
#[test]
fn test_rfc_failure() {
let params = LdwmParams {
w: Winternitz::W4,
m: 20,
h: 2,
k: 4,
};
let msg = "Hello world!\n".as_bytes();
let mut hash: [u8; SHA256_LEN] = <Sha256 as Digest>::digest(msg).into();
let key: &'static [u8; SHA256_LEN] = include_bytes!("../resources/example_mts_pub.bin");
let mut ots = include_bytes!("../resources/example_ots.bin").to_vec();
let mut auth_path = include_bytes!("../resources/example_auth_path.bin").to_vec();
let node_num = 0;
let sig = Signature {
auth_path: &auth_path,
ots: &ots,
node_num,
};
hash[0] = !hash[0];
assert!(!verify_from_hash(¶ms, &sig, key, &hash));
hash[0] = !hash[0];
ots[0] = !ots[0];
let sig = Signature {
auth_path: &auth_path,
ots: &ots,
node_num,
};
assert!(!verify_from_hash(¶ms, &sig, key, &hash));
ots[0] = !ots[0];
auth_path[0] = !auth_path[0];
let sig = Signature {
auth_path: &auth_path,
ots: &ots,
node_num,
};
assert!(!verify_from_hash(¶ms, &sig, key, &hash));
auth_path[0] = !auth_path[0];
let node_num = 1;
let sig = Signature {
auth_path: &auth_path,
ots: &ots,
node_num,
};
assert!(!verify_from_hash(¶ms, &sig, key, &hash));
let node_num = 0;
let sig = Signature {
auth_path: &auth_path,
ots: &ots,
node_num,
};
assert!(verify_from_hash(¶ms, &sig, key, &hash))
}
}