lib.rs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  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 CMZ14 credentials (GGM version, which is more efficient, but
  5. makes a stronger security assumption): "Algebraic MACs and
  6. Keyed-Verification Anonymous Credentials" (Chase, Meiklejohn, and
  7. Zaverucha, CCS 2014)
  8. The notation follows that of the paper "Hyphae: Social Secret Sharing"
  9. (Lovecruft and de Valence, 2017), Section 4. */
  10. // We really want points to be capital letters and scalars to be
  11. // lowercase letters
  12. #![allow(non_snake_case)]
  13. #[macro_use]
  14. extern crate zkp;
  15. pub mod bridge_table;
  16. pub mod cred;
  17. pub mod dup_filter;
  18. pub mod migration_table;
  19. pub mod trust_promotion;
  20. use sha2::Sha512;
  21. use rand::rngs::OsRng;
  22. use rand::RngCore;
  23. use std::convert::{TryFrom, TryInto};
  24. use curve25519_dalek::constants as dalek_constants;
  25. use curve25519_dalek::ristretto::RistrettoBasepointTable;
  26. use curve25519_dalek::ristretto::RistrettoPoint;
  27. use curve25519_dalek::scalar::Scalar;
  28. use ed25519_dalek::{Keypair, PublicKey, Signature, SignatureError, Signer, Verifier};
  29. use subtle::ConstantTimeEq;
  30. use lazy_static::lazy_static;
  31. lazy_static! {
  32. pub static ref CMZ_A: RistrettoPoint =
  33. RistrettoPoint::hash_from_bytes::<Sha512>(b"CMZ Generator A");
  34. pub static ref CMZ_B: RistrettoPoint = dalek_constants::RISTRETTO_BASEPOINT_POINT;
  35. pub static ref CMZ_A_TABLE: RistrettoBasepointTable = RistrettoBasepointTable::create(&CMZ_A);
  36. pub static ref CMZ_B_TABLE: RistrettoBasepointTable =
  37. dalek_constants::RISTRETTO_BASEPOINT_TABLE;
  38. }
  39. #[derive(Clone, Debug)]
  40. pub struct IssuerPrivKey {
  41. x0tilde: Scalar,
  42. x: Vec<Scalar>,
  43. }
  44. impl IssuerPrivKey {
  45. /// Create an IssuerPrivKey for credentials with the given number of
  46. /// attributes.
  47. pub fn new(n: u16) -> IssuerPrivKey {
  48. let mut rng = rand::thread_rng();
  49. let x0tilde = Scalar::random(&mut rng);
  50. let mut x: Vec<Scalar> = Vec::with_capacity((n + 1) as usize);
  51. // Set x to a vector of n+1 random Scalars
  52. x.resize_with((n + 1) as usize, || Scalar::random(&mut rng));
  53. IssuerPrivKey { x0tilde, x }
  54. }
  55. }
  56. #[derive(Clone, Debug)]
  57. pub struct IssuerPubKey {
  58. X: Vec<RistrettoPoint>,
  59. }
  60. impl IssuerPubKey {
  61. /// Create an IssuerPubKey from the corresponding IssuerPrivKey
  62. pub fn new(privkey: &IssuerPrivKey) -> IssuerPubKey {
  63. let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
  64. let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
  65. let n_plus_one = privkey.x.len();
  66. let mut X: Vec<RistrettoPoint> = Vec::with_capacity(n_plus_one);
  67. // The first element is a special case; it is
  68. // X[0] = x0tilde*A + x[0]*B
  69. X.push(&privkey.x0tilde * Atable + &privkey.x[0] * Btable);
  70. // The other elements (1 through n) are X[i] = x[i]*A
  71. for i in 1..n_plus_one {
  72. X.push(&privkey.x[i] * Atable);
  73. }
  74. IssuerPubKey { X }
  75. }
  76. }
  77. /// The BridgeDb. This will typically be a singleton object. The
  78. /// BridgeDb's role is simply to issue signed "open invitations" to
  79. /// people who are not yet part of the system.
  80. #[derive(Debug)]
  81. pub struct BridgeDb {
  82. /// The keypair for signing open invitations
  83. keypair: Keypair,
  84. /// The public key for verifying open invitations
  85. pub pubkey: PublicKey,
  86. /// The number of open-invitation buckets
  87. num_openinv_buckets: u32,
  88. }
  89. /// An open invitation is a [u8; OPENINV_LENGTH] where the first 32
  90. /// bytes are the serialization of a random Scalar (the invitation id),
  91. /// the next 4 bytes are a little-endian bucket number, and the last
  92. /// SIGNATURE_LENGTH bytes are the signature on the first 36 bytes.
  93. pub const OPENINV_LENGTH: usize = 32 // the length of the random
  94. // invitation id (a Scalar)
  95. + 4 // the length of the u32 for the bucket number
  96. + ed25519_dalek::SIGNATURE_LENGTH; // the length of the signature
  97. impl BridgeDb {
  98. /// Create the BridgeDb.
  99. pub fn new(num_openinv_buckets: u32) -> Self {
  100. let mut csprng = OsRng {};
  101. let keypair = Keypair::generate(&mut csprng);
  102. let pubkey = keypair.public;
  103. Self {
  104. keypair,
  105. pubkey,
  106. num_openinv_buckets,
  107. }
  108. }
  109. /// Produce an open invitation. In this example code, we just
  110. /// choose a random open-invitation bucket.
  111. pub fn invite(&self) -> [u8; OPENINV_LENGTH] {
  112. let mut res: [u8; OPENINV_LENGTH] = [0; OPENINV_LENGTH];
  113. let mut rng = rand::thread_rng();
  114. // Choose a random invitation id (a Scalar) and serialize it
  115. let id = Scalar::random(&mut rng);
  116. res[0..32].copy_from_slice(&id.to_bytes());
  117. // Choose a random bucket number (mod num_openinv_buckets) and
  118. // serialize it
  119. let bucket_num = rng.next_u32() % self.num_openinv_buckets;
  120. res[32..(32 + 4)].copy_from_slice(&bucket_num.to_le_bytes());
  121. // Sign the first 36 bytes and serialize it
  122. let sig = self.keypair.sign(&res[0..(32 + 4)]);
  123. res[(32 + 4)..].copy_from_slice(&sig.to_bytes());
  124. res
  125. }
  126. /// Verify an open invitation. Returns the invitation id and the
  127. /// bucket number if the signature checked out. It is up to the
  128. /// caller to then check that the invitation id has not been used
  129. /// before.
  130. pub fn verify(
  131. invitation: [u8; OPENINV_LENGTH],
  132. pubkey: PublicKey,
  133. ) -> Result<(Scalar, u32), SignatureError> {
  134. // Pull out the signature and verify it
  135. let sig = Signature::try_from(&invitation[(32 + 4)..])?;
  136. pubkey.verify(&invitation[0..(32 + 4)], &sig)?;
  137. // The signature passed. Pull out the bucket number and then
  138. // the invitation id
  139. let bucket = u32::from_le_bytes(invitation[32..(32 + 4)].try_into().unwrap());
  140. match Scalar::from_canonical_bytes(invitation[0..32].try_into().unwrap()) {
  141. // It should never happen that there's a valid signature on
  142. // an invalid serialization of a Scalar, but check anyway.
  143. None => Err(SignatureError::new()),
  144. Some(s) => Ok((s, bucket)),
  145. }
  146. }
  147. }
  148. /// The bridge authority. This will typically be a singleton object.
  149. #[derive(Debug)]
  150. pub struct BridgeAuth {
  151. /// The private key for the main Lox credential
  152. lox_priv: IssuerPrivKey,
  153. /// The public key for the main Lox credential
  154. pub lox_pub: IssuerPubKey,
  155. /// The private key for migration credentials
  156. migration_priv: IssuerPrivKey,
  157. /// The public key for migration credentials
  158. pub migration_pub: IssuerPubKey,
  159. /// The private key for migration key credentials
  160. migrationkey_priv: IssuerPrivKey,
  161. /// The public key for migration key credentials
  162. pub migrationkey_pub: IssuerPubKey,
  163. /// The public key of the BridgeDb issuing open invitations
  164. pub bridgedb_pub: PublicKey,
  165. /// The bridge table
  166. bridge_table: bridge_table::BridgeTable,
  167. /// The migration table
  168. migration_table: migration_table::MigrationTable,
  169. /// Duplicate filter for open invitations
  170. openinv_filter: dup_filter::DupFilter<Scalar>,
  171. /// Duplicate filter for credential ids
  172. id_filter: dup_filter::DupFilter<Scalar>,
  173. /// Duplicate filter for trust promotions (from untrusted level 0 to
  174. /// trusted level 1)
  175. trust_promotion_filter: dup_filter::DupFilter<Scalar>,
  176. /// For testing only: offset of the true time to the simulated time
  177. time_offset: time::Duration,
  178. }
  179. impl BridgeAuth {
  180. pub fn new(bridgedb_pub: PublicKey) -> Self {
  181. // Create the private and public keys for each of the types of
  182. // credential, each with the appropriate number of attributes
  183. let lox_priv = IssuerPrivKey::new(6);
  184. let lox_pub = IssuerPubKey::new(&lox_priv);
  185. let migration_priv = IssuerPrivKey::new(3);
  186. let migration_pub = IssuerPubKey::new(&migration_priv);
  187. let migrationkey_priv = IssuerPrivKey::new(2);
  188. let migrationkey_pub = IssuerPubKey::new(&migrationkey_priv);
  189. Self {
  190. lox_priv,
  191. lox_pub,
  192. migration_priv,
  193. migration_pub,
  194. migrationkey_priv,
  195. migrationkey_pub,
  196. bridgedb_pub,
  197. bridge_table: Default::default(),
  198. migration_table: Default::default(),
  199. openinv_filter: Default::default(),
  200. id_filter: Default::default(),
  201. trust_promotion_filter: Default::default(),
  202. time_offset: time::Duration::zero(),
  203. }
  204. }
  205. /// For testing only: manually advance the day by 1 day
  206. pub fn advance_day(&mut self) {
  207. self.time_offset += time::Duration::days(1);
  208. }
  209. /// For testing only: manually advance the day by the given number
  210. /// of days
  211. pub fn advance_days(&mut self, days: u16) {
  212. self.time_offset += time::Duration::days(days.into());
  213. }
  214. /// Get today's (real or simulated) date
  215. fn today(&self) -> u64 {
  216. // We will not encounter negative Julian dates (~6700 years ago)
  217. (time::OffsetDateTime::now_utc().date() + self.time_offset)
  218. .julian_day()
  219. .try_into()
  220. .unwrap()
  221. }
  222. }
  223. /// Try to extract a u64 from a Scalar
  224. pub fn scalar_u64(s: &Scalar) -> Option<u64> {
  225. // Check that the top 24 bytes of the Scalar are 0
  226. let sbytes = s.as_bytes();
  227. if sbytes[8..].ct_eq(&[0u8; 24]).unwrap_u8() == 0 {
  228. return None;
  229. }
  230. Some(u64::from_le_bytes(sbytes[..8].try_into().unwrap()))
  231. }
  232. /// Double a Scalar
  233. pub fn scalar_dbl(s: &Scalar) -> Scalar {
  234. s + s
  235. }
  236. // The protocol modules
  237. pub mod open_invite;
  238. // Unit tests
  239. #[cfg(test)]
  240. mod tests;