use std::collections::HashMap;
use std::fs;
use std::path::Path;
use crate::error::UnicornError;
fn known_hashes() -> HashMap<&'static str, &'static str> {
let mut m = HashMap::new();
m.insert("Unicorn.dll", "59d997aa6595a4d35f81730365bdd16eacea14fc86d1f2b33fdd64e535a1b30f");
m.insert("libunicorn.so", "75df760af1a58c76098cf3d12a9f5184869e705d296a9687ed7bce6124cd1a3c");
m.insert("libgtecble.dylib", "0e0265d33e598622fe282ffd8eaf36d4bb668e80a1b5644cfec30b7710941a4f");
m.insert("libgtecble.dll", "dff2a56949200fb225644905fcf65b2a6ddbf5bfd5c8bf90130f824b06cfdd3e");
m
}
fn sha256(data: &[u8]) -> [u8; 32] {
const K: [u32; 64] = [
0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2,
];
let bit_len = (data.len() as u64) * 8;
let mut msg = data.to_vec();
msg.push(0x80);
while (msg.len() % 64) != 56 { msg.push(0); }
msg.extend_from_slice(&bit_len.to_be_bytes());
let mut h: [u32; 8] = [0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19];
for chunk in msg.chunks(64) {
let mut w = [0u32; 64];
for i in 0..16 { w[i] = u32::from_be_bytes([chunk[4*i],chunk[4*i+1],chunk[4*i+2],chunk[4*i+3]]); }
for i in 16..64 {
let s0 = w[i-15].rotate_right(7) ^ w[i-15].rotate_right(18) ^ (w[i-15] >> 3);
let s1 = w[i-2].rotate_right(17) ^ w[i-2].rotate_right(19) ^ (w[i-2] >> 10);
w[i] = w[i-16].wrapping_add(s0).wrapping_add(w[i-7]).wrapping_add(s1);
}
let (mut a,mut b,mut c,mut d,mut e,mut f,mut g,mut hh) = (h[0],h[1],h[2],h[3],h[4],h[5],h[6],h[7]);
for i in 0..64 {
let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25);
let ch = (e & f) ^ ((!e) & g);
let t1 = hh.wrapping_add(s1).wrapping_add(ch).wrapping_add(K[i]).wrapping_add(w[i]);
let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22);
let maj = (a & b) ^ (a & c) ^ (b & c);
let t2 = s0.wrapping_add(maj);
hh=g; g=f; f=e; e=d.wrapping_add(t1); d=c; c=b; b=a; a=t1.wrapping_add(t2);
}
for (i,v) in [a,b,c,d,e,f,g,hh].iter().enumerate() { h[i] = h[i].wrapping_add(*v); }
}
let mut out = [0u8; 32];
for (i, &v) in h.iter().enumerate() { out[4*i..4*i+4].copy_from_slice(&v.to_be_bytes()); }
out
}
fn bytes_to_hex(bytes: &[u8]) -> String {
bytes.iter().map(|b| format!("{:02x}", b)).collect()
}
pub fn verify_library<P: AsRef<Path>>(path: P) -> Result<(), UnicornError> {
let path = path.as_ref();
let file_name = path.file_name().and_then(|n| n.to_str()).unwrap_or("unknown");
let known = known_hashes();
let expected = known.get(file_name).ok_or_else(|| UnicornError::LibraryNotAvailable {
reason: format!("No known checksum for '{}'", file_name),
})?;
let data = fs::read(path).map_err(|e| UnicornError::LibraryNotAvailable {
reason: format!("Cannot read '{}': {}", path.display(), e),
})?;
let actual = bytes_to_hex(&sha256(&data));
if actual != *expected {
return Err(UnicornError::LibraryNotAvailable {
reason: format!("INTEGRITY CHECK FAILED for '{}'!\nExpected: {}\nActual: {}", file_name, expected, actual),
});
}
log::info!("Library verified: {} (SHA-256: {})", file_name, &actual[..16]);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sha256_abc() {
assert_eq!(bytes_to_hex(&sha256(b"abc")), "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
}
#[test]
fn test_sha256_empty() {
assert_eq!(bytes_to_hex(&sha256(b"")), "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
}
}