use crate::engine::bt_mse_handshake::*;
use sha1::{Digest, Sha1};
fn create_test_info_hash() -> [u8; 20] {
[
0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32,
0x10, 0xAA, 0xBB, 0xCC, 0xDD,
]
}
#[test]
fn test_method_selection_encrypted_support() {
let info_hash = create_test_info_hash();
let manager = MseHandshakeManager::new(info_hash).unwrap();
let method_sel = manager.build_method_selection();
assert_eq!(method_sel.len(), 8);
assert_eq!(&method_sel, b"\x13MSegadd");
}
#[test]
fn test_method_selection_plain_only() {
let info_hash = create_test_info_hash();
let manager = MseHandshakeManager::new(info_hash).unwrap();
let method_sel = manager.build_method_selection();
assert_eq!(method_sel.len(), 8);
assert_eq!(&method_sel, b"\x13MSegadd");
}
#[test]
fn test_parse_remote_method_msegadd() {
let result = MseHandshakeManager::parse_remote_method_selection(b"\x13MSegadd").unwrap();
assert_eq!(result, CryptoMethod::Rc4);
}
#[test]
fn test_parse_remote_method_invalid() {
let result = MseHandshakeManager::parse_remote_method_selection(b"");
assert!(result.is_err());
let result = MseHandshakeManager::parse_remote_method_selection(b"\xffInvalid");
assert!(result.is_err());
}
#[test]
fn test_key_exchange_payload_format() {
let info_hash = create_test_info_hash();
let manager = MseHandshakeManager::new(info_hash).unwrap();
let payload = manager
.build_key_exchange_payload(&[CryptoMethod::Rc4])
.unwrap();
assert!(payload.len() >= 38, "Payload too short: {}", payload.len());
let pad_d = u16::from_be_bytes([payload[0], payload[1]]);
let pad_len = u16::from_be_bytes([payload[2], payload[3]]);
let crypto_pro = u16::from_be_bytes([payload[4], payload[5]]);
assert_eq!(pad_d, pad_len);
assert!(crypto_pro & 0x0002 != 0);
let dh_pubkey_start = payload.len() - 32;
let dh_pubkey = &payload[dh_pubkey_start..];
assert_eq!(dh_pubkey.len(), 32);
}
#[test]
fn test_dh_shared_secret_computation() {
use ring::agreement::{self, EphemeralPrivateKey, UnparsedPublicKey};
use ring::rand::SystemRandom;
let rng = SystemRandom::new();
let alice_private = EphemeralPrivateKey::generate(&agreement::X25519, &rng).unwrap();
let alice_public = alice_private.compute_public_key().unwrap();
let mut alice_pubkey_vec = vec![0u8; 32]; alice_pubkey_vec.copy_from_slice(alice_public.as_ref());
let bob_private = EphemeralPrivateKey::generate(&agreement::X25519, &rng).unwrap();
let bob_public = bob_private.compute_public_key().unwrap();
let mut bob_pubkey_vec = vec![0u8; 32]; bob_pubkey_vec.copy_from_slice(bob_public.as_ref());
let bob_pubkey_parsed = UnparsedPublicKey::new(&agreement::X25519, &bob_pubkey_vec);
let alice_shared = agreement::agree_ephemeral(
alice_private,
&bob_pubkey_parsed,
|s: &[u8]| -> Result<Vec<u8>, ring::error::Unspecified> { Ok(s.to_vec()) },
)
.unwrap();
let alice_pubkey_parsed = UnparsedPublicKey::new(&agreement::X25519, &alice_pubkey_vec);
let bob_shared = agreement::agree_ephemeral(
bob_private,
&alice_pubkey_parsed,
|s: &[u8]| -> Result<Vec<u8>, ring::error::Unspecified> { Ok(s.to_vec()) },
)
.unwrap();
assert_eq!(alice_shared, bob_shared, "DH shared secrets must match");
}
#[test]
fn test_skey_computation() {
let info_hash = create_test_info_hash();
let mut manager = MseHandshakeManager::new(info_hash).unwrap();
let fake_shared_secret: Vec<u8> = vec![0x42; 32];
manager.shared_secret = Some(fake_shared_secret.clone());
let skey = manager.compute_skey().unwrap();
let mut hasher = Sha1::new();
hasher.update(info_hash);
hasher.update(&fake_shared_secret);
let expected_skey = hasher.finalize();
assert_eq!(skey.to_vec(), expected_skey.to_vec());
assert_eq!(skey.len(), 20); }
#[test]
fn test_key_derivation_send_recv_different() {
let skey = vec![0xA5u8; 20];
let shared_secret = vec![0x42u8; 32];
let (send_key, recv_key): (Vec<u8>, Vec<u8>) =
MseHandshakeManager::derive_keys(&skey, &shared_secret);
assert_ne!(
send_key, recv_key,
"Send and receive keys must be different"
);
assert_eq!(send_key.len(), 16);
assert_eq!(recv_key.len(), 16);
}
#[test]
fn test_rc4_encrypt_decrypt_roundtrip() {
let send_key = vec![0xA5u8; 16];
let recv_key = vec![0xB6u8; 16];
let mut sender_ctx = MseCryptoContext::new(&send_key, &recv_key, CryptoMethod::Rc4);
let plaintext = b"Hello, BitTorrent MSE!";
let encrypted = sender_ctx.encrypt(plaintext).unwrap();
assert_ne!(encrypted, plaintext.to_vec());
let mut receiver_ctx = MseCryptoContext::new(&recv_key, &send_key, CryptoMethod::Rc4);
let decrypted = receiver_ctx.decrypt(&encrypted).unwrap();
assert_eq!(
decrypted,
plaintext.to_vec(),
"Receiver should decrypt sender's ciphertext"
);
}
#[test]
fn test_rc4_initial_state_discard() {
let key = vec![0x42u8; 16];
let mut ctx1 = MseCryptoContext::new(&key, &key, CryptoMethod::Rc4);
let mut ctx2 = MseCryptoContext::new(&key, &key, CryptoMethod::Rc4);
let data = b"Test data for keystream discard verification";
let enc1 = ctx1.encrypt(data).unwrap();
let enc2 = ctx2.encrypt(data).unwrap();
assert_eq!(
enc1, enc2,
"Same key should produce same ciphertext after discard"
);
}
#[test]
fn test_plaintext_fallback_no_op() {
let ctx = MseHandshakeManager::plaintext_fallback();
assert!(!ctx.is_encrypted());
assert_eq!(ctx.crypto_method(), CryptoMethod::Plain);
let plaintext = b"Plain text should not be modified";
let mut ctx_mut = ctx;
let encrypted = ctx_mut.encrypt(plaintext).unwrap();
assert_eq!(encrypted, plaintext.to_vec());
let decrypted = ctx_mut.decrypt(&encrypted).unwrap();
assert_eq!(decrypted, plaintext.to_vec());
}
#[test]
fn test_state_machine_full_flow() {
let info_hash = create_test_info_hash();
let manager = MseHandshakeManager::new(info_hash).unwrap();
assert!(matches!(manager.state(), MseState::Idle));
manager.set_state(MseState::MethodSelectionSent);
assert!(matches!(manager.state(), MseState::MethodSelectionSent));
manager.set_state(MseState::KeyExchangeInProgress);
assert!(matches!(manager.state(), MseState::KeyExchangeInProgress));
manager.set_state(MseState::VerificationPending);
assert!(matches!(manager.state(), MseState::VerificationPending));
let fallback_ctx = MseHandshakeManager::plaintext_fallback();
manager.set_state(MseState::Established(fallback_ctx));
assert!(matches!(manager.state(), MseState::Established(_)));
}
#[test]
fn test_crypto_method_negotiation_rc4() {
let info_hash = create_test_info_hash();
let mut alice = MseHandshakeManager::new(info_hash).unwrap();
let mut bob = MseHandshakeManager::new(info_hash).unwrap();
let alice_method = alice.build_method_selection();
assert_eq!(&alice_method, b"\x13MSegadd");
let bob_method = bob.build_method_selection();
assert_eq!(&bob_method, b"\x13MSegadd");
let alice_parse = MseHandshakeManager::parse_remote_method_selection(&bob_method).unwrap();
let bob_parse = MseHandshakeManager::parse_remote_method_selection(&alice_method).unwrap();
assert_eq!(alice_parse, CryptoMethod::Rc4);
assert_eq!(bob_parse, CryptoMethod::Rc4);
let alice_payload = alice
.build_key_exchange_payload(&[CryptoMethod::Rc4])
.unwrap();
let bob_payload = bob
.build_key_exchange_payload(&[CryptoMethod::Rc4])
.unwrap();
alice.process_remote_key_exchange(&bob_payload).unwrap();
bob.process_remote_key_exchange(&alice_payload).unwrap();
assert_eq!(
alice.shared_secret(),
bob.shared_secret(),
"Shared secrets must match"
);
let alice_verify = alice.build_verification_payload(CryptoMethod::Rc4).unwrap();
let bob_verify = bob.build_verification_payload(CryptoMethod::Rc4).unwrap();
let _alice_ctx = alice.process_remote_verification(&bob_verify).unwrap();
let _bob_ctx = bob.process_remote_verification(&alice_verify).unwrap();
}
#[test]
fn test_crypto_method_negotiation_fallback_to_plain() {
let info_hash = create_test_info_hash();
let _alice = MseHandshakeManager::new(info_hash).unwrap();
let bob_method_selection = b"\x00".to_vec();
let parsed = MseHandshakeManager::parse_remote_method_selection(&bob_method_selection).unwrap();
assert_eq!(parsed, CryptoMethod::Plain);
let ctx = MseHandshakeManager::plaintext_fallback();
assert!(!ctx.is_encrypted());
}