use md5::{Digest, Md5};
use sha2::Sha256;
pub fn generate_serial(mac: &str) -> String {
let mut hasher = Md5::new();
hasher.update(mac.as_bytes());
let hash = hasher.finalize();
hex::encode(hash)[..13].to_uppercase()
}
pub fn generate_device_id(mac: &str) -> String {
let mut hasher = Sha256::new();
hasher.update(mac.trim().as_bytes());
let hash = hasher.finalize();
hex::encode(hash).to_uppercase()
}
pub fn generate_signature(mac: &str, serial: &str, device_id: &str, device_id2: &str) -> String {
let data = format!("{mac}{serial}{device_id}{device_id2}");
let mut hasher = Sha256::new();
hasher.update(data.as_bytes());
let hash = hasher.finalize();
hex::encode(hash).to_uppercase()
}
pub fn generate_random_hex() -> String {
use rand::Rng;
let mut rng = rand::thread_rng();
let bytes: Vec<u8> = (0..20).map(|_| rng.r#gen()).collect();
hex::encode(bytes)
}
pub fn generate_token() -> String {
use rand::Rng;
const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
let mut rng = rand::thread_rng();
(0..32)
.map(|_| {
let idx = rng.gen_range(0..CHARSET.len());
CHARSET[idx] as char
})
.collect()
}
pub fn generate_prehash(token: &str) -> String {
use sha1::Digest;
let mut hasher = sha1::Sha1::new();
hasher.update(token.as_bytes());
let hash = hasher.finalize();
hex::encode(hash)
}
pub fn generate_metrics(mac: &str, serial: &str, random: &str) -> String {
serde_json::json!({
"mac": mac,
"sn": serial,
"type": "STB",
"model": "MAG250",
"uid": "",
"random": random,
})
.to_string()
}
pub fn generate_hw_version_2(mac: &str) -> String {
use sha1::Digest;
let mut hasher = sha1::Sha1::new();
hasher.update(mac.as_bytes());
let hash = hasher.finalize();
hex::encode(hash)
}
mod hex {
const HEX_CHARS: &[u8; 16] = b"0123456789abcdef";
pub fn encode(bytes: impl AsRef<[u8]>) -> String {
let bytes = bytes.as_ref();
let mut s = String::with_capacity(bytes.len() * 2);
for &b in bytes {
s.push(HEX_CHARS[(b >> 4) as usize] as char);
s.push(HEX_CHARS[(b & 0x0f) as usize] as char);
}
s
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn serial_from_mac_is_13_uppercase_hex() {
let serial = generate_serial("00:1A:79:AB:CD:EF");
assert_eq!(serial.len(), 13);
assert!(serial.chars().all(|c| c.is_ascii_hexdigit()));
assert_eq!(serial, serial.to_uppercase());
}
#[test]
fn serial_is_deterministic() {
let s1 = generate_serial("00:1A:79:AB:CD:EF");
let s2 = generate_serial("00:1A:79:AB:CD:EF");
assert_eq!(s1, s2);
}
#[test]
fn serial_differs_for_different_macs() {
let s1 = generate_serial("00:1A:79:AB:CD:EF");
let s2 = generate_serial("00:1A:79:AB:CD:FF");
assert_ne!(s1, s2);
}
#[test]
fn device_id_is_64_uppercase_hex() {
let device_id = generate_device_id("00:1A:79:AB:CD:EF");
assert_eq!(device_id.len(), 64);
assert!(device_id.chars().all(|c| c.is_ascii_hexdigit()));
assert_eq!(device_id, device_id.to_uppercase());
}
#[test]
fn device_id_is_deterministic() {
let d1 = generate_device_id("00:1A:79:AB:CD:EF");
let d2 = generate_device_id("00:1A:79:AB:CD:EF");
assert_eq!(d1, d2);
}
#[test]
fn signature_combines_all_fields() {
let mac = "00:1A:79:AB:CD:EF";
let serial = generate_serial(mac);
let device_id = generate_device_id(mac);
let sig = generate_signature(mac, &serial, &device_id, &device_id);
assert_eq!(sig.len(), 64);
assert!(sig.chars().all(|c| c.is_ascii_hexdigit()));
assert_eq!(sig, sig.to_uppercase());
}
#[test]
fn signature_is_deterministic() {
let mac = "00:1A:79:AB:CD:EF";
let serial = generate_serial(mac);
let device_id = generate_device_id(mac);
let s1 = generate_signature(mac, &serial, &device_id, &device_id);
let s2 = generate_signature(mac, &serial, &device_id, &device_id);
assert_eq!(s1, s2);
}
#[test]
fn prehash_is_sha1_of_token() {
let token = "TESTTOKEN123";
let prehash = generate_prehash(token);
assert_eq!(prehash.len(), 40);
assert!(prehash.chars().all(|c| c.is_ascii_hexdigit()));
}
#[test]
fn random_hex_is_40_chars() {
let r = generate_random_hex();
assert_eq!(r.len(), 40);
assert!(r.chars().all(|c| c.is_ascii_hexdigit()));
}
#[test]
fn token_is_32_uppercase_alphanumeric() {
let t = generate_token();
assert_eq!(t.len(), 32);
assert!(
t.chars()
.all(|c| c.is_ascii_uppercase() || c.is_ascii_digit())
);
}
#[test]
fn metrics_is_valid_json() {
let m = generate_metrics("00:1A:79:AB:CD:EF", "ABCDEF1234567", "aabbcc");
let parsed: serde_json::Value = serde_json::from_str(&m).unwrap();
assert_eq!(parsed["mac"], "00:1A:79:AB:CD:EF");
assert_eq!(parsed["sn"], "ABCDEF1234567");
assert_eq!(parsed["model"], "MAG250");
}
#[test]
fn hw_version_2_is_sha1_of_mac() {
let hw = generate_hw_version_2("00:1A:79:AB:CD:EF");
assert_eq!(hw.len(), 40);
}
}