2021-04-29 14:58:51 -04:00
|
|
|
/*! 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. */
|
2021-04-27 13:00:18 -04:00
|
|
|
|
2021-05-01 17:12:03 -04:00
|
|
|
use super::cred;
|
|
|
|
use super::IssuerPrivKey;
|
|
|
|
use super::CMZ_B_TABLE;
|
2021-04-27 13:00:18 -04:00
|
|
|
use aes_gcm::aead;
|
|
|
|
use aes_gcm::aead::{generic_array::GenericArray, Aead, NewAead};
|
|
|
|
use aes_gcm::Aes128Gcm;
|
2021-05-01 17:12:03 -04:00
|
|
|
use curve25519_dalek::ristretto::CompressedRistretto;
|
|
|
|
use curve25519_dalek::ristretto::RistrettoBasepointTable;
|
2021-04-28 18:31:47 -04:00
|
|
|
use curve25519_dalek::scalar::Scalar;
|
2021-04-27 13:00:18 -04:00
|
|
|
use rand::RngCore;
|
2021-05-26 18:15:13 -04:00
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use serde_with::serde_as;
|
2021-05-05 13:58:43 -04:00
|
|
|
use std::collections::{HashMap, HashSet};
|
2021-04-27 13:00:18 -04:00
|
|
|
use std::convert::TryInto;
|
2021-04-29 11:28:05 -04:00
|
|
|
use subtle::ConstantTimeEq;
|
2021-04-27 13:00:18 -04:00
|
|
|
|
|
|
|
/// Each bridge information line is serialized into this many bytes
|
2023-04-03 11:47:11 -04:00
|
|
|
pub const BRIDGE_BYTES: usize = 260;
|
2021-04-27 13:00:18 -04:00
|
|
|
|
|
|
|
/// The max number of bridges per bucket
|
|
|
|
pub const MAX_BRIDGES_PER_BUCKET: usize = 3;
|
|
|
|
|
2021-05-01 17:12:03 -04:00
|
|
|
/// The minimum number of bridges in a bucket that must be reachable for
|
|
|
|
/// the bucket to get a Bucket Reachability credential that will allow
|
|
|
|
/// users of that bucket to gain trust levels (once they are already at
|
|
|
|
/// level 1)
|
|
|
|
pub const MIN_BUCKET_REACHABILITY: usize = 2;
|
2021-04-27 13:00:18 -04:00
|
|
|
|
|
|
|
/// A bridge information line
|
2021-05-26 18:15:13 -04:00
|
|
|
#[serde_as]
|
2023-03-09 18:18:02 -05:00
|
|
|
#[derive(Serialize, Deserialize, Copy, Clone, Hash, Eq, PartialEq, Debug)]
|
2021-04-27 13:00:18 -04:00
|
|
|
pub struct BridgeLine {
|
|
|
|
/// IPv4 or IPv6 address
|
|
|
|
pub addr: [u8; 16],
|
|
|
|
/// port
|
|
|
|
pub port: u16,
|
2023-04-03 11:47:11 -04:00
|
|
|
/// fingerprint
|
|
|
|
#[serde_as(as = "[_; 40]")]
|
|
|
|
pub fingerprint: [u8; 40],
|
2023-03-09 18:18:02 -05:00
|
|
|
/// other protocol information, including pluggable transport,
|
|
|
|
/// public key, etc.
|
2023-04-03 11:47:11 -04:00
|
|
|
#[serde_as(as = "[_; BRIDGE_BYTES - 58]")]
|
|
|
|
pub info: [u8; BRIDGE_BYTES - 58],
|
2021-04-27 13:00:18 -04:00
|
|
|
}
|
|
|
|
|
2021-05-01 17:12:03 -04:00
|
|
|
/// A bucket contains MAX_BRIDGES_PER_BUCKET bridges plus the
|
|
|
|
/// information needed to construct a Bucket Reachability credential,
|
|
|
|
/// which is a 4-byte date, and a (P,Q) MAC
|
|
|
|
type Bucket = (
|
|
|
|
[BridgeLine; MAX_BRIDGES_PER_BUCKET],
|
|
|
|
Option<cred::BucketReachability>,
|
|
|
|
);
|
|
|
|
|
|
|
|
/// The size of a plaintext bucket
|
|
|
|
pub const BUCKET_BYTES: usize = BRIDGE_BYTES * MAX_BRIDGES_PER_BUCKET + 4 + 32 + 32;
|
|
|
|
|
|
|
|
/// The size of an encrypted bucket
|
|
|
|
pub const ENC_BUCKET_BYTES: usize = BUCKET_BYTES + 12 + 16;
|
|
|
|
|
2021-04-27 13:00:18 -04:00
|
|
|
impl Default for BridgeLine {
|
|
|
|
/// An "empty" BridgeLine is represented by all zeros
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
addr: [0; 16],
|
|
|
|
port: 0,
|
2023-04-03 11:47:11 -04:00
|
|
|
fingerprint: [0; 40],
|
|
|
|
info: [0; BRIDGE_BYTES - 58],
|
2021-04-27 13:00:18 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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());
|
2023-04-03 11:47:11 -04:00
|
|
|
res[18..58].copy_from_slice(&self.fingerprint);
|
|
|
|
res[58..].copy_from_slice(&self.info);
|
2021-04-27 13:00:18 -04:00
|
|
|
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());
|
2023-04-03 11:47:11 -04:00
|
|
|
res.fingerprint.copy_from_slice(&data[18..58]);
|
|
|
|
res.info.copy_from_slice(&data[58..]);
|
2021-04-27 13:00:18 -04:00
|
|
|
res
|
|
|
|
}
|
2021-05-01 17:12:03 -04:00
|
|
|
/// Encode a bucket to a byte array, including a Bucket Reachability
|
|
|
|
/// credential if appropriate
|
|
|
|
pub fn bucket_encode(
|
|
|
|
bucket: &[BridgeLine; MAX_BRIDGES_PER_BUCKET],
|
2021-05-05 13:58:43 -04:00
|
|
|
reachable: &HashMap<BridgeLine, Vec<(u32, usize)>>,
|
2021-05-01 17:12:03 -04:00
|
|
|
today: u32,
|
|
|
|
bucket_attr: &Scalar,
|
|
|
|
reachability_priv: &IssuerPrivKey,
|
|
|
|
) -> [u8; BUCKET_BYTES] {
|
2021-04-27 13:00:18 -04:00
|
|
|
let mut res: [u8; BUCKET_BYTES] = [0; BUCKET_BYTES];
|
|
|
|
let mut pos: usize = 0;
|
2021-05-01 17:12:03 -04:00
|
|
|
let mut num_reachable: usize = 0;
|
2021-04-27 13:00:18 -04:00
|
|
|
for bridge in bucket {
|
|
|
|
res[pos..pos + BRIDGE_BYTES].copy_from_slice(&bridge.encode());
|
2021-05-05 13:58:43 -04:00
|
|
|
if reachable.contains_key(bridge) {
|
2021-05-01 17:12:03 -04:00
|
|
|
num_reachable += 1;
|
|
|
|
}
|
2021-04-27 13:00:18 -04:00
|
|
|
pos += BRIDGE_BYTES;
|
|
|
|
}
|
2021-05-01 17:12:03 -04:00
|
|
|
if num_reachable >= MIN_BUCKET_REACHABILITY {
|
|
|
|
// Construct a Bucket Reachability credential for this
|
|
|
|
// bucket and today's date
|
|
|
|
let today_attr: Scalar = today.into();
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
|
|
|
|
let b = Scalar::random(&mut rng);
|
|
|
|
let P = &b * Btable;
|
|
|
|
let Q = &(b
|
|
|
|
* (reachability_priv.x[0]
|
|
|
|
+ reachability_priv.x[1] * today_attr
|
|
|
|
+ reachability_priv.x[2] * bucket_attr))
|
|
|
|
* Btable;
|
|
|
|
res[pos..pos + 4].copy_from_slice(&today.to_le_bytes());
|
2021-05-03 11:44:42 -04:00
|
|
|
res[pos + 4..pos + 36].copy_from_slice(P.compress().as_bytes());
|
|
|
|
res[pos + 36..].copy_from_slice(Q.compress().as_bytes());
|
2021-05-01 17:12:03 -04:00
|
|
|
}
|
2021-04-27 13:00:18 -04:00
|
|
|
res
|
|
|
|
}
|
2021-05-01 17:12:03 -04:00
|
|
|
/// Decode a bucket from a byte array, yielding the array of
|
|
|
|
/// BridgeLine entries and an optional Bucket Reachability
|
|
|
|
/// credential
|
|
|
|
fn bucket_decode(data: &[u8; BUCKET_BYTES], bucket_attr: &Scalar) -> Bucket {
|
2021-04-27 13:00:18 -04:00
|
|
|
let mut pos: usize = 0;
|
2021-05-01 17:12:03 -04:00
|
|
|
let mut bridges: [BridgeLine; MAX_BRIDGES_PER_BUCKET] = Default::default();
|
|
|
|
for bridge in bridges.iter_mut().take(MAX_BRIDGES_PER_BUCKET) {
|
2021-04-27 13:00:18 -04:00
|
|
|
*bridge = BridgeLine::decode(data[pos..pos + BRIDGE_BYTES].try_into().unwrap());
|
|
|
|
pos += BRIDGE_BYTES;
|
|
|
|
}
|
2021-05-01 17:12:03 -04:00
|
|
|
// See if there's a nonzero date in the Bucket Reachability
|
|
|
|
// Credential
|
|
|
|
let date = u32::from_le_bytes(data[pos..pos + 4].try_into().unwrap());
|
|
|
|
let (optP, optQ) = if date > 0 {
|
|
|
|
(
|
|
|
|
CompressedRistretto::from_slice(&data[pos + 4..pos + 36]).decompress(),
|
|
|
|
CompressedRistretto::from_slice(&data[pos + 36..]).decompress(),
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
(None, None)
|
|
|
|
};
|
|
|
|
if let (Some(P), Some(Q)) = (optP, optQ) {
|
|
|
|
let date_attr: Scalar = date.into();
|
|
|
|
(
|
|
|
|
bridges,
|
|
|
|
Some(cred::BucketReachability {
|
|
|
|
P,
|
|
|
|
Q,
|
|
|
|
date: date_attr,
|
|
|
|
bucket: *bucket_attr,
|
|
|
|
}),
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
(bridges, None)
|
|
|
|
}
|
2021-04-27 13:00:18 -04:00
|
|
|
}
|
2021-04-27 18:26:49 -04:00
|
|
|
/// 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];
|
2023-04-03 11:47:11 -04:00
|
|
|
let mut fingerprint: [u8; 40] = [0; 40];
|
|
|
|
rng.fill_bytes(&mut fingerprint);
|
2021-04-27 18:26:49 -04:00
|
|
|
let mut cert: [u8; 52] = [0; 52];
|
|
|
|
rng.fill_bytes(&mut fingerprint);
|
2023-04-03 11:47:11 -04:00
|
|
|
res.fingerprint[0..40].copy_from_slice(&fingerprint);
|
2021-04-27 18:26:49 -04:00
|
|
|
rng.fill_bytes(&mut cert);
|
|
|
|
let infostr: String = format!(
|
2023-04-03 11:47:11 -04:00
|
|
|
"obfs4 cert={}, iat-mode=0",
|
2021-04-27 18:26:49 -04:00
|
|
|
base64::encode_config(cert, base64::STANDARD_NO_PAD)
|
|
|
|
);
|
|
|
|
res.info[..infostr.len()].copy_from_slice(infostr.as_bytes());
|
|
|
|
res
|
|
|
|
}
|
2021-04-27 13:00:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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.
|
2022-11-22 19:15:09 -05:00
|
|
|
#[serde_as]
|
|
|
|
#[derive(Debug, Default, Serialize, Deserialize)]
|
2021-04-27 13:00:18 -04:00
|
|
|
pub struct BridgeTable {
|
2021-04-28 18:31:47 -04:00
|
|
|
pub keys: Vec<[u8; 16]>,
|
|
|
|
pub buckets: Vec<[BridgeLine; MAX_BRIDGES_PER_BUCKET]>,
|
2022-11-22 19:15:09 -05:00
|
|
|
#[serde_as(as = "Vec<[_; ENC_BUCKET_BYTES]>")]
|
2021-04-28 18:31:47 -04:00
|
|
|
pub encbuckets: Vec<[u8; ENC_BUCKET_BYTES]>,
|
2021-05-05 13:58:43 -04:00
|
|
|
/// Individual bridges that are reachable
|
|
|
|
pub reachable: HashMap<BridgeLine, Vec<(u32, usize)>>,
|
|
|
|
/// bucket ids of "hot spare" buckets. These buckets are not handed
|
|
|
|
/// to users, nor do they have any Migration credentials pointing to
|
|
|
|
/// them. When a new Migration credential is needed, a bucket is
|
|
|
|
/// removed from this set and used for that purpose.
|
|
|
|
pub spares: HashSet<u32>,
|
2021-05-01 17:12:03 -04:00
|
|
|
/// The date the buckets were last encrypted to make the encbucket.
|
|
|
|
///
|
|
|
|
/// The encbucket must be rebuilt each day so that the Bucket
|
|
|
|
/// Reachability credentials in the buckets can be refreshed.
|
|
|
|
pub date_last_enc: u32,
|
2021-04-27 13:00:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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 {
|
2021-04-28 18:31:47 -04:00
|
|
|
/// Get the number of buckets in the bridge table
|
|
|
|
pub fn num_buckets(&self) -> usize {
|
|
|
|
self.buckets.len()
|
|
|
|
}
|
|
|
|
|
2021-05-05 13:58:43 -04:00
|
|
|
/// Append a new bucket to the bridge table, returning its index
|
|
|
|
pub fn new_bucket(&mut self, bucket: &[BridgeLine; MAX_BRIDGES_PER_BUCKET]) -> u32 {
|
2021-04-27 13:00:18 -04:00
|
|
|
// 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);
|
2021-05-05 13:58:43 -04:00
|
|
|
self.buckets.push(*bucket);
|
|
|
|
let bucketnum: u32 = (self.buckets.len() - 1).try_into().unwrap();
|
2021-05-01 17:12:03 -04:00
|
|
|
// Mark the new bridges as available
|
2021-05-05 13:58:43 -04:00
|
|
|
for (i, b) in bucket.iter().enumerate() {
|
2021-05-01 17:12:03 -04:00
|
|
|
if b.port > 0 {
|
2021-05-05 13:58:43 -04:00
|
|
|
if let Some(v) = self.reachable.get_mut(b) {
|
|
|
|
v.push((bucketnum, i));
|
|
|
|
} else {
|
|
|
|
let v = vec![(bucketnum, i)];
|
|
|
|
self.reachable.insert(*b, v);
|
|
|
|
}
|
2021-05-01 17:12:03 -04:00
|
|
|
}
|
|
|
|
}
|
2021-05-05 13:58:43 -04:00
|
|
|
bucketnum
|
2021-04-27 13:00:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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).
|
2021-05-01 17:12:03 -04:00
|
|
|
/// Bucket Reachability credentials are added to the buckets when
|
|
|
|
/// enough (at least MIN_BUCKET_REACHABILITY) bridges in the bucket
|
|
|
|
/// are reachable.
|
|
|
|
pub fn encrypt_table(&mut self, today: u32, reachability_priv: &IssuerPrivKey) {
|
2021-04-27 13:00:18 -04:00
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
self.encbuckets.clear();
|
2021-05-01 17:12:03 -04:00
|
|
|
// We want id to be a u32, so we use .zip(0u32..) instead of
|
|
|
|
// enumerate()
|
|
|
|
for ((key, bucket), id) in self.keys.iter().zip(self.buckets.iter()).zip(0u32..) {
|
2021-04-27 13:00:18 -04:00
|
|
|
let mut encbucket: [u8; ENC_BUCKET_BYTES] = [0; ENC_BUCKET_BYTES];
|
2021-05-01 17:12:03 -04:00
|
|
|
let plainbucket: [u8; BUCKET_BYTES] = BridgeLine::bucket_encode(
|
|
|
|
bucket,
|
|
|
|
&self.reachable,
|
|
|
|
today,
|
|
|
|
&to_scalar(id, key),
|
|
|
|
reachability_priv,
|
|
|
|
);
|
2021-04-27 13:00:18 -04:00
|
|
|
// 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);
|
2022-10-18 00:51:17 -04:00
|
|
|
let ciphertext: Vec<u8> = cipher.encrypt(nonce, plainbucket.as_ref()).unwrap();
|
2021-04-27 13:00:18 -04:00
|
|
|
encbucket[0..12].copy_from_slice(&noncebytes);
|
|
|
|
encbucket[12..].copy_from_slice(ciphertext.as_slice());
|
|
|
|
self.encbuckets.push(encbucket);
|
|
|
|
}
|
2021-05-01 17:12:03 -04:00
|
|
|
self.date_last_enc = today;
|
2021-04-27 13:00:18 -04:00
|
|
|
}
|
|
|
|
|
2021-05-01 17:12:03 -04:00
|
|
|
/// Decrypt an individual encrypted bucket, given its id, key, and
|
|
|
|
/// the encrypted bucket itself
|
2021-04-27 13:00:18 -04:00
|
|
|
pub fn decrypt_bucket(
|
2021-05-01 17:12:03 -04:00
|
|
|
id: u32,
|
2021-04-27 13:00:18 -04:00
|
|
|
key: &[u8; 16],
|
|
|
|
encbucket: &[u8; ENC_BUCKET_BYTES],
|
2021-05-01 17:12:03 -04:00
|
|
|
) -> Result<Bucket, aead::Error> {
|
2021-04-27 13:00:18 -04:00
|
|
|
// 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);
|
2022-10-18 00:51:17 -04:00
|
|
|
let plaintext: Vec<u8> = cipher.decrypt(nonce, encbucket[12..].as_ref())?;
|
2021-04-27 13:00:18 -04:00
|
|
|
// Convert the plaintext bytes to an array of BridgeLines
|
|
|
|
Ok(BridgeLine::bucket_decode(
|
|
|
|
plaintext.as_slice().try_into().unwrap(),
|
2021-05-01 17:12:03 -04:00
|
|
|
&to_scalar(id, key),
|
2021-04-27 13:00:18 -04:00
|
|
|
))
|
|
|
|
}
|
2021-04-29 11:28:05 -04:00
|
|
|
|
|
|
|
/// Decrypt an individual encrypted bucket, given its id and key
|
2021-05-01 17:12:03 -04:00
|
|
|
pub fn decrypt_bucket_id(&self, id: u32, key: &[u8; 16]) -> Result<Bucket, aead::Error> {
|
2021-04-29 11:28:05 -04:00
|
|
|
let encbucket = self.encbuckets[id as usize];
|
2021-05-01 17:12:03 -04:00
|
|
|
BridgeTable::decrypt_bucket(id, key, &encbucket)
|
2021-04-29 11:28:05 -04:00
|
|
|
}
|
2021-04-27 13:00:18 -04:00
|
|
|
}
|
2021-04-27 18:26:49 -04:00
|
|
|
|
2021-04-28 18:31:47 -04:00
|
|
|
// Unit tests that require access to the testing-only function
|
|
|
|
// BridgeLine::random()
|
2021-04-27 18:26:49 -04:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bridge_table() -> Result<(), aead::Error> {
|
2021-05-01 17:12:03 -04:00
|
|
|
// Create private keys for the Bucket Reachability credentials
|
|
|
|
let reachability_priv = IssuerPrivKey::new(2);
|
2021-04-27 18:26:49 -04:00
|
|
|
// 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()];
|
2021-05-05 13:58:43 -04:00
|
|
|
btable.new_bucket(&bucket);
|
2021-04-27 18:26:49 -04:00
|
|
|
}
|
|
|
|
// And 20 more with three random bridges each
|
|
|
|
for _ in 0..20 {
|
|
|
|
let bucket: [BridgeLine; 3] = [
|
|
|
|
BridgeLine::random(),
|
|
|
|
BridgeLine::random(),
|
|
|
|
BridgeLine::random(),
|
|
|
|
];
|
2021-05-05 13:58:43 -04:00
|
|
|
btable.new_bucket(&bucket);
|
2021-04-27 18:26:49 -04:00
|
|
|
}
|
2021-05-01 17:12:03 -04:00
|
|
|
let today: u32 = time::OffsetDateTime::now_utc()
|
|
|
|
.date()
|
|
|
|
.julian_day()
|
|
|
|
.try_into()
|
|
|
|
.unwrap();
|
2021-04-27 18:26:49 -04:00
|
|
|
// Create the encrypted bridge table
|
2021-05-01 17:12:03 -04:00
|
|
|
btable.encrypt_table(today, &reachability_priv);
|
2021-04-27 18:26:49 -04:00
|
|
|
// Try to decrypt a 1-bridge bucket
|
|
|
|
let key7 = btable.keys[7];
|
2021-04-29 11:28:05 -04:00
|
|
|
let bucket7 = btable.decrypt_bucket_id(7, &key7)?;
|
2021-04-27 18:26:49 -04:00
|
|
|
println!("bucket 7 = {:?}", bucket7);
|
|
|
|
// Try to decrypt a 3-bridge bucket
|
|
|
|
let key24 = btable.keys[24];
|
2021-04-29 11:28:05 -04:00
|
|
|
let bucket24 = btable.decrypt_bucket_id(24, &key24)?;
|
2021-04-27 18:26:49 -04:00
|
|
|
println!("bucket 24 = {:?}", bucket24);
|
|
|
|
// Try to decrypt a bucket with the wrong key
|
|
|
|
let key12 = btable.keys[12];
|
2021-04-29 11:28:05 -04:00
|
|
|
let res = btable.decrypt_bucket_id(15, &key12).unwrap_err();
|
2021-04-27 18:26:49 -04:00
|
|
|
println!("bucket key mismatch = {:?}", res);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
2021-04-28 18:31:47 -04:00
|
|
|
|
|
|
|
/// Convert an id and key to a Scalar attribute
|
2021-04-30 11:58:31 -04:00
|
|
|
pub fn to_scalar(id: u32, key: &[u8; 16]) -> Scalar {
|
2021-04-28 18:31:47 -04:00
|
|
|
let mut b: [u8; 32] = [0; 32];
|
|
|
|
// b is a little-endian representation of the Scalar; put the key in
|
|
|
|
// the low 16 bytes, and the id in the next 4 bytes.
|
2021-04-30 11:58:31 -04:00
|
|
|
b[0..16].copy_from_slice(key);
|
2021-04-28 18:31:47 -04:00
|
|
|
b[16..20].copy_from_slice(&id.to_le_bytes());
|
|
|
|
// This cannot fail, since we're only using the low 20 bytes of b
|
|
|
|
Scalar::from_canonical_bytes(b).unwrap()
|
|
|
|
}
|
2021-04-29 11:28:05 -04:00
|
|
|
|
|
|
|
/// Convert a Scalar attribute to an id and key if possible
|
|
|
|
pub fn from_scalar(s: Scalar) -> Result<(u32, [u8; 16]), aead::Error> {
|
|
|
|
// Check that the top 12 bytes of the Scalar are 0
|
|
|
|
let sbytes = s.as_bytes();
|
|
|
|
if sbytes[20..].ct_eq(&[0u8; 12]).unwrap_u8() == 0 {
|
|
|
|
return Err(aead::Error);
|
|
|
|
}
|
|
|
|
let id = u32::from_le_bytes(sbytes[16..20].try_into().unwrap());
|
|
|
|
let mut key: [u8; 16] = [0; 16];
|
|
|
|
key.copy_from_slice(&sbytes[..16]);
|
|
|
|
Ok((id, key))
|
|
|
|
}
|