Unit test for the bridge table

This commit is contained in:
Ian Goldberg 2021-04-27 18:26:49 -04:00
parent beed9d95b2
commit 89b7fdf921
2 changed files with 83 additions and 3 deletions

View File

@ -15,6 +15,7 @@ sha2 = "0.9"
lazy_static = "1" lazy_static = "1"
hex_fmt = "0.3" hex_fmt = "0.3"
aes-gcm = "0.8" aes-gcm = "0.8"
base64 = "0.13"
[features] [features]
default = ["u64_backend"] default = ["u64_backend"]

View File

@ -14,7 +14,7 @@ use rand::RngCore;
use std::convert::TryInto; use std::convert::TryInto;
/// Each bridge information line is serialized into this many bytes /// Each bridge information line is serialized into this many bytes
pub const BRIDGE_BYTES: usize = 128; pub const BRIDGE_BYTES: usize = 220;
/// The max number of bridges per bucket /// The max number of bridges per bucket
pub const MAX_BRIDGES_PER_BUCKET: usize = 3; pub const MAX_BRIDGES_PER_BUCKET: usize = 3;
@ -32,8 +32,8 @@ pub struct BridgeLine {
pub addr: [u8; 16], pub addr: [u8; 16],
/// port /// port
pub port: u16, pub port: u16,
/// other protocol information, including pluggable trasport, public /// other protocol information, including pluggable transport,
/// key, etc. /// public key, etc.
pub info: [u8; BRIDGE_BYTES - 18], pub info: [u8; BRIDGE_BYTES - 18],
} }
@ -85,6 +85,40 @@ impl BridgeLine {
} }
res res
} }
/// Create a random BridgeLine for testing
#[cfg(test)]
pub fn random() -> Self {
let mut rng = rand::thread_rng();
let mut res: Self = Default::default();
// Pick a random 4-byte address
let mut addr: [u8; 4] = [0; 4];
rng.fill_bytes(&mut addr);
// If the leading byte is 224 or more, that's not a valid IPv4
// address. Choose an IPv6 address instead (but don't worry too
// much about it being well formed).
if addr[0] >= 224 {
rng.fill_bytes(&mut res.addr);
} else {
// Store an IPv4 address as a v4-mapped IPv6 address
res.addr[10] = 255;
res.addr[11] = 255;
res.addr[12..16].copy_from_slice(&addr);
};
let ports: [u16; 4] = [443, 4433, 8080, 43079];
let portidx = (rng.next_u32() % 4) as usize;
res.port = ports[portidx];
let mut fingerprint: [u8; 20] = [0; 20];
let mut cert: [u8; 52] = [0; 52];
rng.fill_bytes(&mut fingerprint);
rng.fill_bytes(&mut cert);
let infostr: String = format!(
"obfs4 {} cert={} iat-mode=0",
hex_fmt::HexFmt(fingerprint),
base64::encode_config(cert, base64::STANDARD_NO_PAD)
);
res.info[..infostr.len()].copy_from_slice(infostr.as_bytes());
res
}
} }
/// A BridgeTable is the internal structure holding the buckets /// A BridgeTable is the internal structure holding the buckets
@ -155,3 +189,48 @@ impl BridgeTable {
)) ))
} }
} }
// Unit tests that require access to private fields
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bridge_table() -> Result<(), aead::Error> {
// Create an empty bridge table
let mut btable: BridgeTable = Default::default();
// Make 20 buckets with one random bridge each
for _ in 0..20 {
let bucket: [BridgeLine; 3] =
[BridgeLine::random(), Default::default(), Default::default()];
btable.new_bucket(bucket);
}
// And 20 more with three random bridges each
for _ in 0..20 {
let bucket: [BridgeLine; 3] = [
BridgeLine::random(),
BridgeLine::random(),
BridgeLine::random(),
];
btable.new_bucket(bucket);
}
// Create the encrypted bridge table
btable.encrypt_table();
// Try to decrypt a 1-bridge bucket
let key7 = btable.keys[7];
let encbucket7 = btable.encbuckets[7];
let bucket7 = BridgeTable::decrypt_bucket(&key7, &encbucket7)?;
println!("bucket 7 = {:?}", bucket7);
// Try to decrypt a 3-bridge bucket
let key24 = btable.keys[24];
let encbucket24 = btable.encbuckets[24];
let bucket24 = BridgeTable::decrypt_bucket(&key24, &encbucket24)?;
println!("bucket 24 = {:?}", bucket24);
// Try to decrypt a bucket with the wrong key
let key12 = btable.keys[12];
let encbucket15 = btable.encbuckets[15];
let res = BridgeTable::decrypt_bucket(&key12, &encbucket15).unwrap_err();
println!("bucket key mismatch = {:?}", res);
Ok(())
}
}