diff --git a/crates/lox-library/Cargo.toml b/crates/lox-library/Cargo.toml index 5d4eab2..388ae60 100644 --- a/crates/lox-library/Cargo.toml +++ b/crates/lox-library/Cargo.toml @@ -14,6 +14,7 @@ serde = "1" sha2 = "0.9" lazy_static = "1" hex_fmt = "0.3" +aes-gcm = "0.8" [features] default = ["u64_backend"] diff --git a/crates/lox-library/src/bridge_table.rs b/crates/lox-library/src/bridge_table.rs new file mode 100644 index 0000000..6116857 --- /dev/null +++ b/crates/lox-library/src/bridge_table.rs @@ -0,0 +1,157 @@ +/*! 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(), + )) + } +} diff --git a/crates/lox-library/src/lib.rs b/crates/lox-library/src/lib.rs index dc17633..639a231 100644 --- a/crates/lox-library/src/lib.rs +++ b/crates/lox-library/src/lib.rs @@ -17,6 +17,7 @@ The notation follows that of the paper "Hyphae: Social Secret Sharing" #[macro_use] extern crate zkp; +pub mod bridge_table; pub mod dup_filter; use sha2::Sha512;