lib.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. /*! Implementation of a new style of bridge authority for Tor that
  2. allows users to invite other users, while protecting the social graph
  3. from the bridge authority itself.
  4. We use uCMZ credentials (Orr`u, 2024 https://eprint.iacr.org/2024/1552.pdf) which improves issuer efficiency
  5. over our original CMZ14 credential (GGM version, which is more efficient, but
  6. makes a stronger security assumption) implementation: "Algebraic MACs and
  7. Keyed-Verification Anonymous Credentials" (Chase, Meiklejohn, and
  8. Zaverucha, CCS 2014)
  9. The notation follows that of the paper "Hyphae: Social Secret Sharing"
  10. (Lovecruft and de Valence, 2017), Section 4. */
  11. // We want Scalars to be lowercase letters, and Points and credentials
  12. // to be capital letters
  13. #![allow(non_snake_case)]
  14. #[cfg(feature = "bridgeauth")]
  15. use ed25519_dalek::{Signature, SignatureError, SigningKey, Verifier, VerifyingKey};
  16. use subtle::ConstantTimeEq;
  17. #[cfg(feature = "bridgeauth")]
  18. use chrono::{DateTime, Utc};
  19. #[cfg(feature = "bridgeauth")]
  20. use cmz::*;
  21. use curve25519_dalek::ristretto::RistrettoPoint as G;
  22. use group::Group;
  23. #[cfg(feature = "bridgeauth")]
  24. use rand::rngs::OsRng;
  25. type Scalar = <G as Group>::Scalar;
  26. #[cfg(feature = "bridgeauth")]
  27. use sha2::Sha512;
  28. pub mod bridge_table;
  29. pub mod dup_filter;
  30. pub mod lox_creds;
  31. pub mod migration_table;
  32. pub mod proto {
  33. pub mod blockage_migration;
  34. pub mod errors;
  35. pub mod issue_invite;
  36. pub mod level_up;
  37. pub mod migration;
  38. pub mod open_invite;
  39. pub mod redeem_invite;
  40. pub mod trust_promotion;
  41. pub mod update_cred;
  42. pub mod update_invite;
  43. }
  44. #[cfg(feature = "bridgeauth")]
  45. use bridge_table::BridgeTable;
  46. // BridgeLine, EncryptedBucket, MAX_BRIDGES_PER_BUCKET, MIN_BUCKET_REACHABILITY,
  47. //};
  48. #[cfg(feature = "bridgeauth")]
  49. use lox_creds::*;
  50. #[cfg(feature = "bridgeauth")]
  51. use migration_table::{MigrationTable, MigrationType};
  52. #[cfg(feature = "bridgeauth")]
  53. use serde::{Deserialize, Serialize};
  54. #[cfg(feature = "bridgeauth")]
  55. use std::collections::HashSet;
  56. /// Number of times a given invitation is ditributed
  57. pub const OPENINV_K: u32 = 10;
  58. /// TODO: Decide on maximum daily number of invitations to be distributed
  59. pub const MAX_DAILY_BRIDGES: u32 = 100;
  60. /// The BridgeDb. This will typically be a singleton object. The
  61. /// BridgeDb's role is simply to issue signed "open invitations" to
  62. /// people who are not yet part of the system.
  63. #[derive(Debug, Serialize, Deserialize)]
  64. #[cfg(feature = "bridgeauth")]
  65. pub struct BridgeDb {
  66. /// The keypair for signing open invitations
  67. keypair: SigningKey,
  68. /// The public key for verifying open invitations
  69. pub pubkey: VerifyingKey,
  70. /// The set of open-invitation buckets
  71. openinv_buckets: HashSet<u32>,
  72. /// The set of open invitation buckets that have been distributed
  73. distributed_buckets: Vec<u32>,
  74. #[serde(skip)]
  75. today: DateTime<Utc>,
  76. pub current_k: u32,
  77. pub daily_bridges_distributed: u32,
  78. }
  79. #[derive(Debug, Clone, Serialize, Deserialize)]
  80. #[cfg(feature = "bridgeauth")]
  81. pub struct OldKeyStore {
  82. // /// Most recently outdated lox secret and private keys for verifying update_cred credentials
  83. priv_key: CMZPrivkey<G>,
  84. // /// The public key for verifying update_cred credentials
  85. pub pub_key: CMZPubkey<G>,
  86. }
  87. #[derive(Debug, Default, Clone, Serialize, Deserialize)]
  88. #[cfg(feature = "bridgeauth")]
  89. pub struct OldKeys {
  90. /// Most recently outdated lox secret and private keys for verifying update_cred credentials
  91. lox_keys: Vec<OldKeyStore>,
  92. /// Most recently outdated open_invitation VerifyingKey for verifying update_openinv tokens
  93. bridgedb_key: Vec<VerifyingKey>,
  94. /// Most recently outdated invitation secret and private keys for verifying update_inv credentials
  95. invitation_keys: Vec<OldKeyStore>,
  96. }
  97. #[derive(Debug, Default, Clone, Serialize, Deserialize)]
  98. #[cfg(feature = "bridgeauth")]
  99. pub struct OldFilters {
  100. /// Most recently outdated lox id filter
  101. lox_filter: Vec<dup_filter::DupFilter<Scalar>>,
  102. /// Most recently outdated open invitation filter
  103. openinv_filter: Vec<dup_filter::DupFilter<Scalar>>,
  104. /// Most recently outdated invitation filter
  105. invitation_filter: Vec<dup_filter::DupFilter<Scalar>>,
  106. }
  107. /// An open invitation is a [u8; OPENINV_LENGTH] where the first 32
  108. /// bytes are the serialization of a random Scalar (the invitation id),
  109. /// the next 4 bytes are a little-endian bucket number, and the last
  110. /// SIGNATURE_LENGTH bytes are the signature on the first 36 bytes.
  111. pub const OPENINV_LENGTH: usize = 32 // the length of the random
  112. // invitation id (a Scalar)
  113. + 4 // the length of the u32 for the bucket number
  114. + ed25519_dalek::SIGNATURE_LENGTH; // the length of the signature
  115. #[cfg(feature = "bridgeauth")]
  116. impl Default for BridgeDb {
  117. fn default() -> Self {
  118. Self::new()
  119. }
  120. }
  121. #[cfg(feature = "bridgeauth")]
  122. impl BridgeDb {
  123. /// Create the BridgeDb.
  124. pub fn new() -> Self {
  125. let mut csprng = OsRng {};
  126. let keypair = SigningKey::generate(&mut csprng);
  127. let pubkey = keypair.verifying_key();
  128. Self {
  129. keypair,
  130. pubkey,
  131. openinv_buckets: Default::default(),
  132. distributed_buckets: Default::default(),
  133. today: Utc::now(),
  134. current_k: 0,
  135. daily_bridges_distributed: 0,
  136. }
  137. }
  138. /// Verify an open invitation. Returns the invitation id and the
  139. /// bucket number if the signature checked out. It is up to the
  140. /// caller to then check that the invitation id has not been used
  141. /// before.
  142. pub fn verify(
  143. invitation: [u8; OPENINV_LENGTH],
  144. pubkey: VerifyingKey,
  145. ) -> Result<(Scalar, u32), SignatureError> {
  146. // Pull out the signature and verify it
  147. let sig = Signature::try_from(&invitation[(32 + 4)..])?;
  148. pubkey.verify(&invitation[0..(32 + 4)], &sig)?;
  149. // The signature passed. Pull out the bucket number and then
  150. // the invitation id
  151. let bucket = u32::from_le_bytes(invitation[32..(32 + 4)].try_into().unwrap());
  152. let s = Scalar::from_canonical_bytes(invitation[0..32].try_into().unwrap());
  153. if s.is_some().into() {
  154. Ok((s.unwrap(), bucket))
  155. } else {
  156. // It should never happen that there's a valid signature on
  157. // an invalid serialization of a Scalar, but check anyway.
  158. Err(SignatureError::new())
  159. }
  160. }
  161. }
  162. /// The bridge authority. This will typically be a singleton object.
  163. #[cfg(feature = "bridgeauth")]
  164. #[derive(Debug, Serialize, Deserialize)]
  165. pub struct BridgeAuth {
  166. /// The private key for the main Lox credential
  167. lox_priv: CMZPrivkey<G>,
  168. /// The public key for the main Lox credential
  169. pub lox_pub: CMZPubkey<G>,
  170. /// The private key for migration credentials
  171. migration_priv: CMZPrivkey<G>,
  172. /// The public key for migration credentials
  173. pub migration_pub: CMZPubkey<G>,
  174. /// The private key for migration key credentials
  175. migrationkey_priv: CMZPrivkey<G>,
  176. /// The public key for migration key credentials
  177. pub migrationkey_pub: CMZPubkey<G>,
  178. /// The private key for bucket reachability credentials
  179. reachability_priv: CMZPrivkey<G>,
  180. /// The public key for bucket reachability credentials
  181. pub reachability_pub: CMZPubkey<G>,
  182. /// The private key for invitation credentials
  183. invitation_priv: CMZPrivkey<G>,
  184. /// The public key for invitation credentials
  185. pub invitation_pub: CMZPubkey<G>,
  186. /// The public key of the BridgeDb issuing open invitations
  187. pub bridgedb_pub: VerifyingKey,
  188. /// The bridge table
  189. bridge_table: BridgeTable,
  190. // Map of bridge fingerprint to values needed to verify TP reports
  191. //pub tp_bridge_infos: HashMap<String, BridgeVerificationInfo>,
  192. /// The migration tables
  193. trustup_migration_table: MigrationTable,
  194. blockage_migration_table: MigrationTable,
  195. /// Duplicate filter for open invitations
  196. bridgedb_pub_filter: dup_filter::DupFilter<Scalar>,
  197. /// Duplicate filter for Lox credential ids
  198. id_filter: dup_filter::DupFilter<Scalar>,
  199. /// Duplicate filter for Invitation credential ids
  200. inv_id_filter: dup_filter::DupFilter<Scalar>,
  201. /// Duplicate filter for trust promotions (from untrusted level 0 to
  202. /// trusted level 1)
  203. trust_promotion_filter: dup_filter::DupFilter<Scalar>,
  204. // Outdated Lox Keys to be populated with the old Lox private and public keys
  205. // after a key rotation
  206. old_keys: OldKeys,
  207. old_filters: OldFilters,
  208. /// For testing only: offset of the true time to the simulated time
  209. #[serde(skip)]
  210. time_offset: time::Duration,
  211. }
  212. #[cfg(feature = "bridgeauth")]
  213. impl BridgeAuth {
  214. pub fn new(bridgedb_pub: VerifyingKey) -> Self {
  215. // Initialization
  216. let mut rng = rand::thread_rng();
  217. cmz_group_init(G::hash_from_bytes::<Sha512>(b"CMZ Generator A"));
  218. // Create the private and public keys for each of the types of
  219. // credential with 'true' to indicate uCMZ
  220. let (lox_priv, lox_pub) = Lox::gen_keys(&mut rng, true);
  221. let (migration_priv, migration_pub) = Migration::gen_keys(&mut rng, true);
  222. let (migrationkey_priv, migrationkey_pub) = MigrationKey::gen_keys(&mut rng, true);
  223. let (reachability_priv, reachability_pub) = BucketReachability::gen_keys(&mut rng, true);
  224. let (invitation_priv, invitation_pub) = Invitation::gen_keys(&mut rng, true);
  225. Self {
  226. lox_priv,
  227. lox_pub,
  228. migration_priv,
  229. migration_pub,
  230. migrationkey_priv,
  231. migrationkey_pub,
  232. reachability_priv,
  233. reachability_pub,
  234. invitation_priv,
  235. invitation_pub,
  236. bridgedb_pub,
  237. bridge_table: Default::default(),
  238. // tp_bridge_infos: HashMap::<String, BridgeVerificationInfo>::new(),
  239. trustup_migration_table: MigrationTable::new(MigrationType::TrustUpgrade),
  240. blockage_migration_table: MigrationTable::new(MigrationType::Blockage),
  241. bridgedb_pub_filter: Default::default(),
  242. id_filter: Default::default(),
  243. inv_id_filter: Default::default(),
  244. trust_promotion_filter: Default::default(),
  245. time_offset: time::Duration::ZERO,
  246. old_keys: Default::default(),
  247. old_filters: Default::default(),
  248. }
  249. }
  250. /// Get today's (real or simulated) date as u32
  251. pub fn today(&self) -> u32 {
  252. // We will not encounter negative Julian dates (~6700 years ago)
  253. // or ones larger than 32 bits
  254. (time::OffsetDateTime::now_utc().date() + self.time_offset)
  255. .to_julian_day()
  256. .try_into()
  257. .unwrap()
  258. }
  259. /// Get today's (real or simulated) date as a DateTime<Utc> value
  260. pub fn today_date(&self) -> DateTime<Utc> {
  261. Utc::now()
  262. }
  263. }
  264. // Try to extract a u32 from a Scalar
  265. pub fn scalar_u32(s: &Scalar) -> Option<u32> {
  266. // Check that the top 28 bytes of the Scalar are 0
  267. let sbytes: &[u8; 32] = s.as_bytes();
  268. if sbytes[4..].ct_eq(&[0u8; 28]).unwrap_u8() == 0 {
  269. return None;
  270. }
  271. Some(u32::from_le_bytes(sbytes[..4].try_into().unwrap()))
  272. }