3
2

rate_limiting.rs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. #![allow(non_snake_case)]
  2. use chrono::Utc;
  3. use cmz::*;
  4. use curve25519_dalek::ristretto::RistrettoPoint;
  5. use group::{Group, GroupEncoding};
  6. use rand::{CryptoRng, RngCore};
  7. use sha2::Sha512;
  8. use std::collections::HashSet;
  9. type G = RistrettoPoint;
  10. CMZ! { Cred: key }
  11. CMZ! { PresNum: pres_num }
  12. muCMZProtocol! { issue_cred,
  13. ,
  14. N: Cred { key: J },
  15. }
  16. muCMZProtocol! { pres_cred<max_pres, @Epoch_base, @VRF_output>,
  17. [ C: Cred { key: H },
  18. P?: PresNum { pres_num: H } ],
  19. ,
  20. Epoch_base = (C.key + P.pres_num)*VRF_output,
  21. (0..max_pres).contains(P.pres_num),
  22. }
  23. struct RateLimitClient {
  24. presnum_pubkey: CMZPubkey<G>,
  25. cred: Cred,
  26. }
  27. impl RateLimitClient {
  28. pub fn new(rng: &mut (impl CryptoRng + RngCore), cred: &Cred) -> Self {
  29. let (_, presnum_pubkey) = PresNum::mucmz_gen_keys(rng);
  30. Self {
  31. presnum_pubkey,
  32. cred: cred.clone(),
  33. }
  34. }
  35. pub fn pres(
  36. &mut self,
  37. rng: &mut (impl CryptoRng + RngCore),
  38. epoch: &[u8],
  39. pres_num: u32,
  40. ) -> Result<Vec<u8>, CMZError> {
  41. let mut P = PresNum::using_pubkey(&self.presnum_pubkey);
  42. P.pres_num = Some(pres_num.into());
  43. P.fake_MAC(rng);
  44. let Epoch_base = RistrettoPoint::hash_from_bytes::<Sha512>(epoch);
  45. let VRF_output = (self.cred.key.unwrap() + P.pres_num.unwrap()).invert() * Epoch_base;
  46. let params = pres_cred::Params {
  47. max_pres: 5u32.into(),
  48. Epoch_base,
  49. VRF_output,
  50. };
  51. let (request, _) = pres_cred::prepare(rng, b"pres_cred", &self.cred, &P, &params)?;
  52. let mut msg: Vec<u8> = Vec::new();
  53. msg.extend(VRF_output.to_bytes());
  54. msg.extend(request.as_bytes());
  55. Ok(msg)
  56. }
  57. }
  58. struct RateLimitServer {
  59. privkey: CMZPrivkey<G>,
  60. presnum_privkey: CMZPrivkey<G>,
  61. seen_tags: HashSet<[u8; 32]>,
  62. }
  63. impl RateLimitServer {
  64. pub fn new(rng: &mut (impl CryptoRng + RngCore), privkey: &CMZPrivkey<G>) -> Self {
  65. let (presnum_privkey, _) = PresNum::mucmz_gen_keys(rng);
  66. Self {
  67. privkey: privkey.clone(),
  68. presnum_privkey,
  69. seen_tags: HashSet::new(),
  70. }
  71. }
  72. pub fn check(
  73. &mut self,
  74. rng: &mut (impl CryptoRng + RngCore),
  75. epoch: &[u8],
  76. msg: &[u8],
  77. ) -> Result<(), CMZError> {
  78. let Epoch_base = RistrettoPoint::hash_from_bytes::<Sha512>(epoch);
  79. // Separate the message into the VRF output and the request
  80. let VRF_output = G::from_bytes(&msg[..32].try_into().unwrap()).unwrap();
  81. let request = pres_cred::Request::try_from(&msg[32..]).unwrap();
  82. let res = pres_cred::handle(
  83. rng,
  84. b"pres_cred",
  85. request,
  86. |C: &mut Cred, P: &mut PresNum| {
  87. let params = pres_cred::Params {
  88. max_pres: 5u32.into(),
  89. Epoch_base,
  90. VRF_output,
  91. };
  92. C.set_privkey(&self.privkey);
  93. P.set_privkey(&self.presnum_privkey);
  94. Ok(params)
  95. },
  96. |_C: &Cred, _P: &PresNum| {
  97. if !self.seen_tags.insert(VRF_output.to_bytes()) {
  98. print!("(duplicate tag seen) ");
  99. Err(CMZError::CliProofFailed)
  100. } else {
  101. Ok(())
  102. }
  103. },
  104. );
  105. match res {
  106. Ok(_) => Ok(()),
  107. Err(e) => Err(e),
  108. }
  109. }
  110. }
  111. #[test]
  112. fn test_rate_limiting() -> Result<(), CMZError> {
  113. let mut rng = rand::thread_rng();
  114. cmz_group_init(RistrettoPoint::hash_from_bytes::<Sha512>(
  115. b"CMZ Generator A",
  116. ));
  117. let (privkey, pubkey) = Cred::mucmz_gen_keys(&mut rng);
  118. // Issue the credential
  119. let (request, state) =
  120. issue_cred::prepare(&mut rng, b"issue_cred", Cred::using_pubkey(&pubkey))?;
  121. let (reply, _) = issue_cred::handle(
  122. &mut rng,
  123. b"issue_cred",
  124. request,
  125. |C: &mut Cred| {
  126. C.set_privkey(&privkey);
  127. Ok(())
  128. },
  129. |_C: &Cred| Ok(()),
  130. )?;
  131. let res = state.finalize(reply);
  132. let cred = match res {
  133. Ok(c) => c,
  134. Err((err, _state)) => Err(err)?,
  135. };
  136. let mut client = RateLimitClient::new(&mut rng, &cred);
  137. let mut server = RateLimitServer::new(&mut rng, &privkey);
  138. let today = Utc::now().date_naive().format("Epoch %Y-%m-%d").to_string();
  139. let mut run_test = |pres_num: u32, should_succeed: bool| {
  140. print!("Presenting {pres_num}: ");
  141. let msg = client.pres(&mut rng, today.as_bytes(), pres_num).unwrap();
  142. let res = server.check(&mut rng, today.as_bytes(), &msg);
  143. match res {
  144. Ok(_) => {
  145. if should_succeed {
  146. println!("success");
  147. } else {
  148. println!("succeeded but should have failed!");
  149. res.unwrap_err();
  150. }
  151. }
  152. Err(_) => {
  153. if should_succeed {
  154. println!("fail!");
  155. res.unwrap();
  156. } else {
  157. println!("failed as expected");
  158. }
  159. }
  160. }
  161. };
  162. run_test(3, true);
  163. run_test(4, true);
  164. run_test(2, true);
  165. // Should fail, because we've presented #3 already
  166. run_test(3, false);
  167. // Should fail, because 5 is out of range of (0..5) = {0,1,2,3,4}
  168. run_test(5, false);
  169. run_test(0, true);
  170. run_test(1, true);
  171. Ok(())
  172. }