use crate::metadata::tables::AssemblyHashAlgorithm;
#[cfg(feature = "legacy-crypto")]
use crate::utils::{compute_md5, compute_sha1};
use crate::utils::{compute_sha256, compute_sha384, compute_sha512, read_le};
use crate::Result;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Identity {
PubKey(Vec<u8>),
Token(u64),
EcmaKey(Vec<u8>),
}
impl Identity {
pub fn from(data: &[u8], is_pub: bool) -> Result<Self> {
Ok(if is_pub {
match data.len() {
16 => Identity::EcmaKey(data.to_vec()),
_ => Identity::PubKey(data.to_vec()),
}
} else {
Identity::Token(read_le::<u64>(data)?)
})
}
pub fn to_token(&self, algo: u32) -> Result<u64> {
match &self {
Identity::PubKey(data) | Identity::EcmaKey(data) => {
Self::compute_token_from_data(data, algo)
}
Identity::Token(token) => Ok(*token),
}
}
fn compute_token_from_data(data: &[u8], algo: u32) -> Result<u64> {
let hash = match algo {
#[cfg(feature = "legacy-crypto")]
AssemblyHashAlgorithm::MD5 => compute_md5(data),
#[cfg(feature = "legacy-crypto")]
AssemblyHashAlgorithm::SHA1 => compute_sha1(data),
#[cfg(not(feature = "legacy-crypto"))]
AssemblyHashAlgorithm::MD5 | AssemblyHashAlgorithm::SHA1 => {
return Err(malformed_error!(
"Hash algorithm 0x{:08X} requires the 'legacy-crypto' feature. \
Compile with `features = [\"legacy-crypto\"]` to enable MD5/SHA1 support.",
algo
));
}
AssemblyHashAlgorithm::SHA256 => compute_sha256(data),
AssemblyHashAlgorithm::SHA384 => compute_sha384(data),
AssemblyHashAlgorithm::SHA512 => compute_sha512(data),
_ => {
return Err(malformed_error!(
"Unsupported hash algorithm: 0x{:08X}",
algo
));
}
};
read_le::<u64>(&hash[hash.len() - 8..])
}
}
#[cfg(all(test, feature = "legacy-crypto"))]
mod tests {
use super::*;
use crate::metadata::tables::AssemblyHashAlgorithm;
#[test]
fn test_identity_from_pubkey() {
let data = vec![
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
];
let identity = Identity::from(&data, true).unwrap();
let Identity::PubKey(pubkey_data) = identity else {
panic!("Expected PubKey variant, got {:?}", identity);
};
assert_eq!(pubkey_data, data);
}
#[test]
fn test_identity_from_ecma_key() {
let data = vec![
0x06, 0x28, 0xAC, 0x03, 0x00, 0x06, 0x7A, 0x06, 0x6F, 0xAB, 0x02, 0x00, 0x0A, 0x0B,
0x17, 0x6A,
];
let identity = Identity::from(&data, true).unwrap();
let Identity::EcmaKey(ecma_data) = identity else {
panic!("Expected EcmaKey variant, got {:?}", identity);
};
assert_eq!(ecma_data, data);
assert_eq!(ecma_data.len(), 16);
}
#[test]
fn test_identity_from_token() {
let data = vec![0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0];
let identity = Identity::from(&data, false).unwrap();
let Identity::Token(token) = identity else {
panic!("Expected Token variant, got {:?}", identity);
};
assert_eq!(token, 0xF0DEBC9A78563412);
}
#[test]
fn test_identity_from_empty_pubkey() {
let data = vec![];
let identity = Identity::from(&data, true).unwrap();
let Identity::PubKey(pubkey_data) = identity else {
panic!("Expected PubKey variant, got {:?}", identity);
};
assert!(pubkey_data.is_empty());
}
#[test]
fn test_identity_from_token_insufficient_data() {
let data = vec![1, 2, 3]; let result = Identity::from(&data, false);
assert!(result.is_err());
}
#[test]
fn test_to_token_from_pubkey_md5() {
let pubkey_data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
let identity = Identity::PubKey(pubkey_data.clone());
let token = identity.to_token(AssemblyHashAlgorithm::MD5).unwrap();
let result = compute_md5(&pubkey_data);
let last_8_bytes = &result[result.len() - 8..];
let expected_token = read_le::<u64>(last_8_bytes).unwrap();
assert_eq!(token, expected_token);
}
#[test]
fn test_to_token_from_pubkey_sha1() {
let pubkey_data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
let identity = Identity::PubKey(pubkey_data.clone());
let token = identity.to_token(AssemblyHashAlgorithm::SHA1).unwrap();
let result = compute_sha1(&pubkey_data);
let last_8_bytes = &result[result.len() - 8..];
let expected_token = read_le::<u64>(last_8_bytes).unwrap();
assert_eq!(token, expected_token);
}
#[test]
fn test_to_token_from_pubkey_sha256() {
let pubkey_data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
let identity = Identity::PubKey(pubkey_data.clone());
let token = identity.to_token(AssemblyHashAlgorithm::SHA256).unwrap();
let result = compute_sha256(&pubkey_data);
let last_8_bytes = &result[result.len() - 8..];
let expected_token = read_le::<u64>(last_8_bytes).unwrap();
assert_eq!(token, expected_token);
}
#[test]
fn test_to_token_from_pubkey_sha384() {
let pubkey_data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
let identity = Identity::PubKey(pubkey_data.clone());
let token = identity.to_token(AssemblyHashAlgorithm::SHA384).unwrap();
let result = compute_sha384(&pubkey_data);
let last_8_bytes = &result[result.len() - 8..];
let expected_token = read_le::<u64>(last_8_bytes).unwrap();
assert_eq!(token, expected_token);
}
#[test]
fn test_to_token_from_pubkey_sha512() {
let pubkey_data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
let identity = Identity::PubKey(pubkey_data.clone());
let token = identity.to_token(AssemblyHashAlgorithm::SHA512).unwrap();
let result = compute_sha512(&pubkey_data);
let last_8_bytes = &result[result.len() - 8..];
let expected_token = read_le::<u64>(last_8_bytes).unwrap();
assert_eq!(token, expected_token);
}
#[test]
fn test_to_token_from_token_identity() {
let original_token = 0x123456789ABCDEF0;
let identity = Identity::Token(original_token);
let result_md5 = identity.to_token(AssemblyHashAlgorithm::MD5).unwrap();
let result_sha1 = identity.to_token(AssemblyHashAlgorithm::SHA1).unwrap();
let result_none = identity.to_token(AssemblyHashAlgorithm::NONE).unwrap();
assert_eq!(result_md5, original_token);
assert_eq!(result_sha1, original_token);
assert_eq!(result_none, original_token);
}
#[test]
fn test_to_token_unsupported_algorithm() {
let pubkey_data = vec![1, 2, 3, 4, 5, 6, 7, 8];
let identity = Identity::PubKey(pubkey_data);
let result = identity.to_token(0x9999);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(
err_msg.contains("Unsupported hash algorithm"),
"Error message should mention unsupported algorithm: {}",
err_msg
);
}
#[test]
fn test_to_token_unsupported_algorithm_ecma_key() {
let ecma_data = vec![
0x06, 0x28, 0xAC, 0x03, 0x00, 0x06, 0x7A, 0x06, 0x6F, 0xAB, 0x02, 0x00, 0x0A, 0x0B,
0x17, 0x6A,
];
let identity = Identity::EcmaKey(ecma_data);
let result = identity.to_token(0x9999);
assert!(result.is_err());
}
#[test]
fn test_to_token_empty_pubkey_md5() {
let identity = Identity::PubKey(vec![]);
let token = identity.to_token(AssemblyHashAlgorithm::MD5).unwrap();
let result = compute_md5(&[]);
let last_8_bytes = &result[result.len() - 8..];
let expected_token = read_le::<u64>(last_8_bytes).unwrap();
assert_eq!(token, expected_token);
}
#[test]
fn test_to_token_empty_pubkey_sha1() {
let identity = Identity::PubKey(vec![]);
let token = identity.to_token(AssemblyHashAlgorithm::SHA1).unwrap();
let result = compute_sha1(&[]);
let last_8_bytes = &result[result.len() - 8..];
let expected_token = read_le::<u64>(last_8_bytes).unwrap();
assert_eq!(token, expected_token);
}
#[test]
fn test_large_pubkey_data() {
let large_pubkey: Vec<u8> = (0..256).map(|i| (i % 256) as u8).collect();
let identity = Identity::PubKey(large_pubkey.clone());
let token_md5 = identity.to_token(AssemblyHashAlgorithm::MD5).unwrap();
let token_sha1 = identity.to_token(AssemblyHashAlgorithm::SHA1).unwrap();
assert_ne!(token_md5, token_sha1);
assert_ne!(token_md5, 0);
assert_ne!(token_sha1, 0);
}
#[test]
fn test_hash_algorithm_consistency() {
let pubkey_data = vec![42, 123, 255, 0, 17, 88, 99, 200];
let identity = Identity::PubKey(pubkey_data);
let token1 = identity.to_token(AssemblyHashAlgorithm::MD5).unwrap();
let token2 = identity.to_token(AssemblyHashAlgorithm::MD5).unwrap();
assert_eq!(token1, token2);
}
#[test]
fn test_from_exact_8_bytes() {
let data = vec![0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88];
let identity = Identity::from(&data, false).unwrap();
match identity {
Identity::Token(token) => {
assert_eq!(token, 0x8899AABBCCDDEEFF);
}
Identity::PubKey(_) => panic!("Expected Token variant"),
Identity::EcmaKey(_) => panic!("Expected Token variant"),
}
}
#[test]
fn test_from_more_than_8_bytes_token() {
let data = vec![0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA];
let identity = Identity::from(&data, false).unwrap();
match identity {
Identity::Token(token) => {
assert_eq!(token, 0x8877665544332211);
}
Identity::PubKey(_) => panic!("Expected Token variant"),
Identity::EcmaKey(_) => panic!("Expected Token variant"),
}
}
#[test]
fn test_identity_variants_different_behavior() {
let pubkey_data = vec![1, 2, 3, 4, 5, 6, 7, 8];
let pubkey_identity = Identity::from(&pubkey_data, true).unwrap();
let token_identity = Identity::from(&pubkey_data, false).unwrap();
let pubkey_token = pubkey_identity
.to_token(AssemblyHashAlgorithm::MD5)
.unwrap();
let direct_token = token_identity.to_token(AssemblyHashAlgorithm::MD5).unwrap();
assert_ne!(pubkey_token, direct_token);
}
}