lib.rs 11 KB

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