#![allow(non_snake_case)] #![allow(clippy::many_single_char_names)]
#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::clone_on_copy)]
#![allow(clippy::dbg_macro)]
#![allow(clippy::print_stderr)]
#![allow(clippy::print_stdout)]
#![allow(clippy::single_char_pattern)]
#![allow(clippy::unwrap_used)]
#![allow(clippy::unchecked_duration_subtraction)]
#![allow(clippy::useless_vec)]
#![allow(clippy::needless_pass_by_value)]
use super::*;
use crate::{
common::colorize,
common::x25519_elligator2::*,
test_utils::{init_subscriber, FakePRNG},
};
use hex_literal::hex;
use tor_basic_utils::test_rng::testing_rng;
fn make_fake_ephem_key(bytes: &[u8]) -> EphemeralSecret {
assert_eq!(bytes.len(), 32);
let rng = FakePRNG::new(bytes);
EphemeralSecret::random_from_rng(rng)
}
#[test]
fn test_obfs4_roundtrip() -> Result<()> {
let mut rng = rand::thread_rng();
let relay_private = Obfs4NtorSecretKey::generate_for_test(&mut rng);
let x_sk = EphemeralSecret::random_from_rng(&mut rng);
let y = Obfs4NtorSecretKey::generate_for_test(&mut rng);
let mut sid = [0u8; SESSION_ID_LEN];
rand::thread_rng().fill_bytes(&mut sid);
let chs_materials = CHSMaterials::new(relay_private.pk, colorize(sid));
let server = Server::new_from_key(relay_private);
let shs_materials = SHSMaterials {
identity_keys: server.identity_keys.clone(),
len_seed: [0u8; SEED_LENGTH],
session_id: "s-yyy".into(),
};
let (state, create_msg) = client_handshake_obfs4_no_keygen(x_sk, chs_materials).unwrap();
let ephem = make_fake_ephem_key(&y.sk.as_bytes()[..]);
let (s_keygen, created_msg) = server
.server_handshake_obfs4_no_keygen(ephem, &create_msg[..], shs_materials)
.unwrap();
let (c_keygen, _) = client_handshake2_obfs4(created_msg, &state)?;
let c_keys = c_keygen.expand(72)?;
let s_keys = s_keygen.expand(72)?;
assert_eq!(c_keys, s_keys);
Ok(())
}
#[test]
fn test_obfs4_roundtrip_highlevel() -> Result<()> {
let rng = testing_rng();
let relay_secret = StaticSecret::random_from_rng(rng);
let relay_public = PublicKey::from(&relay_secret);
let relay_identity = RsaIdentity::from_bytes(&[12; 20]).unwrap();
let relay_ntpk = Obfs4NtorPublicKey {
id: relay_identity,
pk: relay_public,
};
let hs_materials = CHSMaterials::new(relay_ntpk, "c-xxx".into());
let (state, cmsg) = Obfs4NtorHandshake::client1(&hs_materials, &())?;
let relay_ntsk = Obfs4NtorSecretKey {
pk: relay_ntpk,
sk: relay_secret,
};
let server = Server::new_from_key(relay_ntsk.clone());
let shs_materials = [SHSMaterials::new(
&relay_ntsk,
"s-yyy".into(),
[0u8; SEED_LENGTH],
)];
let (skeygen, smsg) = server
.server(&mut |_: &()| Some(()), &shs_materials, &cmsg)
.unwrap();
let (_extensions, ckeygen) = Obfs4NtorHandshake::client2(state, smsg)?;
let skeys = skeygen.expand(55)?;
let ckeys = ckeygen.expand(55)?;
assert_eq!(skeys, ckeys);
Ok(())
}
#[test]
fn test_obfs4_testvec_compat() -> Result<()> {
init_subscriber();
let b_sk = hex!("a83fdd04eb9ed77a2b38d86092a09a1cecfb93a7bdec0da35e542775b2e7af6e");
let x_sk = hex!("308ff4f3a0ebe8c1a93bcd40d67e3eec6b856aa5c07ef6d5a3d3cedf13dcf150");
let y_sk = hex!("881f9ad60e0833a627f0c47f5aafbdcb0b5471800eaeaa1e678291b947e4295c");
let id = hex!("000102030405060708090a0b0c0d0e0f10111213");
let expected_seed = "05b858d18df21a01566c74d39a5b091b4415f103c05851e77e79b274132dc5b5";
let expected_auth = "dc71f8ded2e56f829f1b944c1e94357fa8b7987f10211a017e2d1f2455092917";
let sk: StaticSecret = b_sk.into();
let pk = Obfs4NtorPublicKey {
id: RsaIdentity::from_bytes(&id).unwrap(),
pk: (&sk).into(),
};
let relay_sk = Obfs4NtorSecretKey { pk, sk };
let server = Server::new_from_key(relay_sk.clone());
let x = EphemeralSecret::from_parts(x_sk.into(), 0u8);
let chs_materials = CHSMaterials::new(pk, "c-xxx".into());
let shs_materials =
SHSMaterials::new(&server.identity_keys, "s-yyy".into(), [0u8; SEED_LENGTH]);
let (state, create_msg) = client_handshake_obfs4_no_keygen(x, chs_materials).unwrap();
let (s_keygen, created_msg) = server
.server_handshake_obfs4_no_keygen(
make_fake_ephem_key(&y_sk[..]), &create_msg[..],
shs_materials,
)
.unwrap();
let (c_keygen, auth) = client_handshake2_no_auth_check_obfs4(created_msg, &state)?;
let seed = c_keygen.seed.clone();
let c_keys = c_keygen.expand(72)?;
let s_keys = s_keygen.expand(72)?;
assert_eq!(&s_keys[..], &c_keys[..]);
assert_eq!(hex::encode(auth), expected_auth);
assert_eq!(hex::encode(&seed[..]), expected_seed);
Ok(())
}
#[cfg(target_feature = "disabled")]
#[test]
fn test_ntor_v1_testvec() -> Result<()> {
let b_sk = hex!("4820544f4c4420594f5520444f474954204b454550532048415050454e494e47");
let x_sk = hex!("706f6461792069207075742e2e2e2e2e2e2e2e4a454c4c59206f6e2074686973");
let y_sk = hex!("70686520737175697272656c2e2e2e2e2e2e2e2e686173206869732067616d65");
let id = hex!("69546f6c64596f7541626f75745374616972732e");
let client_handshake = hex!("69546f6c64596f7541626f75745374616972732eccbc8541904d18af08753eae967874749e6149f873de937f57f8fd903a21c471e65dfdbef8b2635837fe2cebc086a8096eae3213e6830dc407516083d412b078");
let server_handshake = hex!("390480a14362761d6aec1fea840f6e9e928fb2adb7b25c670be1045e35133a371cbdf68b89923e1f85e8e18ee6e805ea333fe4849c790ffd2670bd80fec95cc8");
let keys = hex!("0c62dee7f48893370d0ef896758d35729867beef1a5121df80e00f79ed349af39b51cae125719182f19d932a667dae1afbf2e336e6910e7822223e763afad0a13342157969dc6b79");
let sk: StaticSecret = b_sk.into();
let pk = Obfs4NtorPublicKey {
id: RsaIdentity::from_bytes(&id).unwrap(),
pk: (&sk).into(),
rp: (&sk).into(),
};
let relay_sk = Obfs4NtorSecretKey { pk, sk };
let x: StaticSecret = x_sk.into();
let y: StaticSecret = y_sk.into();
let (state, create_msg) =
client_handshake_obfs4_no_keygen((&x).into(), x, &relay_sk.pk).unwrap();
assert_eq!(&create_msg[..], &client_handshake[..]);
let (s_keygen, created_msg) = server_handshake_obfs4_no_keygen(
(&y).into(),
make_fake_ephem_key(&y_sk[..]), &create_msg[..],
&[relay_sk],
)
.unwrap();
assert_eq!(&created_msg[..], &server_handshake[..]);
let c_keygen = client_handshake2_obfs4(created_msg, &state)?;
let c_keys = c_keygen.expand(keys.len())?;
let s_keys = s_keygen.expand(keys.len())?;
assert_eq!(&c_keys[..], &keys[..]);
assert_eq!(&s_keys[..], &keys[..]);
Ok(())
}
#[test]
fn failing_handshakes() {
let mut rng = testing_rng();
let relay_secret = StaticSecret::random_from_rng(&mut rng);
let relay_public = PublicKey::from(&relay_secret);
let wrong_public = PublicKey::from([16_u8; 32]);
let relay_identity = RsaIdentity::from_bytes(&[12; 20]).unwrap();
let wrong_identity = RsaIdentity::from_bytes(&[13; 20]).unwrap();
let relay_ntpk = Obfs4NtorPublicKey {
id: relay_identity,
pk: relay_public,
};
let relay_ntsk = Obfs4NtorSecretKey {
pk: relay_ntpk.clone(),
sk: relay_secret,
};
let wrong_ntpk1 = Obfs4NtorPublicKey {
id: wrong_identity,
pk: relay_public,
};
let wrong_ntpk2 = Obfs4NtorPublicKey {
id: relay_identity,
pk: wrong_public,
};
let resources = &Server::new_from_random(&mut rng);
let mut hs_materials = CHSMaterials::new(wrong_ntpk1.clone(), "c-xxx".into());
let (_, handshake1) = Obfs4NtorHandshake::client1(&hs_materials, &()).unwrap();
hs_materials.node_pubkey = wrong_ntpk2;
let (_, handshake2) = Obfs4NtorHandshake::client1(&hs_materials, &()).unwrap();
hs_materials.node_pubkey = relay_ntpk;
let (st3, handshake3) = Obfs4NtorHandshake::client1(&hs_materials, &()).unwrap();
let shs_materials = [SHSMaterials::new(
&relay_ntsk,
"s-yyy".into(),
[0u8; SEED_LENGTH],
)];
let ans1 = resources.server(&mut |_: &()| Some(()), &shs_materials, &handshake1);
let ans2 = resources.server(&mut |_: &()| Some(()), &shs_materials, &handshake2);
assert!(ans1.is_err());
assert!(ans2.is_err());
let (_, mut smsg) = resources
.server(&mut |_: &()| Some(()), &shs_materials, &handshake3)
.unwrap();
smsg[60] ^= 7;
let ans3 = Obfs4NtorHandshake::client2(st3, smsg);
assert!(ans3.is_err());
}