lib.rs 11 KB

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