/*! The encrypted table of bridges. The table consists of a number of * buckets, each holding some number (currently up to 3) of bridges. * Each bucket is individually encrypted with a bucket key. Users will * have a credential containing a bucket (number, key) combination, and * so will be able to read one of the buckets. Users will either * download the whole encrypted bucket list or use PIR to download a * piece of it, so that the bridge authority does not learn which bucket * the user has access to. */ use aes_gcm::aead; use aes_gcm::aead::{generic_array::GenericArray, Aead, NewAead}; use aes_gcm::Aes128Gcm; use rand::RngCore; use std::convert::TryInto; /// Each bridge information line is serialized into this many bytes pub const BRIDGE_BYTES: usize = 128; /// The max number of bridges per bucket pub const MAX_BRIDGES_PER_BUCKET: usize = 3; /// The size of a plaintext bucket pub const BUCKET_BYTES: usize = BRIDGE_BYTES * MAX_BRIDGES_PER_BUCKET; /// The size of an encrypted bucket pub const ENC_BUCKET_BYTES: usize = BUCKET_BYTES + 12 + 16; /// A bridge information line #[derive(Debug)] pub struct BridgeLine { /// IPv4 or IPv6 address pub addr: [u8; 16], /// port pub port: u16, /// other protocol information, including pluggable trasport, public /// key, etc. pub info: [u8; BRIDGE_BYTES - 18], } impl Default for BridgeLine { /// An "empty" BridgeLine is represented by all zeros fn default() -> Self { Self { addr: [0; 16], port: 0, info: [0; BRIDGE_BYTES - 18], } } } impl BridgeLine { /// Encode a BridgeLine to a byte array pub fn encode(&self) -> [u8; BRIDGE_BYTES] { let mut res: [u8; BRIDGE_BYTES] = [0; BRIDGE_BYTES]; res[0..16].copy_from_slice(&self.addr); res[16..18].copy_from_slice(&self.port.to_be_bytes()); res[18..].copy_from_slice(&self.info); res } /// Decode a BridgeLine from a byte array pub fn decode(data: &[u8; BRIDGE_BYTES]) -> Self { let mut res: Self = Default::default(); res.addr.copy_from_slice(&data[0..16]); res.port = u16::from_be_bytes(data[16..18].try_into().unwrap()); res.info.copy_from_slice(&data[18..]); res } /// Encode a bucket to a byte array pub fn bucket_encode(bucket: &[BridgeLine; MAX_BRIDGES_PER_BUCKET]) -> [u8; BUCKET_BYTES] { let mut res: [u8; BUCKET_BYTES] = [0; BUCKET_BYTES]; let mut pos: usize = 0; for bridge in bucket { res[pos..pos + BRIDGE_BYTES].copy_from_slice(&bridge.encode()); pos += BRIDGE_BYTES; } res } /// Decode a bucket from a byte array pub fn bucket_decode(data: &[u8; BUCKET_BYTES]) -> [BridgeLine; MAX_BRIDGES_PER_BUCKET] { let mut pos: usize = 0; let mut res: [BridgeLine; MAX_BRIDGES_PER_BUCKET] = Default::default(); for bridge in res.iter_mut().take(MAX_BRIDGES_PER_BUCKET) { *bridge = BridgeLine::decode(data[pos..pos + BRIDGE_BYTES].try_into().unwrap()); pos += BRIDGE_BYTES; } res } } /// A BridgeTable is the internal structure holding the buckets /// containing the bridges, the keys used to encrypt the buckets, and /// the encrypted buckets. The encrypted buckets will be exposed to the /// users of the system, and each user credential will contain the /// decryption key for one bucket. #[derive(Debug, Default)] pub struct BridgeTable { keys: Vec<[u8; 16]>, buckets: Vec<[BridgeLine; MAX_BRIDGES_PER_BUCKET]>, encbuckets: Vec<[u8; ENC_BUCKET_BYTES]>, } // Invariant: the lengths of the keys and buckets vectors are the same. // The encbuckets vector only gets updated when encrypt_table is called. impl BridgeTable { /// Append a new bucket to the bridge table pub fn new_bucket(&mut self, bucket: [BridgeLine; MAX_BRIDGES_PER_BUCKET]) { // Pick a random key to encrypt this bucket let mut rng = rand::thread_rng(); let mut key: [u8; 16] = [0; 16]; rng.fill_bytes(&mut key); self.keys.push(key); self.buckets.push(bucket); } /// Create the vector of encrypted buckets from the keys and buckets /// in the BridgeTable. All of the entries will be (randomly) /// re-encrypted, so it will be hidden whether any individual bucket /// has changed (except for entirely new buckets, of course). pub fn encrypt_table(&mut self) { let mut rng = rand::thread_rng(); self.encbuckets.clear(); for (key, bucket) in self.keys.iter().zip(self.buckets.iter()) { let mut encbucket: [u8; ENC_BUCKET_BYTES] = [0; ENC_BUCKET_BYTES]; let plainbucket: [u8; BUCKET_BYTES] = BridgeLine::bucket_encode(bucket); // Set the AES key let aeskey = GenericArray::from_slice(key); // Pick a random nonce let mut noncebytes: [u8; 12] = [0; 12]; rng.fill_bytes(&mut noncebytes); let nonce = GenericArray::from_slice(&noncebytes); // Encrypt let cipher = Aes128Gcm::new(aeskey); let ciphertext: Vec = cipher.encrypt(&nonce, plainbucket.as_ref()).unwrap(); encbucket[0..12].copy_from_slice(&noncebytes); encbucket[12..].copy_from_slice(ciphertext.as_slice()); self.encbuckets.push(encbucket); } } /// Decrypt an individual encrypted bucket, given its key pub fn decrypt_bucket( key: &[u8; 16], encbucket: &[u8; ENC_BUCKET_BYTES], ) -> Result<[BridgeLine; MAX_BRIDGES_PER_BUCKET], aead::Error> { // Set the nonce and the key let nonce = GenericArray::from_slice(&encbucket[0..12]); let aeskey = GenericArray::from_slice(key); // Decrypt let cipher = Aes128Gcm::new(aeskey); let plaintext: Vec = cipher.decrypt(&nonce, encbucket[12..].as_ref())?; // Convert the plaintext bytes to an array of BridgeLines Ok(BridgeLine::bucket_decode( plaintext.as_slice().try_into().unwrap(), )) } }