vncrs 0.1.4

A pure Rust VNC server library for Windows
Documentation
use crate::error::{Result, VncError};
use cipher::{BlockEncrypt, KeyInit};
use des::Des;
use rand::RngCore;
use std::io::{Read, Write};

fn reverse_bits(byte: u8) -> u8 {
    let mut result = 0u8;
    for i in 0..8 {
        result |= ((byte >> i) & 1) << (7 - i);
    }
    result
}

fn password_to_key(password: &str) -> [u8; 8] {
    let mut key = [0u8; 8];
    for (i, &b) in password.as_bytes().iter().take(8).enumerate() {
        key[i] = reverse_bits(b);
    }
    key
}

fn encrypt_challenge(key: &[u8; 8], challenge: &[u8; 16]) -> [u8; 16] {
    let cipher = Des::new_from_slice(key).unwrap();
    let mut result = [0u8; 16];
    result.copy_from_slice(challenge);

    let (block1, block2) = result.split_at_mut(8);
    cipher.encrypt_block(block1.into());
    cipher.encrypt_block(block2.into());

    result
}

pub fn perform_vnc_auth<R: Read, W: Write>(
    reader: &mut R,
    writer: &mut W,
    password: &str,
) -> Result<()> {
    let mut challenge = [0u8; 16];
    rand::thread_rng().fill_bytes(&mut challenge);

    writer.write_all(&challenge)?;
    writer.flush()?;

    let mut client_response = [0u8; 16];
    reader.read_exact(&mut client_response)?;

    let key = password_to_key(password);
    let expected = encrypt_challenge(&key, &challenge);

    if client_response == expected {
        writer.write_all(&0u32.to_be_bytes())?;
        writer.flush()?;
        println!("🔓 Authentication successful");
        Ok(())
    } else {
        writer.write_all(&1u32.to_be_bytes())?;
        let reason = b"Authentication failed";
        writer.write_all(&(reason.len() as u32).to_be_bytes())?;
        writer.write_all(reason)?;
        writer.flush()?;
        println!("🔒 Authentication FAILED");
        Err(VncError::Handshake("Authentication failed".into()))
    }
}
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_reverse_bits() {
        assert_eq!(reverse_bits(0b10000000), 0b00000001);
        assert_eq!(reverse_bits(0b11001010), 0b01010011);
        assert_eq!(reverse_bits(0xFF), 0xFF);
        assert_eq!(reverse_bits(0x00), 0x00);
    }

    #[test]
    fn test_password_to_key_length() {
        let key = password_to_key("test");
        assert_eq!(key.len(), 8);
    }

    #[test]
    fn test_password_to_key_truncation() {
        let key_short = password_to_key("ab");
        let key_long = password_to_key("abcdefghijklmnop");
        assert_eq!(key_short[0], key_long[0]);
        assert_eq!(key_short[1], key_long[1]);
        assert_eq!(key_short[2], 0);
    }

    #[test]
    fn test_encrypt_challenge_deterministic() {
        let key = password_to_key("secret");
        let challenge = [1u8; 16];
        let result1 = encrypt_challenge(&key, &challenge);
        let result2 = encrypt_challenge(&key, &challenge);
        assert_eq!(result1, result2);
    }

    #[test]
    fn test_auth_success() {
        let password = "secret";
        let key = password_to_key(password);
        let challenge = [42u8; 16];

        let client_response = encrypt_challenge(&key, &challenge);
        let expected = encrypt_challenge(&key, &challenge);
        assert_eq!(client_response, expected);
    }

    #[test]
    fn test_auth_wrong_password() {
        let server_key = password_to_key("correct");
        let client_key = password_to_key("wrong");
        let challenge = [7u8; 16];

        let server_expected = encrypt_challenge(&server_key, &challenge);
        let client_response = encrypt_challenge(&client_key, &challenge);
        assert_ne!(server_expected, client_response);
    }
}