Prechádzať zdrojové kódy

A basic encrypted bridge table

Ian Goldberg 3 rokov pred
rodič
commit
579363c73c
3 zmenil súbory, kde vykonal 159 pridanie a 0 odobranie
  1. 1 0
      Cargo.toml
  2. 157 0
      src/bridge_table.rs
  3. 1 0
      src/lib.rs

+ 1 - 0
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"]

+ 157 - 0
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<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(),
+        ))
+    }
+}

+ 1 - 0
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;