Menu
Public repository / master
AkurAI-Framework / crates/crypto/tests/helpers.rs
Latest change 3aae9f4e85241bcb775c31f5c03094becd3da56a - Pure-Rust UI layer: akurai-ui, akurai-crypto, Tailwind-grade akurai-css by Ólafur Búi Ólafsson
//! Hex, constant-time compare, randomness and password hashing.
use akurai_crypto::{ct_eq, from_hex, hex, verify_password};
#[test]
fn hex_is_lowercase_and_round_trips() {
let bytes = [0x00, 0x01, 0xab, 0xff, 0x7f];
assert_eq!(hex(&bytes), "0001abff7f");
assert_eq!(from_hex("0001abff7f"), Some(bytes.to_vec()));
assert_eq!(from_hex("0001ABFF7F"), Some(bytes.to_vec()));
assert_eq!(hex(&[]), "");
assert_eq!(from_hex(""), Some(vec![]));
}
#[test]
fn from_hex_rejects_bad_input() {
assert_eq!(from_hex("abc"), None, "odd length");
assert_eq!(from_hex("zz"), None, "non-hex digit");
assert_eq!(from_hex("0g"), None, "non-hex digit");
assert_eq!(from_hex("é1"), None, "non-ascii");
assert_eq!(from_hex("+1"), None, "sign is not a digit");
}
#[test]
fn ct_eq_compares_content_and_length() {
assert!(ct_eq(b"", b""));
assert!(ct_eq(b"secret", b"secret"));
assert!(!ct_eq(b"secret", b"secreT"));
assert!(!ct_eq(b"secret", b"secret!"));
assert!(!ct_eq(b"a", b""));
}
#[test]
fn verify_password_rejects_malformed_strings() {
let good_salt = "00112233445566778899aabbccddeeff";
let good_hash = "00".repeat(32);
for bad in [
String::new(),
"garbage".to_string(),
format!("pbkdf2-sha256$1${good_salt}"),
format!("pbkdf2-sha256$1${good_salt}${good_hash}$extra"),
format!("bcrypt$1${good_salt}${good_hash}"),
format!("pbkdf2-sha256$0${good_salt}${good_hash}"),
format!("pbkdf2-sha256$-1${good_salt}${good_hash}"),
format!("pbkdf2-sha256$abc${good_salt}${good_hash}"),
format!("pbkdf2-sha256$1$zz${good_hash}"),
format!("pbkdf2-sha256$1${good_salt}$"),
format!("pbkdf2-sha256$1${good_salt}$abc"),
] {
assert!(!verify_password("pw", &bad), "accepted {bad:?}");
}
}
#[test]
fn verify_password_accepts_hand_built_low_iteration_hash() {
let salt = b"0123456789abcdef";
let mut out = [0u8; 32];
akurai_crypto::pbkdf2_sha256(b"hunter2", salt, 2, &mut out);
let encoded = format!("pbkdf2-sha256$2${}${}", hex(salt), hex(&out));
assert!(verify_password("hunter2", &encoded));
assert!(!verify_password("hunter3", &encoded));
}
#[cfg(not(target_arch = "wasm32"))]
mod os {
use akurai_crypto::{
from_hex, hash_password, random_bytes, random_token, verify_password, PASSWORD_ITERATIONS,
};
#[test]
fn random_bytes_fills_buffer() {
let mut a = [0u8; 32];
let mut b = [0u8; 32];
random_bytes(&mut a).unwrap();
random_bytes(&mut b).unwrap();
assert_ne!(a, b);
assert_ne!(a, [0u8; 32]);
}
#[test]
fn random_token_is_hex_of_requested_length() {
let t = random_token(24).unwrap();
assert_eq!(t.len(), 48);
assert_eq!(from_hex(&t).map(|v| v.len()), Some(24));
assert_ne!(t, random_token(24).unwrap());
}
#[test]
fn hash_password_format_and_round_trip() {
let encoded = hash_password("correct horse").unwrap();
let parts: Vec<&str> = encoded.split('$').collect();
assert_eq!(parts.len(), 4);
assert_eq!(parts[0], "pbkdf2-sha256");
assert_eq!(parts[1], PASSWORD_ITERATIONS.to_string());
assert_eq!(from_hex(parts[2]).unwrap().len(), 16);
assert_eq!(from_hex(parts[3]).unwrap().len(), 32);
assert!(verify_password("correct horse", &encoded));
assert!(!verify_password("correct horsf", &encoded));
assert!(!verify_password("", &encoded));
}
#[test]
fn hashing_same_password_twice_uses_fresh_salt() {
let a = hash_password("same").unwrap();
let b = hash_password("same").unwrap();
assert_ne!(a, b);
assert!(verify_password("same", &a));
assert!(verify_password("same", &b));
}
}