bridge_table.rs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. /*! The encrypted table of bridges.
  2. The table consists of a number of buckets, each holding some number
  3. (currently up to 3) of bridges. Each bucket is individually encrypted
  4. with a bucket key. Users will have a credential containing a bucket
  5. (number, key) combination, and so will be able to read one of the
  6. buckets. Users will either download the whole encrypted bucket list or
  7. use PIR to download a piece of it, so that the bridge authority does not
  8. learn which bucket the user has access to. */
  9. use super::lox_creds::BucketReachability;
  10. use super::{Scalar, G};
  11. use aes_gcm::aead;
  12. use aes_gcm::aead::{generic_array::GenericArray, Aead};
  13. use aes_gcm::{Aes128Gcm, KeyInit};
  14. #[cfg(feature = "bridgeauth")]
  15. #[allow(unused_imports)]
  16. use base64::{engine::general_purpose, Engine as _};
  17. use cmz::{CMZCredential, CMZPrivkey, CMZPubkey};
  18. use curve25519_dalek::ristretto::CompressedRistretto;
  19. #[allow(unused_imports)]
  20. use rand::RngCore;
  21. use serde::{Deserialize, Serialize};
  22. use serde_with::{serde_as, DisplayFromStr};
  23. use sha1::{Digest, Sha1};
  24. use std::collections::{HashMap, HashSet};
  25. use subtle::ConstantTimeEq;
  26. /// Each bridge information line is serialized into this many bytes
  27. pub const BRIDGE_BYTES: usize = 250;
  28. /// The bridge info field is this many bytes long
  29. pub const BRIDGE_INFO_BYTES: usize = BRIDGE_BYTES - 46;
  30. /// The max number of bridges per bucket
  31. pub const MAX_BRIDGES_PER_BUCKET: usize = 3;
  32. /// The minimum number of bridges in a bucket that must be reachable for
  33. /// the bucket to get a Bucket Reachability credential that will allow
  34. /// users of that bucket to gain trust levels (once they are already at
  35. /// level 1)
  36. pub const MIN_BUCKET_REACHABILITY: usize = 2;
  37. /// A bridge information line
  38. #[serde_as]
  39. #[derive(Serialize, Deserialize, Copy, Clone, Hash, Eq, PartialEq, Debug)]
  40. pub struct BridgeLine {
  41. /// IPv4 or IPv6 address
  42. pub addr: [u8; 16],
  43. /// port
  44. pub port: u16,
  45. /// fingerprint
  46. #[serde_as(as = "DisplayFromStr")]
  47. pub uid_fingerprint: u64,
  48. /// unhashed fingerprint (20-byte bridge ID)
  49. pub unhashed_fingerprint: [u8; 20], // may be changed to a string later
  50. /// other protocol information, including pluggable transport,
  51. /// public key, etc.
  52. #[serde_as(as = "[_; BRIDGE_INFO_BYTES]")]
  53. pub info: [u8; BRIDGE_INFO_BYTES],
  54. }
  55. impl BridgeLine {
  56. pub fn get_hashed_fingerprint(&self) -> [u8; 20] {
  57. let mut hasher = Sha1::new();
  58. hasher.update(self.unhashed_fingerprint);
  59. // If the fingerprint gets changed to a string:
  60. //hasher.update(array_bytes::hex2array(&self.fingerprint).unwrap());
  61. hasher.finalize().into()
  62. }
  63. }
  64. /// A bucket contains MAX_BRIDGES_PER_BUCKET bridges plus the
  65. /// information needed to construct a Bucket Reachability credential,
  66. /// which is a 4-byte date, and a (P,Q) MAC
  67. type Bucket = (
  68. [BridgeLine; MAX_BRIDGES_PER_BUCKET],
  69. Option<BucketReachability>,
  70. );
  71. /// The size of a plaintext bucket
  72. pub const BUCKET_BYTES: usize = BRIDGE_BYTES * MAX_BRIDGES_PER_BUCKET + 4 + 32 + 32;
  73. /// The size of an encrypted bucket
  74. pub const ENC_BUCKET_BYTES: usize = BUCKET_BYTES + 12 + 16;
  75. impl Default for BridgeLine {
  76. /// An "empty" BridgeLine is represented by all zeros
  77. fn default() -> Self {
  78. Self {
  79. addr: [0; 16],
  80. port: 0,
  81. uid_fingerprint: 0,
  82. unhashed_fingerprint: [0; 20],
  83. info: [0; BRIDGE_INFO_BYTES],
  84. }
  85. }
  86. }
  87. impl BridgeLine {
  88. /// Encode a BridgeLine to a byte array
  89. pub fn encode(&self) -> [u8; BRIDGE_BYTES] {
  90. let mut res: [u8; BRIDGE_BYTES] = [0; BRIDGE_BYTES];
  91. res[0..16].copy_from_slice(&self.addr);
  92. res[16..18].copy_from_slice(&self.port.to_be_bytes());
  93. res[18..26].copy_from_slice(&self.uid_fingerprint.to_be_bytes());
  94. res[26..46].copy_from_slice(&self.unhashed_fingerprint);
  95. res[46..].copy_from_slice(&self.info);
  96. res
  97. }
  98. /// Decode a BridgeLine from a byte array
  99. pub fn decode(data: &[u8; BRIDGE_BYTES]) -> Self {
  100. let mut res: Self = Default::default();
  101. res.addr.copy_from_slice(&data[0..16]);
  102. res.port = u16::from_be_bytes(data[16..18].try_into().unwrap());
  103. res.uid_fingerprint = u64::from_be_bytes(data[18..26].try_into().unwrap());
  104. res.unhashed_fingerprint.copy_from_slice(&data[26..46]);
  105. res.info.copy_from_slice(&data[46..]);
  106. res
  107. }
  108. /// Encode a bucket to a byte array, including a Bucket Reachability
  109. /// credential if appropriate
  110. pub fn bucket_encode(
  111. bucket: &[BridgeLine; MAX_BRIDGES_PER_BUCKET],
  112. reachable: &HashMap<BridgeLine, Vec<(u32, usize)>>,
  113. today: u32,
  114. bucket_attr: &Scalar,
  115. reachability_priv: &CMZPrivkey<G>,
  116. ) -> [u8; BUCKET_BYTES] {
  117. let mut res: [u8; BUCKET_BYTES] = [0; BUCKET_BYTES];
  118. let mut pos: usize = 0;
  119. let mut num_reachable: usize = 0;
  120. for bridge in bucket {
  121. res[pos..pos + BRIDGE_BYTES].copy_from_slice(&bridge.encode());
  122. if reachable.contains_key(bridge) {
  123. num_reachable += 1;
  124. }
  125. pos += BRIDGE_BYTES;
  126. }
  127. if num_reachable >= MIN_BUCKET_REACHABILITY {
  128. // Construct a Bucket Reachability credential for this
  129. // bucket and today's date
  130. let today_attr: Scalar = today.into();
  131. //let mut rng = rand::rngs::OsRng;
  132. let mut B = BucketReachability::using_privkey(reachability_priv);
  133. B.date = Some(today_attr);
  134. B.bucket = Some(*bucket_attr);
  135. //let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
  136. // let b = Scalar::random(&mut rng);
  137. // let P = &b * Btable;
  138. // let Q = &(b
  139. // * (reachability_priv.x[0]
  140. // + reachability_priv.x[1] * today_attr
  141. // + reachability_priv.x[2] * bucket_attr))
  142. // * Btable;
  143. res[pos..pos + 4].copy_from_slice(&today.to_le_bytes());
  144. res[pos + 4..pos + 36].copy_from_slice(B.MAC.P.compress().as_bytes());
  145. res[pos + 36..].copy_from_slice(B.MAC.Q.compress().as_bytes());
  146. }
  147. res
  148. }
  149. /// Decode a bucket from a byte array, yielding the array of
  150. /// BridgeLine entries and an optional Bucket Reachability
  151. /// credential
  152. fn bucket_decode(
  153. data: &[u8; BUCKET_BYTES],
  154. bucket_attr: &Scalar,
  155. reachability_pub: &CMZPubkey<G>,
  156. ) -> Bucket {
  157. let mut pos: usize = 0;
  158. let mut bridges: [BridgeLine; MAX_BRIDGES_PER_BUCKET] = Default::default();
  159. for bridge in bridges.iter_mut().take(MAX_BRIDGES_PER_BUCKET) {
  160. *bridge = BridgeLine::decode(data[pos..pos + BRIDGE_BYTES].try_into().unwrap());
  161. pos += BRIDGE_BYTES;
  162. }
  163. // See if there's a nonzero date in the Bucket Reachability
  164. // Credential
  165. let date = u32::from_le_bytes(data[pos..pos + 4].try_into().unwrap());
  166. let (optP, optQ) = if date > 0 {
  167. (
  168. CompressedRistretto::from_slice(&data[pos + 4..pos + 36])
  169. .expect("Unable to extract P from bucket")
  170. .decompress(),
  171. CompressedRistretto::from_slice(&data[pos + 36..])
  172. .expect("Unable to extract Q from bucket")
  173. .decompress(),
  174. )
  175. } else {
  176. (None, None)
  177. };
  178. if let (Some(P), Some(Q)) = (optP, optQ) {
  179. let date_attr: Scalar = date.into();
  180. let mut B = BucketReachability::using_pubkey(reachability_pub);
  181. B.date = Some(date_attr);
  182. B.bucket = Some(*bucket_attr);
  183. B.MAC.P = P;
  184. B.MAC.Q = Q;
  185. (bridges, Some(B))
  186. } else {
  187. (bridges, None)
  188. }
  189. }
  190. }
  191. #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
  192. #[serde(try_from = "Vec<u8>", into = "Vec<u8>")]
  193. pub struct EncryptedBucket([u8; ENC_BUCKET_BYTES]);
  194. impl From<EncryptedBucket> for Vec<u8> {
  195. fn from(e: EncryptedBucket) -> Vec<u8> {
  196. e.0.into()
  197. }
  198. }
  199. #[derive(thiserror::Error, Debug)]
  200. #[error("wrong slice length")]
  201. pub struct WrongSliceLengthError;
  202. impl TryFrom<Vec<u8>> for EncryptedBucket {
  203. type Error = WrongSliceLengthError;
  204. fn try_from(v: Vec<u8>) -> Result<EncryptedBucket, Self::Error> {
  205. Ok(EncryptedBucket(
  206. *Box::<[u8; ENC_BUCKET_BYTES]>::try_from(v).map_err(|_| WrongSliceLengthError)?,
  207. ))
  208. }
  209. }
  210. #[derive(Debug, Serialize, Deserialize)]
  211. struct K {
  212. encbucket: EncryptedBucket,
  213. }
  214. /// A BridgeTable is the internal structure holding the buckets
  215. /// containing the bridges, the keys used to encrypt the buckets, and
  216. /// the encrypted buckets. The encrypted buckets will be exposed to the
  217. /// users of the system, and each user credential will contain the
  218. /// decryption key for one bucket.
  219. #[serde_as]
  220. #[derive(Debug, Default, Serialize, Deserialize)]
  221. pub struct BridgeTable {
  222. /// All structures in the bridgetable are indexed by counter
  223. pub counter: u32,
  224. /// The keys of all buckets, indexed by counter, that are still part of the bridge table.
  225. pub keys: HashMap<u32, [u8; 16]>,
  226. /// All buckets, indexed by counter corresponding to the key above, that are
  227. /// part of the bridge table.
  228. pub buckets: HashMap<u32, [BridgeLine; MAX_BRIDGES_PER_BUCKET]>,
  229. pub encbuckets: HashMap<u32, EncryptedBucket>,
  230. /// Individual bridges that are reachable.
  231. #[serde_as(as = "HashMap<serde_with::json::JsonString, _>")]
  232. pub reachable: HashMap<BridgeLine, Vec<(u32, usize)>>,
  233. /// Bucket ids of "hot spare" buckets. These buckets are not handed
  234. /// to users, nor do they have any Migration credentials pointing to
  235. /// them. When a new Migration credential is needed, a bucket is
  236. /// removed from this set and used for that purpose.
  237. pub spares: HashSet<u32>,
  238. /// In some instances a single bridge may need to be added to a bucket as a replacement
  239. /// or otherwise. In that case, a spare bucket will be removed from the set of spares, one
  240. /// bridge will be used as the replacement and the left over bridges will be appended to
  241. /// unallocated_bridges.
  242. pub unallocated_bridges: Vec<BridgeLine>,
  243. // To prevent issues with the counter for the hashmap keys, keep a list of keys that
  244. // no longer match any buckets that can be used before increasing the counter.
  245. pub recycleable_keys: Vec<u32>,
  246. // A list of keys that have been blocked (bucket_id: u32), as well as the
  247. // time (julian_date: u32) of their blocking so that they can be repurposed with new
  248. // buckets after the EXPIRY_DATE.
  249. pub blocked_keys: Vec<(u32, u32)>,
  250. // Similarly, a list of open entry buckets (bucket_id: u32) and the time they were
  251. // created (julian_date: u32) so they will be listed as expired after the EXPIRY_DATE.
  252. // TODO: add open entry buckets to the open_inv_keys only once they have been distributed
  253. pub open_inv_keys: Vec<(u32, u32)>,
  254. /// The date the buckets were last encrypted to make the encbucket.
  255. /// The encbucket must be rebuilt at least each day so that the Bucket
  256. /// Reachability credentials in the buckets can be refreshed.
  257. pub date_last_enc: u32,
  258. }
  259. // Invariant: the lengths of the keys and bucket hashmap are the same.
  260. // The encbuckets hashmap only gets updated when encrypt_table is called.
  261. impl BridgeTable {
  262. /// Get the number of buckets in the bridge table
  263. #[cfg(any(feature = "bridgeauth", test))]
  264. pub fn num_buckets(&self) -> usize {
  265. self.buckets.len()
  266. }
  267. /// Insert a new bucket into the bridge table, returning its index
  268. #[cfg(any(feature = "bridgeauth", test))]
  269. pub fn new_bucket(&mut self, index: u32, bucket: &[BridgeLine; MAX_BRIDGES_PER_BUCKET]) {
  270. // Pick a random key to encrypt this bucket
  271. let mut rng = rand::rngs::OsRng;
  272. let mut key: [u8; 16] = [0; 16];
  273. rng.fill_bytes(&mut key);
  274. self.keys.insert(index, key);
  275. self.buckets.insert(index, *bucket);
  276. // TODO: maybe we don't need this if the hashtable can keep track of available bridges
  277. // Mark the new bridges as available
  278. for (i, b) in bucket.iter().enumerate() {
  279. if b.port > 0 {
  280. if let Some(v) = self.reachable.get_mut(b) {
  281. v.push((index, i));
  282. } else {
  283. let v = vec![(index, i)];
  284. self.reachable.insert(*b, v);
  285. }
  286. }
  287. }
  288. }
  289. /// Create the vector of encrypted buckets from the keys and buckets
  290. /// in the BridgeTable. All of the entries will be (randomly)
  291. /// re-encrypted, so it will be hidden whether any individual bucket
  292. /// has changed (except for entirely new buckets, of course).
  293. /// Bucket Reachability credentials are added to the buckets when
  294. /// enough (at least MIN_BUCKET_REACHABILITY) bridges in the bucket
  295. /// are reachable.
  296. #[cfg(any(feature = "bridgeauth", test))]
  297. pub fn encrypt_table(&mut self, today: u32, reachability_priv: &CMZPrivkey<G>) {
  298. let mut rng = rand::rngs::OsRng;
  299. self.encbuckets.clear();
  300. for (uid, key) in self.keys.iter() {
  301. let bucket = self.buckets.get(uid).unwrap();
  302. let mut encbucket: [u8; ENC_BUCKET_BYTES] = [0; ENC_BUCKET_BYTES];
  303. let plainbucket: [u8; BUCKET_BYTES] = BridgeLine::bucket_encode(
  304. bucket,
  305. &self.reachable,
  306. today,
  307. &to_scalar(*uid, key),
  308. reachability_priv,
  309. );
  310. // Set the AES key
  311. let aeskey = GenericArray::from_slice(key);
  312. // Pick a random nonce
  313. let mut noncebytes: [u8; 12] = [0; 12];
  314. rng.fill_bytes(&mut noncebytes);
  315. let nonce = GenericArray::from_slice(&noncebytes);
  316. // Encrypt
  317. let cipher = Aes128Gcm::new(aeskey);
  318. let ciphertext: Vec<u8> = cipher.encrypt(nonce, plainbucket.as_ref()).unwrap();
  319. encbucket[0..12].copy_from_slice(&noncebytes);
  320. encbucket[12..].copy_from_slice(ciphertext.as_slice());
  321. let k = EncryptedBucket(encbucket);
  322. self.encbuckets.insert(*uid, k);
  323. }
  324. self.date_last_enc = today;
  325. }
  326. /// Decrypt an individual encrypted bucket, given its id, key, and
  327. /// the encrypted bucket itself
  328. pub fn decrypt_bucket(
  329. id: u32,
  330. key: &[u8; 16],
  331. encbucket: &EncryptedBucket,
  332. reachability_pub: &CMZPubkey<G>,
  333. ) -> Result<Bucket, aead::Error> {
  334. // Set the nonce and the key
  335. let k = K {
  336. encbucket: *encbucket,
  337. };
  338. let nonce = GenericArray::from_slice(&k.encbucket.0[0..12]);
  339. let aeskey = GenericArray::from_slice(key);
  340. // Decrypt
  341. let cipher = Aes128Gcm::new(aeskey);
  342. let plaintext: Vec<u8> = cipher.decrypt(nonce, k.encbucket.0[12..].as_ref())?;
  343. // Convert the plaintext bytes to an array of BridgeLines
  344. Ok(BridgeLine::bucket_decode(
  345. plaintext.as_slice().try_into().unwrap(),
  346. &to_scalar(id, key),
  347. reachability_pub,
  348. ))
  349. }
  350. /// Decrypt an individual encrypted bucket, given its id and key
  351. #[cfg(any(feature = "bridgeauth", test))]
  352. pub fn decrypt_bucket_id(
  353. &self,
  354. id: u32,
  355. key: &[u8; 16],
  356. reachability_pub: &CMZPubkey<G>,
  357. ) -> Result<Bucket, aead::Error> {
  358. let encbucket: &EncryptedBucket = match self.encbuckets.get(&id) {
  359. Some(encbucket) => encbucket,
  360. None => panic!("Provided ID not found"),
  361. };
  362. BridgeTable::decrypt_bucket(id, key, encbucket, reachability_pub)
  363. }
  364. }
  365. // Unit tests that require access to the testing-only function
  366. // BridgeLine::random()
  367. #[cfg(all(test, feature = "bridgeauth"))]
  368. mod tests {
  369. use super::*;
  370. use crate::mock_auth::random;
  371. use cmz::cmz_group_init;
  372. use sha2::Sha512;
  373. #[test]
  374. fn test_bridge_table() -> Result<(), aead::Error> {
  375. // Create private keys for the Bucket Reachability credentials
  376. let mut rng = rand::thread_rng();
  377. cmz_group_init(G::hash_from_bytes::<Sha512>(b"CMZ Generator A"));
  378. let (reachability_priv, reachability_pub) = BucketReachability::gen_keys(&mut rng, true);
  379. // Create an empty bridge table
  380. let mut btable: BridgeTable = Default::default();
  381. // Make 20 buckets with one random bridge each
  382. for _ in 0..20 {
  383. let bucket: [BridgeLine; 3] = [random(), Default::default(), Default::default()];
  384. btable.counter += 1;
  385. btable.new_bucket(btable.counter, &bucket);
  386. }
  387. // And 20 more with three random bridges each
  388. for _ in 0..20 {
  389. let bucket: [BridgeLine; 3] = [random(), random(), random()];
  390. btable.counter += 1;
  391. btable.new_bucket(btable.counter, &bucket);
  392. }
  393. let today: u32 = time::OffsetDateTime::now_utc()
  394. .date()
  395. .to_julian_day()
  396. .try_into()
  397. .unwrap();
  398. // Create the encrypted bridge table
  399. btable.encrypt_table(today, &reachability_priv);
  400. // Try to decrypt a 1-bridge bucket
  401. let key7 = btable.keys.get(&7u32).unwrap();
  402. let bucket7 = btable.decrypt_bucket_id(7, key7, &reachability_pub)?;
  403. println!("bucket 7 = {:?}", bucket7);
  404. // Try to decrypt a 3-bridge bucket
  405. let key24 = btable.keys.get(&24u32).unwrap();
  406. let bucket24 = btable.decrypt_bucket_id(24, key24, &reachability_pub)?;
  407. println!("bucket 24 = {:?}", bucket24);
  408. // Try to decrypt a bucket with the wrong key
  409. let key12 = btable.keys.get(&12u32).unwrap();
  410. let res = btable
  411. .decrypt_bucket_id(15, key12, &reachability_pub)
  412. .unwrap_err();
  413. println!("bucket key mismatch = {:?}", res);
  414. Ok(())
  415. }
  416. }
  417. /// Convert an id and key to a Scalar attribute
  418. pub fn to_scalar(id: u32, key: &[u8; 16]) -> Scalar {
  419. let mut b: [u8; 32] = [0; 32];
  420. // b is a little-endian representation of the Scalar; put the key in
  421. // the low 16 bytes, and the id in the next 4 bytes.
  422. b[0..16].copy_from_slice(key);
  423. b[16..20].copy_from_slice(&id.to_le_bytes());
  424. // This cannot fail, since we're only using the low 20 bytes of b
  425. Scalar::from_canonical_bytes(b).unwrap()
  426. }
  427. /// Convert a Scalar attribute to an id and key if possible
  428. pub fn from_scalar(s: Scalar) -> Result<(u32, [u8; 16]), aead::Error> {
  429. // Check that the top 12 bytes of the Scalar are 0
  430. let sbytes = s.as_bytes();
  431. if sbytes[20..].ct_eq(&[0u8; 12]).unwrap_u8() == 0 {
  432. return Err(aead::Error);
  433. }
  434. let id = u32::from_le_bytes(sbytes[16..20].try_into().unwrap());
  435. let mut key: [u8; 16] = [0; 16];
  436. key.copy_from_slice(&sbytes[..16]);
  437. Ok((id, key))
  438. }