kiwavi 0.1.1

A secure TOTP-based key derivation system using user salts
Documentation
use kiwavi::{AppConfig, Kiwavi};

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_different_salt_types() {
        let app_config = AppConfig::new("KIWAVI", "bunny@theaibunny.com");

        // Test with different input types
        let prf_salt = [0u8; 32]; // Like crypto.getRandomValues(new Uint8Array(32))
        let kiwavi1 = Kiwavi::from_prf_salt(prf_salt, app_config.clone()).unwrap();

        let hex_salt = "0000000000000000000000000000000000000000000000000000000000000000";
        let kiwavi2 = Kiwavi::from_hex(hex_salt, app_config.clone()).unwrap();

        let bytes_salt: &[u8] = &[0u8; 32];
        let kiwavi3 = Kiwavi::new(bytes_salt, app_config.clone()).unwrap();

        let string_salt = "test_string_salt";
        let kiwavi4 = Kiwavi::new(string_salt, app_config).unwrap();

        // First three should produce same values (all zeros)
        assert_eq!(
            kiwavi1.preview_derived_value(),
            kiwavi2.preview_derived_value()
        );
        assert_eq!(
            kiwavi2.preview_derived_value(),
            kiwavi3.preview_derived_value()
        );

        // String salt should be different
        assert_ne!(
            kiwavi1.preview_derived_value(),
            kiwavi4.preview_derived_value()
        );
    }

    #[test]
    fn test_performance_with_binary_salts() {
        let app_config = AppConfig::new("KIWAVI", "bunny@theaibunny.com");

        // Simulate typical WebAuthn PRF salt
        let prf_salt = [
            0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
            0x77, 0x88, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
            0x66, 0x77, 0x88, 0x99,
        ];

        let kiwavi = Kiwavi::from_prf_salt(prf_salt, app_config).unwrap();
        let derived = kiwavi.preview_derived_value();

        // Should be deterministic
        assert_eq!(derived.len(), 64); // 32 bytes = 64 hex chars
        assert!(!derived.is_empty());
    }

    #[test]
    fn test_correct_totp_validation() {
        let app_config = AppConfig::new("KIWAVI", "bunny@theaibunny.com");
        let kiwavi = Kiwavi::new("test_salt", app_config).unwrap();

        let correct_code = kiwavi.get_current_code();
        let result = kiwavi.validate_and_derive(&correct_code);

        assert!(result.is_valid());
        assert_eq!(result.hex(), kiwavi.preview_derived_value());
    }

    #[test]
    fn test_wrong_totp_validation() {
        let app_config = AppConfig::new("KIWAVI", "bunny@theaibunny.com");
        let kiwavi = Kiwavi::new("test_salt", app_config).unwrap();

        let result = kiwavi.validate_and_derive("000000");

        assert!(!result.is_valid());
        assert_ne!(result.hex(), kiwavi.preview_derived_value());
    }

    #[test]
    fn test_qr_code_generation() {
        let app_config = AppConfig::new("KIWAVI", "bunny@theaibunny.com");
        let kiwavi = Kiwavi::new("test_salt", app_config).unwrap();

        let qr = kiwavi.get_setup_qr();
        println!("This is: {}", qr);

        assert!(qr.starts_with("otpauth://totp/"));
        assert!(qr.contains("KIWAVI"));
        assert!(qr.contains("bunny%40theaibunny.com"));
    }

    #[test]
    fn test_totp_secret() {
        let app_config = AppConfig::new("KIWAVI", "bunny@theaibunny.com");
        let kiwavi = Kiwavi::new("test_salt", app_config).unwrap();

        let totp = kiwavi.get_totp_secret();

        assert!(!(totp.is_empty()));
    }

    #[test]
    fn test_validate_totp_code_static_method() {
        let app_config = AppConfig::new("KIWAVI", "bunny@theaibunny.com");
        let kiwavi = Kiwavi::new("test_salt", app_config).unwrap();

        // Get the current valid code and secret
        let current_code = kiwavi.get_current_code();
        let secret = kiwavi.get_totp_secret();

        // Test with correct code
        let result = Kiwavi::validate_totp_code(&secret, &current_code).unwrap();
        assert!(result, "Correct code should validate successfully");

        // Test with incorrect code
        let result = Kiwavi::validate_totp_code(&secret, "000000").unwrap();
        assert!(!result, "Incorrect code should not validate");

        // Test with invalid base32 secret
        let result = Kiwavi::validate_totp_code("INVALID_BASE32", &current_code);
        assert!(result.is_err(), "Invalid base32 should return error");

        // Test with empty secret
        let result = Kiwavi::validate_totp_code("", &current_code);
        assert!(result.is_err(), "Empty secret should return error");

        // Test with empty code
        let result = Kiwavi::validate_totp_code(&secret, "");
        assert!(!result.unwrap(), "Empty code should not validate");
    }

    #[test]
    fn test_validate_totp_code_with_known_values() {
        // Test with a known TOTP secret and expected codes
        // This uses the standard test secret from RFC 6238
        let known_secret = "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ"; // Base32 of "12345678901234567890"

        // Note: These tests are time-sensitive.

        let result = Kiwavi::validate_totp_code(known_secret, "000000");
        assert!(
            result.is_ok(),
            "Function should handle known secret without error"
        );
    }
}