chie-crypto 0.2.0

Cryptographic primitives for CHIE Protocol
Documentation
//! Constant-Time Operation Verification Tests
//!
//! This module verifies that cryptographic operations execute in constant time,
//! preventing timing side-channel attacks.

use chie_crypto::{KeyPair, constant_time_eq, decrypt, encrypt};

/// Verify constant-time comparison for equal inputs
#[test]
fn verify_ct_eq_same_inputs() {
    let a = [0x42u8; 32];
    let b = [0x42u8; 32];

    // Should be constant time regardless of input
    assert!(constant_time_eq(&a, &b));
}

/// Verify constant-time comparison for different inputs
#[test]
fn verify_ct_eq_different_inputs() {
    let a = [0x42u8; 32];
    let b = [0x43u8; 32];

    // Should be constant time regardless of where difference occurs
    assert!(!constant_time_eq(&a, &b));
}

/// Verify constant-time comparison with differences at various positions
#[test]
fn verify_ct_eq_difference_positions() {
    let base = [0x42u8; 32];

    // Test difference at each position
    for pos in 0..32 {
        let mut modified = base;
        modified[pos] ^= 0x01;

        // Should take same time regardless of position of difference
        assert!(!constant_time_eq(&base, &modified));
    }
}

/// Verify decryption fails in constant time for wrong authentication tag
#[test]
fn verify_decrypt_ct_auth_failure() {
    let key = [0x42u8; 32];
    let nonce = [0x24u8; 12];
    let plaintext = b"test message for constant-time verification";

    // Encrypt
    let mut ciphertext = encrypt(plaintext, &key, &nonce).unwrap();

    // Modify authentication tag (last 16 bytes)
    let len = ciphertext.len();
    if len >= 16 {
        ciphertext[len - 1] ^= 0x01;

        // Decryption should fail in constant time
        assert!(decrypt(&ciphertext, &key, &nonce).is_err());
    }
}

/// Verify signature verification is constant time for invalid signatures
#[test]
fn verify_signature_ct_invalid() {
    let keypair = KeyPair::generate();
    let message = b"test message";

    // Valid signature
    let signature = keypair.sign(message);

    // Modify signature at various positions
    for pos in 0..signature.len() {
        let mut modified_sig = signature;
        modified_sig[pos] ^= 0x01;

        // Verification should fail in constant time regardless of position
        assert!(!keypair.verify(message, &modified_sig));
    }
}

/// Verify constant-time behavior with all-zero vs all-one inputs
#[test]
fn verify_ct_eq_extremes() {
    let zeros = [0x00u8; 32];
    let ones = [0xFFu8; 32];

    // Should be constant time even for extreme values
    assert!(!constant_time_eq(&zeros, &ones));
    assert!(constant_time_eq(&zeros, &zeros));
    assert!(constant_time_eq(&ones, &ones));
}

/// Verify constant-time comparison with single bit difference
#[test]
fn verify_ct_eq_single_bit() {
    let a = [0x00u8; 32];
    let mut b = [0x00u8; 32];

    // Single bit difference in first byte
    b[0] = 0x01;
    assert!(!constant_time_eq(&a, &b));

    // Single bit difference in last byte
    b[0] = 0x00;
    b[31] = 0x80;
    assert!(!constant_time_eq(&a, &b));
}

/// Verify decryption timing is independent of plaintext content
#[test]
fn verify_decrypt_ct_plaintext_independent() {
    let key = [0x42u8; 32];
    let nonce = [0x24u8; 12];

    // Encrypt various plaintexts
    let plaintexts = [
        vec![0x00u8; 64],                   // All zeros
        vec![0xFFu8; 64],                   // All ones
        vec![0xAAu8; 64],                   // Pattern
        (0..64).map(|i| i as u8).collect(), // Sequence
    ];

    for plaintext in &plaintexts {
        let ciphertext = encrypt(plaintext, &key, &nonce).unwrap();

        // Decryption should be constant time regardless of plaintext content
        let decrypted = decrypt(&ciphertext, &key, &nonce).unwrap();
        assert_eq!(&decrypted[..], &plaintext[..]);
    }
}

/// Verify signature verification timing is independent of message content
#[test]
fn verify_signature_ct_message_independent() {
    let keypair = KeyPair::generate();

    // Various messages
    let messages = [
        vec![0x00u8; 64],                   // All zeros
        vec![0xFFu8; 64],                   // All ones
        vec![0xAAu8; 64],                   // Pattern
        (0..64).map(|i| i as u8).collect(), // Sequence
    ];

    for message in &messages {
        let signature = keypair.sign(message);

        // Verification should be constant time regardless of message content
        assert!(keypair.verify(message, &signature));
    }
}

/// Verify constant-time comparison works with heap-allocated data
#[test]
fn verify_ct_eq_heap_allocated() {
    let a = vec![0x42u8; 32];
    let b = vec![0x42u8; 32];
    let c = vec![0x43u8; 32];

    assert!(constant_time_eq(&a, &b));
    assert!(!constant_time_eq(&a, &c));
}

/// Test constant-time comparison property: time independent of input values
#[test]
fn verify_ct_eq_timing_property() {
    // This test verifies the property that comparison time should not
    // depend on where the first difference occurs

    let base = [0xAAu8; 32];

    // Create arrays with differences at different positions
    for first_diff_pos in [0, 8, 16, 24, 31] {
        let mut test = base;
        test[first_diff_pos] ^= 0x01;

        // All comparisons should take the same time
        // (We can't measure this directly in a unit test, but we verify the behavior)
        assert!(!constant_time_eq(&base, &test));
    }
}

/// Verify encryption produces different ciphertexts for different plaintexts
#[test]
fn verify_encryption_output_different() {
    let key = [0x42u8; 32];
    let nonce = [0x24u8; 12];

    let plaintext1 = b"message one";
    let plaintext2 = b"message two";

    let ciphertext1 = encrypt(plaintext1, &key, &nonce).unwrap();
    let ciphertext2 = encrypt(plaintext2, &key, &nonce).unwrap();

    // Different plaintexts should produce different ciphertexts
    assert_ne!(ciphertext1, ciphertext2);
}

/// Verify constant-time comparison with arrays of all same values
#[test]
fn verify_ct_eq_uniform_arrays() {
    for byte_value in [0x00, 0x55, 0xAA, 0xFF] {
        let a = [byte_value; 32];
        let b = [byte_value; 32];
        let c = [byte_value ^ 0x01; 32];

        assert!(constant_time_eq(&a, &b));
        assert!(!constant_time_eq(&a, &c));
    }
}