use base64::Engine;
use sha2::{Digest, Sha256};
pub fn sha256_base64(data: &[u8]) -> String {
let mut hasher = Sha256::new();
hasher.update(data);
let hash = hasher.finalize();
base64::engine::general_purpose::STANDARD.encode(hash)
}
pub fn sha256(data: &[u8]) -> [u8; 32] {
let mut hasher = Sha256::new();
hasher.update(data);
let hash = hasher.finalize();
let mut result = [0u8; 32];
result.copy_from_slice(&hash);
result
}
pub fn sha256_hex(data: &[u8]) -> String {
let mut hasher = Sha256::new();
hasher.update(data);
let hash = hasher.finalize();
hex::encode(hash)
}
pub fn hmac_sha256_base64(key: &[u8], data: &[u8]) -> String {
use hmac::{Hmac, KeyInit, Mac};
type HmacSha256 = Hmac<Sha256>;
let mut mac = HmacSha256::new_from_slice(key).expect("HMAC 可以接受任意长度的密钥");
mac.update(data);
let result = mac.finalize();
base64::engine::general_purpose::STANDARD.encode(result.into_bytes())
}
pub fn hmac_sha256(key: &[u8], data: &[u8]) -> [u8; 32] {
use hmac::{Hmac, KeyInit, Mac};
type HmacSha256 = Hmac<Sha256>;
let mut mac = HmacSha256::new_from_slice(key).expect("HMAC 可以接受任意长度的密钥");
mac.update(data);
let result = mac.finalize();
let mut output = [0u8; 32];
output.copy_from_slice(&result.into_bytes());
output
}
pub fn build_sign_message(
method: &str,
url: &str,
timestamp: i64,
nonce: &str,
body: &str,
) -> String {
use std::fmt::Write;
let mut s = String::with_capacity(
method.len() + url.len() + nonce.len() + body.len() + 20 + 5,
);
let _ = write!(
s,
"{}\n{}\n{}\n{}\n{}\n",
method, url, timestamp, nonce, body
);
s
}
pub fn build_verify_message(timestamp: i64, nonce: &str, body: &str) -> String {
use std::fmt::Write;
let mut s = String::with_capacity(nonce.len() + body.len() + 20 + 3);
let _ = write!(s, "{}\n{}\n{}\n", timestamp, nonce, body);
s
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sha256_base64() {
let hash = sha256_base64(b"Hello, World!");
assert!(!hash.is_empty());
let hash2 = sha256_base64(b"Hello, World!");
assert_eq!(hash, hash2);
let hash3 = sha256_base64(b"Different input");
assert_ne!(hash, hash3);
}
#[test]
fn test_sha256() {
let hash = sha256(b"Hello, World!");
assert_eq!(hash.len(), 32);
}
#[test]
fn test_sha256_hex() {
let hash = sha256_hex(b"Hello, World!");
assert_eq!(hash.len(), 64);
assert!(hash.chars().all(|c| c.is_ascii_hexdigit()));
}
#[test]
fn test_hmac_sha256_base64() {
let mac = hmac_sha256_base64(b"secret_key", b"Hello, World!");
assert!(!mac.is_empty());
let mac2 = hmac_sha256_base64(b"secret_key", b"Hello, World!");
assert_eq!(mac, mac2);
let mac3 = hmac_sha256_base64(b"different_key", b"Hello, World!");
assert_ne!(mac, mac3);
}
#[test]
fn test_hmac_sha256() {
let mac = hmac_sha256(b"secret_key", b"Hello, World!");
assert_eq!(mac.len(), 32);
}
#[test]
fn test_build_sign_message() {
let message = build_sign_message(
"POST",
"/v3/pay/transactions/jsapi",
1609459200,
"test_nonce",
r#"{"app_id":"wx88888888"}"#,
);
assert!(message.starts_with("POST\n"));
assert!(message.contains("/v3/pay/transactions/jsapi"));
assert!(message.contains("1609459200"));
assert!(message.contains("test_nonce"));
assert!(message.ends_with("\n"));
}
#[test]
fn test_build_verify_message() {
let message = build_verify_message(1609459200, "test_nonce", r#"{"code":"SUCCESS"}"#);
assert!(message.starts_with("1609459200\n"));
assert!(message.contains("test_nonce"));
assert!(message.ends_with("\n"));
}
}