lib.rs 11 KB

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