rust-auth-utils 1.0.0

A rust port of @better-auth/utils.
Documentation
use crate::hmac::HmacBuilder;
use crate::types::{EncodingFormat, SHAFamily};

const TEST_KEY: &[u8] = b"test-key-for-hmac-verification";
const TEST_DATA: &[u8] = b"test-data-to-be-signed-with-hmac";

#[test]
fn test_hmac_sign_verify() {
    let hmac = HmacBuilder::default();
    let signature = hmac.sign(TEST_KEY, TEST_DATA).unwrap();
    assert!(hmac.verify(TEST_KEY, TEST_DATA, &signature).unwrap());
}

#[test]
fn test_hmac_with_hex_encoding() {
    let hmac = HmacBuilder::new(None, Some(EncodingFormat::Hex));
    let signature = hmac.sign(TEST_KEY, TEST_DATA).unwrap();
    assert!(hmac.verify(TEST_KEY, TEST_DATA, &signature).unwrap());
}

#[test]
fn test_hmac_with_base64_encoding() {
    let hmac = HmacBuilder::new(None, Some(EncodingFormat::Base64));
    let signature = hmac.sign(TEST_KEY, TEST_DATA).unwrap();
    assert!(hmac.verify(TEST_KEY, TEST_DATA, &signature).unwrap());
}

#[test]
fn test_hmac_with_base64url_encoding() {
    let hmac = HmacBuilder::new(None, Some(EncodingFormat::Base64Url));
    let signature = hmac.sign(TEST_KEY, TEST_DATA).unwrap();
    assert!(hmac.verify(TEST_KEY, TEST_DATA, &signature).unwrap());
}

#[test]
fn test_hmac_with_base64url_nopad_encoding() {
    let hmac = HmacBuilder::new(None, Some(EncodingFormat::Base64UrlNoPad));
    let signature = hmac.sign(TEST_KEY, TEST_DATA).unwrap();
    assert!(hmac.verify(TEST_KEY, TEST_DATA, &signature).unwrap());
}

#[test]
fn test_all_hash_algorithms() {
    let algorithms = [
        SHAFamily::SHA1,
        SHAFamily::SHA256,
        SHAFamily::SHA384,
        SHAFamily::SHA512,
    ];

    for alg in algorithms {
        let hmac = HmacBuilder::new(Some(alg), None);
        let signature = hmac.sign(TEST_KEY, TEST_DATA).unwrap();
        assert!(hmac.verify(TEST_KEY, TEST_DATA, &signature).unwrap());
    }
}

#[test]
fn test_verify_fails_with_wrong_key() {
    let hmac = HmacBuilder::default();
    let signature = hmac.sign(TEST_KEY, TEST_DATA).unwrap();
    let wrong_key = b"wrong-key";
    assert!(!hmac.verify(wrong_key, TEST_DATA, &signature).unwrap());
}

#[test]
fn test_verify_fails_with_wrong_data() {
    let hmac = HmacBuilder::default();
    let signature = hmac.sign(TEST_KEY, TEST_DATA).unwrap();
    let wrong_data = b"wrong-data";
    assert!(!hmac.verify(TEST_KEY, wrong_data, &signature).unwrap());
}

#[test]
fn test_verify_fails_with_tampered_signature() {
    let hmac = HmacBuilder::default();
    let mut signature = hmac.sign(TEST_KEY, TEST_DATA).unwrap();
    // Tamper with the last byte
    if let Some(last) = signature.last_mut() {
        *last ^= 0xFF;
    }
    assert!(!hmac.verify(TEST_KEY, TEST_DATA, &signature).unwrap());
}

#[test]
fn test_cross_algorithm_verification_fails() {
    let hmac_sha1 = HmacBuilder::new(Some(SHAFamily::SHA1), None);
    let hmac_sha256 = HmacBuilder::new(Some(SHAFamily::SHA256), None);

    let signature_sha1 = hmac_sha1.sign(TEST_KEY, TEST_DATA).unwrap();
    let signature_sha256 = hmac_sha256.sign(TEST_KEY, TEST_DATA).unwrap();

    // Verify that signatures from different algorithms don't verify each other
    assert!(!hmac_sha1
        .verify(TEST_KEY, TEST_DATA, &signature_sha256)
        .unwrap());
    assert!(!hmac_sha256
        .verify(TEST_KEY, TEST_DATA, &signature_sha1)
        .unwrap());
}

#[test]
fn test_invalid_hex_signature() {
    let hmac = HmacBuilder::new(None, Some(EncodingFormat::Hex));
    let invalid_hex = "not-a-hex-string".as_bytes();
    assert!(hmac.verify(TEST_KEY, TEST_DATA, invalid_hex).is_err());
}

#[test]
fn test_invalid_base64_signature() {
    let hmac = HmacBuilder::new(None, Some(EncodingFormat::Base64));
    let invalid_base64 = "not-a-base64-string".as_bytes();
    assert!(hmac.verify(TEST_KEY, TEST_DATA, invalid_base64).is_err());
}

#[test]
fn test_empty_key() {
    let hmac = HmacBuilder::default();
    let empty_key = b"";
    assert!(hmac.sign(empty_key, TEST_DATA).is_ok());
}

#[test]
fn test_empty_data() {
    let hmac = HmacBuilder::default();
    let empty_data = b"";
    let signature = hmac.sign(TEST_KEY, empty_data).unwrap();
    assert!(hmac.verify(TEST_KEY, empty_data, &signature).unwrap());
}

#[test]
fn test_different_encodings_not_compatible() {
    let hmac_hex = HmacBuilder::new(None, Some(EncodingFormat::Hex));
    let hmac_base64 = HmacBuilder::new(None, Some(EncodingFormat::Base64));

    let signature_hex = hmac_hex.sign(TEST_KEY, TEST_DATA).unwrap();
    let signature_base64 = hmac_base64.sign(TEST_KEY, TEST_DATA).unwrap();

    println!("Hex signature: {}", String::from_utf8_lossy(&signature_hex));
    println!(
        "Base64 signature: {}",
        String::from_utf8_lossy(&signature_base64)
    );

    // Verify that signatures with different encodings don't verify each other
    assert!(hmac_hex
        .verify(TEST_KEY, TEST_DATA, &signature_base64)
        .is_err());
    assert!(hmac_base64
        .verify(TEST_KEY, TEST_DATA, &signature_hex)
        .is_err());
}