|
@@ -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<u8> = 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<u8> = 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(),
|
|
|
+ ))
|
|
|
+ }
|
|
|
+}
|