|
use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _}; |
|
use rand::Rng; |
|
use sha2::{Digest, Sha256}; |
|
|
|
pub fn generate_hash() -> String { |
|
let random_bytes = rand::thread_rng().gen::<[u8; 32]>(); |
|
let mut hasher = Sha256::new(); |
|
hasher.update(random_bytes); |
|
hex::encode(hasher.finalize()) |
|
} |
|
|
|
fn obfuscate_bytes(bytes: &mut [u8]) { |
|
let mut prev: u8 = 165; |
|
for (idx, byte) in bytes.iter_mut().enumerate() { |
|
let old_value = *byte; |
|
*byte = (old_value ^ prev).wrapping_add((idx % 256) as u8); |
|
prev = *byte; |
|
} |
|
} |
|
|
|
fn deobfuscate_bytes(bytes: &mut [u8]) { |
|
let mut prev: u8 = 165; |
|
for (idx, byte) in bytes.iter_mut().enumerate() { |
|
let temp = *byte; |
|
*byte = (*byte).wrapping_sub((idx % 256) as u8) ^ prev; |
|
prev = temp; |
|
} |
|
} |
|
|
|
pub fn generate_timestamp_header() -> String { |
|
let timestamp = std::time::SystemTime::now() |
|
.duration_since(std::time::UNIX_EPOCH) |
|
.unwrap() |
|
.as_secs() |
|
/ 1_000; |
|
|
|
let mut timestamp_bytes = vec![ |
|
((timestamp >> 8) & 0xFF) as u8, |
|
(0xFF & timestamp) as u8, |
|
((timestamp >> 24) & 0xFF) as u8, |
|
((timestamp >> 16) & 0xFF) as u8, |
|
((timestamp >> 8) & 0xFF) as u8, |
|
(0xFF & timestamp) as u8, |
|
]; |
|
|
|
obfuscate_bytes(&mut timestamp_bytes); |
|
BASE64.encode(×tamp_bytes) |
|
} |
|
|
|
fn generate_checksum(device_id: &str, mac_addr: Option<&str>) -> String { |
|
let encoded = generate_timestamp_header(); |
|
match mac_addr { |
|
Some(mac) => format!("{}{}/{}", encoded, device_id, mac), |
|
None => format!("{}{}", encoded, device_id), |
|
} |
|
} |
|
|
|
pub fn generate_checksum_with_default() -> String { |
|
generate_checksum(&generate_hash(), Some(&generate_hash())) |
|
} |
|
|
|
pub fn generate_checksum_with_repair(checksum: &str) -> String { |
|
|
|
if checksum.is_empty() |
|
|| !checksum |
|
.chars() |
|
.all(|c| (c.is_ascii_alphanumeric() || c == '/' || c == '+' || c == '=')) |
|
{ |
|
return generate_checksum_with_default(); |
|
} |
|
|
|
|
|
fn try_fix_timestamp(timestamp_base64: &str) -> Option<String> { |
|
if let Ok(timestamp_bytes) = BASE64.decode(timestamp_base64) { |
|
if timestamp_bytes.len() == 6 { |
|
let mut fixed_bytes = timestamp_bytes.clone(); |
|
deobfuscate_bytes(&mut fixed_bytes); |
|
|
|
|
|
if fixed_bytes[0..3].iter().all(|&x| x == 0) { |
|
|
|
let timestamp = ((fixed_bytes[2] as u64) << 24) |
|
| ((fixed_bytes[3] as u64) << 16) |
|
| ((fixed_bytes[4] as u64) << 8) |
|
| (fixed_bytes[5] as u64); |
|
|
|
let current_timestamp = std::time::SystemTime::now() |
|
.duration_since(std::time::UNIX_EPOCH) |
|
.unwrap() |
|
.as_secs() |
|
/ 1_000; |
|
|
|
if timestamp <= current_timestamp { |
|
|
|
fixed_bytes[0] = fixed_bytes[4]; |
|
fixed_bytes[1] = fixed_bytes[5]; |
|
|
|
obfuscate_bytes(&mut fixed_bytes); |
|
return Some(BASE64.encode(&fixed_bytes)); |
|
} |
|
} |
|
} |
|
} |
|
None |
|
} |
|
|
|
if checksum.len() == 8 { |
|
|
|
if let Some(fixed_timestamp) = try_fix_timestamp(checksum) { |
|
return format!("{}{}/{}", fixed_timestamp, generate_hash(), generate_hash()); |
|
} |
|
|
|
|
|
if let Some(timestamp) = extract_time_ks(checksum) { |
|
let current_timestamp = std::time::SystemTime::now() |
|
.duration_since(std::time::UNIX_EPOCH) |
|
.unwrap() |
|
.as_secs() |
|
/ 1_000; |
|
|
|
if timestamp <= current_timestamp { |
|
return format!("{}{}/{}", checksum, generate_hash(), generate_hash()); |
|
} |
|
} |
|
} else if checksum.len() > 8 { |
|
|
|
let parts: Vec<&str> = checksum.split('/').collect(); |
|
match parts.len() { |
|
1 => { |
|
let timestamp_base64 = &checksum[..8]; |
|
let device_id = &checksum[8..]; |
|
|
|
if is_valid_hash(device_id) { |
|
|
|
if let Some(fixed_timestamp) = try_fix_timestamp(timestamp_base64) { |
|
return format!("{}{}/{}", fixed_timestamp, device_id, generate_hash()); |
|
} |
|
|
|
|
|
if let Some(timestamp) = extract_time_ks(timestamp_base64) { |
|
let current_timestamp = std::time::SystemTime::now() |
|
.duration_since(std::time::UNIX_EPOCH) |
|
.unwrap() |
|
.as_secs() |
|
/ 1_000; |
|
|
|
if timestamp <= current_timestamp { |
|
return format!( |
|
"{}{}/{}", |
|
generate_timestamp_header(), |
|
device_id, |
|
generate_hash() |
|
); |
|
} |
|
} |
|
} |
|
} |
|
2 => { |
|
let first_part = parts[0]; |
|
let mac_hash = parts[1]; |
|
|
|
if is_valid_hash(mac_hash) && first_part.len() == mac_hash.len() + 8 { |
|
let timestamp_base64 = &first_part[..8]; |
|
let device_id = &first_part[8..]; |
|
|
|
if is_valid_hash(device_id) { |
|
|
|
if let Some(fixed_timestamp) = try_fix_timestamp(timestamp_base64) { |
|
return format!("{}{}/{}", fixed_timestamp, device_id, mac_hash); |
|
} |
|
|
|
|
|
if let Some(timestamp) = extract_time_ks(timestamp_base64) { |
|
let current_timestamp = std::time::SystemTime::now() |
|
.duration_since(std::time::UNIX_EPOCH) |
|
.unwrap() |
|
.as_secs() |
|
/ 1_000; |
|
|
|
if timestamp <= current_timestamp { |
|
return format!( |
|
"{}{}/{}", |
|
generate_timestamp_header(), |
|
device_id, |
|
mac_hash |
|
); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
_ => {} |
|
} |
|
} |
|
|
|
|
|
generate_checksum_with_default() |
|
} |
|
|
|
pub fn extract_time_ks(timestamp_base64: &str) -> Option<u64> { |
|
let mut timestamp_bytes = BASE64.decode(timestamp_base64).ok()?; |
|
|
|
if timestamp_bytes.len() != 6 { |
|
return None; |
|
} |
|
|
|
deobfuscate_bytes(&mut timestamp_bytes); |
|
|
|
if timestamp_bytes[0] != timestamp_bytes[4] || timestamp_bytes[1] != timestamp_bytes[5] { |
|
return None; |
|
} |
|
|
|
|
|
Some( |
|
((timestamp_bytes[2] as u64) << 24) |
|
| ((timestamp_bytes[3] as u64) << 16) |
|
| ((timestamp_bytes[4] as u64) << 8) |
|
| (timestamp_bytes[5] as u64), |
|
) |
|
} |
|
|
|
pub fn validate_checksum(checksum: &str) -> bool { |
|
|
|
if checksum.is_empty() |
|
|| !checksum |
|
.chars() |
|
.all(|c| (c.is_ascii_alphanumeric() || c == '/' || c == '+' || c == '=')) |
|
{ |
|
return false; |
|
} |
|
|
|
let parts: Vec<&str> = checksum.split('/').collect(); |
|
|
|
match parts.len() { |
|
|
|
1 => { |
|
if checksum.len() < 72 { |
|
|
|
return false; |
|
} |
|
|
|
|
|
let timestamp_base64 = &checksum[..8]; |
|
let timestamp = match extract_time_ks(timestamp_base64) { |
|
Some(ts) => ts, |
|
None => return false, |
|
}; |
|
|
|
let current_timestamp = std::time::SystemTime::now() |
|
.duration_since(std::time::UNIX_EPOCH) |
|
.unwrap() |
|
.as_secs() |
|
/ 1_000; |
|
|
|
if current_timestamp < timestamp { |
|
return false; |
|
} |
|
|
|
|
|
is_valid_hash(&checksum[8..]) |
|
} |
|
|
|
2 => { |
|
let first_part = parts[0]; |
|
let mac_hash = parts[1]; |
|
|
|
|
|
if !is_valid_hash(mac_hash) { |
|
return false; |
|
} |
|
|
|
|
|
if first_part.len() != mac_hash.len() + 8 { |
|
return false; |
|
} |
|
|
|
|
|
validate_checksum(first_part) |
|
} |
|
_ => false, |
|
} |
|
} |
|
|
|
fn is_valid_hash(hash: &str) -> bool { |
|
if hash.len() < 64 { |
|
return false; |
|
} |
|
|
|
|
|
hash.chars().all(|c| c.is_ascii_hexdigit()) |
|
} |
|
|