use super::tables::HashFn;
use sha2::{Digest, Sha256};
use sha3::Sha3_256;
pub const SUBFRAME_S: u32 = 30;
pub const MAX_STEPS: u32 = 2880;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum KeyError {
BadTiming,
ChainMismatch,
BadLength,
}
fn digest(hf: HashFn, data: &[u8]) -> Vec<u8> {
match hf {
HashFn::Sha256 => Sha256::digest(data).to_vec(),
HashFn::Sha3_256 => Sha3_256::digest(data).to_vec(),
}
}
pub fn step(hf: HashFn, key: &[u8], gst: u32, alpha: &[u8; 6], key_bits: usize) -> Vec<u8> {
let mut m = Vec::with_capacity(key.len() + 10);
m.extend_from_slice(key);
m.extend_from_slice(&super::gst_pack(gst).to_be_bytes());
m.extend_from_slice(alpha);
let mut out = digest(hf, &m);
out.truncate(key_bits / 8);
out
}
pub fn verify_key(
hf: HashFn,
key: &[u8],
key_gst: u32,
trusted: &[u8],
trusted_gst: u32,
alpha: &[u8; 6],
) -> Result<(), KeyError> {
let key_bits = trusted.len() * 8;
if key.len() != trusted.len() {
return Err(KeyError::BadLength);
}
let span = key_gst
.checked_sub(trusted_gst)
.ok_or(KeyError::BadTiming)?;
if span == 0 || span % SUBFRAME_S != 0 || span / SUBFRAME_S > MAX_STEPS {
return Err(KeyError::BadTiming);
}
let mut cur = key.to_vec();
let mut gst = key_gst;
while gst > trusted_gst {
gst -= SUBFRAME_S;
cur = step(hf, &cur, gst, alpha, key_bits);
}
if cur == trusted {
Ok(())
} else {
Err(KeyError::ChainMismatch)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn chain(n: u32, gst0: u32, alpha: &[u8; 6], bits: usize) -> Vec<Vec<u8>> {
let seed: Vec<u8> = (0..bits / 8).map(|i| (i * 31 + 7) as u8).collect();
let mut seq = vec![seed];
for i in (0..n).rev() {
let gst = gst0 + 30 * i - 30;
let next = step(HashFn::Sha256, seq.last().unwrap(), gst, alpha, bits);
seq.push(next);
}
seq.reverse(); seq
}
const ALPHA: [u8; 6] = [0xA1, 0xB2, 0xC3, 0xD4, 0xE5, 0xF6];
const GST0: u32 = 1248 * crate::osnma::WEEK_S + 345_600;
#[test]
fn chain_keys_verify_back_to_root_and_to_each_other() {
for bits in [96, 128, 256] {
let c = chain(40, GST0, &ALPHA, bits);
let root_gst = GST0 - 30;
for i in [1usize, 2, 17, 40] {
let kgst = GST0 + 30 * (i as u32 - 1);
assert_eq!(
verify_key(HashFn::Sha256, &c[i], kgst, &c[0], root_gst, &ALPHA),
Ok(()),
"bits {bits} i {i}"
);
}
assert_eq!(
verify_key(
HashFn::Sha256,
&c[30],
GST0 + 30 * 29,
&c[12],
GST0 + 30 * 11,
&ALPHA
),
Ok(())
);
}
}
#[test]
fn corrupted_or_misused_keys_are_rejected() {
let c = chain(10, GST0, &ALPHA, 128);
let root_gst = GST0 - 30;
let kgst = GST0 + 30 * 4;
let ok =
|k: &[u8], t: u32, a: &[u8; 6]| verify_key(HashFn::Sha256, k, t, &c[0], root_gst, a);
assert_eq!(ok(&c[5], kgst, &ALPHA), Ok(()));
let mut bad = c[5].clone();
bad[0] ^= 1;
assert_eq!(ok(&bad, kgst, &ALPHA), Err(KeyError::ChainMismatch));
assert_eq!(ok(&c[5], kgst + 30, &ALPHA), Err(KeyError::ChainMismatch)); let mut a = ALPHA;
a[5] ^= 1;
assert_eq!(ok(&c[5], kgst, &a), Err(KeyError::ChainMismatch)); assert_eq!(ok(&c[5], kgst + 1, &ALPHA), Err(KeyError::BadTiming));
assert_eq!(ok(&c[5], root_gst, &ALPHA), Err(KeyError::BadTiming));
assert_eq!(ok(&c[5][..15], kgst, &ALPHA), Err(KeyError::BadLength));
}
#[test]
fn a_key_further_ahead_than_the_step_bound_is_refused_without_the_work() {
let c = chain(3, GST0, &ALPHA, 128);
let far = GST0 - 30 + SUBFRAME_S * (MAX_STEPS + 1);
assert_eq!(
verify_key(HashFn::Sha256, &c[1], far, &c[0], GST0 - 30, &ALPHA),
Err(KeyError::BadTiming)
);
}
#[test]
fn sha3_256_known_answers() {
assert_eq!(
hex::encode(digest(HashFn::Sha3_256, b"")),
"a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a"
);
assert_eq!(
hex::encode(digest(HashFn::Sha3_256, b"abc")),
"3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532"
);
}
#[test]
fn sha3_chains_verify_and_differ_from_sha2_chains() {
let seed: Vec<u8> = (0..16u8).collect();
let mut keys = vec![seed];
for i in (0..6u32).rev() {
let k = step(
HashFn::Sha3_256,
keys.last().unwrap(),
GST0 + 30 * i - 30,
&ALPHA,
128,
);
keys.push(k);
}
keys.reverse();
assert_eq!(
verify_key(
HashFn::Sha3_256,
&keys[4],
GST0 + 90,
&keys[0],
GST0 - 30,
&ALPHA
),
Ok(())
);
assert_eq!(
verify_key(
HashFn::Sha256,
&keys[4],
GST0 + 90,
&keys[0],
GST0 - 30,
&ALPHA
),
Err(KeyError::ChainMismatch)
);
}
}