use std::process::Command;
use std::time::{Duration, Instant};
use std::net::TcpStream;
use std::io::{Write, Read};
use rand::{Rng, thread_rng};
#[cfg(test)]
mod security_tests {
use super::*;
#[test]
fn fuzz_banner_exchange() {
println!("🔨 Fuzzing SSH banner exchange...");
let long_banner = b"SSH-2.0-".repeat(1000);
let binary_data = [0xFF; 1024];
let fuzz_banners: Vec<&[u8]> = vec![
b"SSH-2.0-QSSH_1.0\r\n", long_banner.as_slice(), b"SSH-1.0-OLD\r\n", b"NOT_SSH\r\n", b"SSH-2.0-\x00\x01\x02\x03\r\n", b"SSH-2.0-\xF0\x9F\x98\x80Unicode\xF0\x9F\x98\x80\r\n", b"", &binary_data, b"SSH-2.0-\nNewline\nInjection\r\n", ];
for (i, banner) in fuzz_banners.iter().enumerate() {
match TcpStream::connect("127.0.0.1:22222") {
Ok(mut stream) => {
let _ = stream.write_all(banner);
let mut response = vec![0; 256];
let _ = stream.read(&mut response);
println!(" Test {}: Sent {} bytes, got response", i, banner.len());
}
Err(_) => println!(" Test {}: Connection refused (good!)", i),
}
}
}
#[test]
fn fuzz_key_exchange() {
println!("🔨 Fuzzing key exchange packets...");
let mut rng = thread_rng();
for size in [0, 1, 100, 1000, 65535, 1_000_000] {
let mut packet = vec![0u8; size];
rng.fill(&mut packet[..]);
match TcpStream::connect("127.0.0.1:22222") {
Ok(mut stream) => {
let _ = stream.write_all(b"SSH-2.0-FuzzClient\r\n");
std::thread::sleep(Duration::from_millis(100));
let _ = stream.write_all(&packet);
let mut response = vec![0; 256];
match stream.read(&mut response) {
Ok(n) => println!(" Size {}: Server responded with {} bytes", size, n),
Err(e) => println!(" Size {}: Server closed connection: {}", size, e),
}
}
Err(_) => println!(" Size {}: Connection refused", size),
}
}
}
#[test]
fn pentest_auth_bypass() {
println!("🔓 Testing authentication bypass attempts...");
let bypass_attempts = vec![
("", ""), ("root", ""), ("admin", "admin"), ("qssh", "qssh"), ("' OR '1'='1", "password"), ("admin\x00extra", "password"), ("\r\nUSER admin", "password"), ("../../etc/passwd", "password"), ];
for (user, pass) in bypass_attempts {
let output = Command::new("timeout")
.arg("2")
.arg("./target/debug/qssh")
.arg("-o")
.arg("StrictHostKeyChecking=no")
.arg(format!("{}@127.0.0.1:22222", user))
.env("QSSH_PASSWORD", pass)
.output();
match output {
Ok(out) => {
if out.status.success() {
panic!("❌ AUTH BYPASS: user='{}' pass='{}' succeeded!", user, pass);
} else {
println!(" ✓ Rejected: user='{}' pass='{}'", user, pass);
}
}
Err(_) => println!(" ✓ Timeout/error for user='{}'", user),
}
}
}
#[test]
fn pentest_quantum_downgrade() {
println!("🔐 Testing quantum algorithm downgrade attacks...");
let downgrade_attempts = vec![
"--pq-algo=none",
"--pq-algo=rsa",
"--pq-algo=classical",
"--pq-algo=weak_falcon",
"--force-classic-crypto",
];
for attempt in downgrade_attempts {
let output = Command::new("./target/debug/qssh")
.arg(attempt)
.arg("test@127.0.0.1:22222")
.arg("-vv")
.output();
if let Ok(out) = output {
let stdout = String::from_utf8_lossy(&out.stdout);
let stderr = String::from_utf8_lossy(&out.stderr);
if stdout.contains("classical") || stderr.contains("RSA") {
panic!("❌ QUANTUM DOWNGRADE: {} allowed classical crypto!", attempt);
} else {
println!(" ✓ Rejected downgrade: {}", attempt);
}
}
}
}
#[test]
fn timing_attack_auth() {
println!("⏱️ Testing timing attacks on authentication...");
let test_users = vec![
"root", "admin", "qsshtest", "zzz999xxx", ];
let mut timings = Vec::new();
for user in &test_users {
let mut user_times = Vec::new();
for _ in 0..5 {
let start = Instant::now();
let _ = Command::new("timeout")
.arg("1")
.arg("./target/debug/qssh")
.arg(format!("{}@127.0.0.1:22222", user))
.env("QSSH_PASSWORD", "wrongpass")
.output();
let elapsed = start.elapsed();
user_times.push(elapsed.as_millis());
}
let avg_time = user_times.iter().sum::<u128>() / user_times.len() as u128;
timings.push((user.to_string(), avg_time));
println!(" User '{}': avg {}ms", user, avg_time);
}
let max_diff = timings.iter().map(|(_, t)| t).max().unwrap()
- timings.iter().map(|(_, t)| t).min().unwrap();
if max_diff > 50 { println!(" ⚠️ WARNING: Timing differences >50ms detected!");
println!(" Could reveal user enumeration!");
} else {
println!(" ✓ Timing differences negligible (<50ms)");
}
}
#[test]
fn dos_connection_flood() {
println!("🌊 Testing connection flooding resistance...");
let mut connections = Vec::new();
let start = Instant::now();
for i in 0..1000 {
match TcpStream::connect("127.0.0.1:22222") {
Ok(stream) => {
connections.push(stream);
if i % 100 == 0 {
println!(" Opened {} connections...", i);
}
}
Err(e) => {
println!(" Connection {} rejected: {} (good!)", i, e);
break;
}
}
if start.elapsed() > Duration::from_secs(5) {
println!(" Timeout after {} connections", i);
break;
}
}
println!(" Max connections achieved: {}", connections.len());
if connections.len() > 500 {
println!(" ⚠️ WARNING: Server allows >500 concurrent connections!");
} else {
println!(" ✓ Connection limits appear reasonable");
}
}
#[test]
fn dos_memory_exhaustion() {
println!("💾 Testing memory exhaustion attacks...");
let sizes = vec![
1024 * 1024, 10 * 1024 * 1024, 100 * 1024 * 1024, ];
for size in sizes {
match TcpStream::connect("127.0.0.1:22222") {
Ok(mut stream) => {
let _ = stream.write_all(b"SSH-2.0-MemTest\r\n");
let big_data = vec![0x41u8; size];
match stream.write_all(&big_data) {
Ok(_) => println!(" Sent {}MB packet", size / 1024 / 1024),
Err(e) => println!(" Failed to send {}MB: {}", size / 1024 / 1024, e),
}
std::thread::sleep(Duration::from_millis(500));
match TcpStream::connect("127.0.0.1:22222") {
Ok(_) => println!(" ✓ Server still responsive after {}MB", size / 1024 / 1024),
Err(_) => println!(" ❌ Server unresponsive after {}MB!", size / 1024 / 1024),
}
}
Err(_) => println!(" Connection refused"),
}
}
}
#[test]
fn quantum_key_reuse() {
println!("🔑 Testing quantum key reuse detection...");
let mut session_keys = Vec::new();
for i in 0..5 {
let output = Command::new("./target/debug/qssh")
.arg("-vvv") .arg("test@127.0.0.1:22222")
.arg("-o")
.arg("BatchMode=yes")
.output();
if let Ok(out) = output {
let stderr = String::from_utf8_lossy(&out.stderr);
if stderr.contains("session_key") || stderr.contains("shared_secret") {
let key_hash = format!("{:x}", i);
if session_keys.contains(&key_hash) {
panic!("❌ QUANTUM KEY REUSE: Same session key used twice!");
}
session_keys.push(key_hash);
}
}
}
println!(" ✓ No key reuse detected in {} connections", session_keys.len());
}
#[test]
fn quantum_stark_bypass() {
println!("🎯 Testing STARK proof validation...");
match TcpStream::connect("127.0.0.1:22222") {
Ok(mut stream) => {
let _ = stream.write_all(b"SSH-2.0-STARKTest\r\n");
let fake_proof = b"STARK_PROOF:INVALID_DATA_HERE";
let _ = stream.write_all(fake_proof);
let mut response = vec![0; 256];
match stream.read(&mut response) {
Ok(n) => {
let resp_str = String::from_utf8_lossy(&response[..n]);
if resp_str.contains("accepted") {
panic!("❌ STARK BYPASS: Invalid proof accepted!");
} else {
println!(" ✓ Invalid STARK proof rejected");
}
}
Err(_) => println!(" ✓ Connection closed on invalid proof"),
}
}
Err(_) => println!(" Connection failed"),
}
}
}