use crate::GatewardenError;
use base64::{engine::general_purpose::STANDARD, Engine};
use sha2::{Digest, Sha256};
pub fn sha256_b64(body: &[u8]) -> String {
let hash = Sha256::digest(body);
STANDARD.encode(hash)
}
pub fn format_digest_header(body: &[u8]) -> String {
format!("sha-256={}", sha256_b64(body))
}
pub fn parse_digest_header(header: &str) -> Option<String> {
let header = header.trim();
header
.strip_prefix("sha-256=")
.or_else(|| header.strip_prefix("SHA-256="))
.map(|s| s.to_string())
}
pub fn verify_digest(body: &[u8], digest_header: Option<&str>) -> Result<(), GatewardenError> {
let Some(header) = digest_header else {
return Ok(());
};
let Some(expected_b64) = parse_digest_header(header) else {
return Err(GatewardenError::DigestMismatch);
};
let computed_b64 = sha256_b64(body);
if computed_b64 != expected_b64 {
return Err(GatewardenError::DigestMismatch);
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sha256_empty_body() {
let digest = sha256_b64(b"");
assert_eq!(digest, "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=");
}
#[test]
fn test_sha256_hello_world() {
let digest = sha256_b64(b"Hello, World!");
assert_eq!(digest, "3/1gIbsr1bCvZ2KQgJ7DpTGR3YHH9wpLKGiKNiGCmG8=");
}
#[test]
fn test_format_digest_header() {
let header = format_digest_header(b"test body");
assert!(header.starts_with("sha-256="));
}
#[test]
fn test_parse_digest_header_valid() {
let result = parse_digest_header("sha-256=abc123=");
assert_eq!(result, Some("abc123=".to_string()));
}
#[test]
fn test_parse_digest_header_uppercase() {
let result = parse_digest_header("SHA-256=abc123=");
assert_eq!(result, Some("abc123=".to_string()));
}
#[test]
fn test_parse_digest_header_invalid() {
let result = parse_digest_header("md5=abc");
assert_eq!(result, None);
}
#[test]
fn test_verify_digest_match() {
let body = b"test body";
let header = format_digest_header(body);
let result = verify_digest(body, Some(&header));
assert!(result.is_ok());
}
#[test]
fn test_verify_digest_mismatch() {
let body = b"test body";
let wrong_header = "sha-256=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
let result = verify_digest(body, Some(wrong_header));
assert!(matches!(result, Err(GatewardenError::DigestMismatch)));
}
#[test]
fn test_verify_digest_absent() {
let body = b"test body";
let result = verify_digest(body, None);
assert!(result.is_ok());
}
#[test]
fn test_verify_digest_malformed_header() {
let body = b"test body";
let result = verify_digest(body, Some("not-a-valid-digest"));
assert!(matches!(result, Err(GatewardenError::DigestMismatch)));
}
}