pub(crate) const VC: [u8; 8] = [0u8; 8];
#[allow(dead_code)]
pub(crate) const MAX_PADDING: usize = 512;
pub const CRYPTO_PLAINTEXT: u32 = 0x01;
pub const CRYPTO_RC4: u32 = 0x02;
#[allow(dead_code)]
fn mse_hash(data: &[u8]) -> [u8; 20] {
let hash = ring::digest::digest(&ring::digest::SHA1_FOR_LEGACY_USE_ONLY, data);
let mut out = [0u8; 20];
out.copy_from_slice(hash.as_ref());
out
}
fn mse_hash2(a: &[u8], b: &[u8]) -> [u8; 20] {
let mut ctx = ring::digest::Context::new(&ring::digest::SHA1_FOR_LEGACY_USE_ONLY);
ctx.update(a);
ctx.update(b);
let hash = ctx.finish();
let mut out = [0u8; 20];
out.copy_from_slice(hash.as_ref());
out
}
fn mse_hash3(a: &[u8], b: &[u8], c: &[u8]) -> [u8; 20] {
let mut ctx = ring::digest::Context::new(&ring::digest::SHA1_FOR_LEGACY_USE_ONLY);
ctx.update(a);
ctx.update(b);
ctx.update(c);
let hash = ctx.finish();
let mut out = [0u8; 20];
out.copy_from_slice(hash.as_ref());
out
}
pub(crate) fn sync_marker(shared_secret: &[u8]) -> [u8; 20] {
mse_hash2(b"req1", shared_secret)
}
pub(crate) fn skey_hash(skey: &[u8]) -> [u8; 20] {
mse_hash2(b"req2", skey)
}
pub(crate) fn req3_hash(shared_secret: &[u8]) -> [u8; 20] {
mse_hash2(b"req3", shared_secret)
}
pub(crate) fn skey_proof(skey: &[u8], shared_secret: &[u8]) -> [u8; 20] {
let h2 = skey_hash(skey);
let h3 = req3_hash(shared_secret);
let mut result = [0u8; 20];
for i in 0..20 {
result[i] = h2[i] ^ h3[i];
}
result
}
pub(crate) fn key_a(shared_secret: &[u8], skey: &[u8]) -> [u8; 20] {
mse_hash3(b"keyA", shared_secret, skey)
}
pub(crate) fn key_b(shared_secret: &[u8], skey: &[u8]) -> [u8; 20] {
mse_hash3(b"keyB", shared_secret, skey)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn vc_is_eight_zeros() {
assert_eq!(VC, [0u8; 8]);
}
#[test]
fn hash_deterministic() {
let h1 = mse_hash2(b"req1", b"shared_secret");
let h2 = mse_hash2(b"req1", b"shared_secret");
assert_eq!(h1, h2);
}
#[test]
fn hash_differs_for_different_input() {
let h1 = mse_hash2(b"req1", b"secret_a");
let h2 = mse_hash2(b"req1", b"secret_b");
assert_ne!(h1, h2);
}
#[test]
fn skey_proof_xor_correct() {
let skey = [1u8; 20];
let secret = [2u8; 96];
let proof = skey_proof(&skey, &secret);
let h3 = req3_hash(&secret);
let mut recovered = [0u8; 20];
for i in 0..20 {
recovered[i] = proof[i] ^ h3[i];
}
assert_eq!(recovered, skey_hash(&skey));
}
#[test]
fn mse_key_derivation_deterministic() {
let shared_secret = [0x42u8; 96];
let skey = [0xDEu8; 20];
let ka1 = key_a(&shared_secret, &skey);
let kb1 = key_b(&shared_secret, &skey);
let ka2 = key_a(&shared_secret, &skey);
let kb2 = key_b(&shared_secret, &skey);
assert_eq!(ka1, ka2, "key_a must be deterministic for same inputs");
assert_eq!(kb1, kb2, "key_b must be deterministic for same inputs");
let sm1 = sync_marker(&shared_secret);
let sm2 = sync_marker(&shared_secret);
assert_eq!(sm1, sm2, "sync_marker must be deterministic");
let sp1 = skey_proof(&skey, &shared_secret);
let sp2 = skey_proof(&skey, &shared_secret);
assert_eq!(sp1, sp2, "skey_proof must be deterministic");
}
#[test]
fn key_a_and_key_b_differ() {
let secret = [0xABu8; 96];
let skey = [0xCDu8; 20];
assert_ne!(key_a(&secret, &skey), key_b(&secret, &skey));
}
}