use base64::{engine::general_purpose::STANDARD, Engine as _};
use regex::Regex;
use sha2::{Digest, Sha256};
use super::errors::{ErrorType, SimpleError};
pub enum UnitSizeType {
Bytes,
KB,
MB,
GB,
}
impl UnitSizeType {
pub const fn as_str(&self) -> &'static str {
match self {
Self::Bytes => "Bytes",
Self::KB => "Kb",
Self::MB => "Mb",
Self::GB => "Gb",
}
}
}
pub fn validate_base64_checksum(
base64_string: &str,
expected_checksum: &str,
pre_decoded_data: Option<Vec<u8>>,
) -> Result<bool, SimpleError> {
let data = match pre_decoded_data {
Some(dec) => dec,
None => {
let re = Regex::new(r"^data:[^;]+;base64,").map_err(|err| SimpleError {
err: Box::new(err),
message: "failed to create a new regex".to_string(),
_type: ErrorType::RegexInvalid,
})?;
let base64_data = re.replace(base64_string, "");
STANDARD.decode(base64_data.as_bytes()).map_err(|err| SimpleError {
err: Box::new(err),
message: "invalid base64 data string".to_string(),
_type: ErrorType::Base64Invalid,
})?
}
};
let mut hasher = Sha256::new();
hasher.update(&data);
let hash_result = hasher.finalize();
let computed_checksum = hash_result.iter().map(|b| format!("{:02x}", b)).collect::<String>();
Ok(computed_checksum == expected_checksum)
}
#[cfg(test)]
mod tests {
use super::*;
use base64::{engine::general_purpose::STANDARD, Engine};
fn get_test_data() -> (String, String, Vec<u8>) {
let base64_data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==";
let decoded_data = STANDARD.decode(base64_data).unwrap();
let mut hasher = Sha256::new();
hasher.update(&decoded_data);
let hash_result = hasher.finalize();
let actual_hash = hash_result.iter().map(|b| format!("{:02x}", b)).collect::<String>();
(base64_data.to_string(), actual_hash, decoded_data)
}
#[test]
fn test_valid_checksum_without_pre_decoded() {
let (base64, expected_hash, _) = get_test_data();
let result = validate_base64_checksum(&base64, &expected_hash, None).unwrap();
assert!(result);
}
#[test]
fn test_valid_checksum_with_pre_decoded() {
let (base64, expected_hash, decoded_data) = get_test_data();
let result = validate_base64_checksum(&base64, &expected_hash, Some(decoded_data)).unwrap();
assert!(result);
}
#[test]
fn test_invalid_checksum() {
let (base64, _, _) = get_test_data();
let wrong_hash = "0000000000000000000000000000000000000000000000000000000000000000";
let result = validate_base64_checksum(&base64, wrong_hash, None).unwrap();
assert!(!result);
}
#[test]
fn test_data_url_without_pre_decoded() {
let (base64, expected_hash, _) = get_test_data();
let data_url = format!("data:image/png;base64,{}", base64);
let result = validate_base64_checksum(&data_url, &expected_hash, None).unwrap();
assert!(result);
}
#[test]
fn test_data_url_with_pre_decoded() {
let (base64, expected_hash, decoded_data) = get_test_data();
let data_url = format!("data:image/png;base64,{}", base64);
let result = validate_base64_checksum(&data_url, &expected_hash, Some(decoded_data)).unwrap();
assert!(result);
}
#[test]
fn test_different_data_url_mime_types() {
let (base64, expected_hash, _) = get_test_data();
let mime_types = vec![
"data:image/jpeg;base64,",
"data:application/octet-stream;base64,",
"data:text/plain;base64,",
];
for mime in mime_types {
let data_url = format!("{}{}", mime, base64);
let result = validate_base64_checksum(&data_url, &expected_hash, None).unwrap();
assert!(result, "Failed with MIME type: {}", mime);
}
}
#[test]
fn test_invalid_base64() {
let invalid_base64 = "invalid!!!base64@@@data";
let result = validate_base64_checksum(invalid_base64, "some_hash", None);
assert!(result.is_err());
if let Err(err) = result {
assert_eq!(err._type, ErrorType::Base64Invalid);
}
}
#[test]
fn test_empty_base64_string() {
let empty_hash = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
let result = validate_base64_checksum("", empty_hash, None).unwrap();
assert!(result);
}
#[test]
fn test_pre_decoded_takes_precedence() {
let (base64, expected_hash, _) = get_test_data();
let different_data = b"completely different data".to_vec();
let result = validate_base64_checksum(&base64, &expected_hash, Some(different_data)).unwrap();
assert!(!result); }
#[test]
fn test_consistency_between_with_and_without_pre_decoded() {
let (base64, expected_hash, decoded_data) = get_test_data();
let result1 = validate_base64_checksum(&base64, &expected_hash, None).unwrap();
let result2 = validate_base64_checksum(&base64, &expected_hash, Some(decoded_data)).unwrap();
assert_eq!(result1, result2);
assert!(result1);
assert!(result2);
}
#[test]
fn test_known_hash_value() {
let base64_data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==";
let _ = "1c30f1a2686fbf86c76c9115b471df8c0b6b9c5b6c5e2c7b8c5c6c5c6c5c6c5c6c5";
let decoded = STANDARD.decode(base64_data).unwrap();
let mut hasher = Sha256::new();
hasher.update(&decoded);
let actual_hash = hasher.finalize();
let actual_hash_hex = actual_hash.iter().map(|b| format!("{:02x}", b)).collect::<String>();
println!("Actual hash for test data: {}", actual_hash_hex);
let result = validate_base64_checksum(base64_data, &actual_hash_hex, None).unwrap();
assert!(result);
}
}