issue_invite.rs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. /*! A module for the protocol for a user to request the issuing of an
  2. Invitation credential they can pass to someone they know.
  3. They are allowed to do this as long as their current Lox credentials has
  4. a non-zero "invites_remaining" attribute (which will be decreased by
  5. one), and they have a Bucket Reachability credential for their current
  6. bucket and today's date. (Such credentials are placed daily in the
  7. encrypted bridge table.)
  8. The user presents their current Lox credential:
  9. - id: revealed
  10. - bucket: blinded
  11. - trust_level: blinded
  12. - level_since: blinded
  13. - invites_remaining: blinded, but proved in ZK that it's not zero
  14. - blockages: blinded
  15. and a Bucket Reachability credential:
  16. - date: revealed to be today
  17. - bucket: blinded, but proved in ZK that it's the same as in the Lox
  18. credential above
  19. and a new Lox credential to be issued:
  20. - id: jointly chosen by the user and BA
  21. - bucket: blinded, but proved in ZK that it's the same as in the Lox
  22. credential above
  23. - trust_level: blinded, but proved in ZK that it's the same as in the
  24. Lox credential above
  25. - level_since: blinded, but proved in ZK that it's the same as in the
  26. Lox credential above
  27. - invites_remaining: blinded, but proved in ZK that it's one less than
  28. the number in the Lox credential above
  29. - blockages: blinded, but proved in ZK that it's the same as in the
  30. Lox credential above
  31. and a new Invitation credential to be issued:
  32. - inv_id: jointly chosen by the user and BA
  33. - date: revealed to be today
  34. - bucket: blinded, but proved in ZK that it's the same as in the Lox
  35. credential above
  36. - blockages: blinded, but proved in ZK that it's the same as in the Lox
  37. credential above
  38. */
  39. #[cfg(feature = "bridgeauth")]
  40. use super::super::dup_filter::SeenType;
  41. #[cfg(feature = "bridgeauth")]
  42. use super::super::BridgeAuth;
  43. use super::super::{scalar_u32, Scalar, G};
  44. use super::errors::CredentialError;
  45. use crate::lox_creds::{BucketReachability, Invitation, Lox};
  46. use cmz::*;
  47. use group::Group;
  48. use rand_core::RngCore;
  49. use sha2::Sha512;
  50. /// Invitations must be used within this many days of being issued.
  51. /// Note that if you change this number to be larger than 15, you must
  52. /// also add bits to the zero knowledge proof.
  53. pub const INVITATION_EXPIRY: u32 = 15;
  54. muCMZProtocol! { issue_invite,
  55. [L: Lox {id: R, bucket: H, trust_level: H, level_since: H, invites_remaining: H, blockages: H}, B: BucketReachability { date: R, bucket: H } ],
  56. [ I: Invitation { inv_id: J, date: S, bucket: H, blockages: H }, N: Lox {id: J, bucket: H, trust_level: H, level_since: H, invites_remaining: H, blockages: H }],
  57. L.bucket = B.bucket,
  58. //L.invites_remaining > 0,
  59. N.bucket = L.bucket,
  60. N.trust_level = L.trust_level,
  61. N.level_since = L.level_since,
  62. N.invites_remaining = L.invites_remaining - Scalar::ONE,
  63. N.blockages = L.blockages,
  64. I. bucket = L.bucket,
  65. I.blockages = L.blockages
  66. }
  67. pub fn request(
  68. L: Lox,
  69. lox_pubkeys: CMZPubkey<G>,
  70. B: BucketReachability,
  71. inv_pub: CMZPubkey<G>,
  72. today: u32,
  73. ) -> Result<(issue_invite::Request, issue_invite::ClientState), CredentialError> {
  74. let mut rng = rand::thread_rng();
  75. cmz_group_init(G::hash_from_bytes::<Sha512>(b"CMZ Generator A"));
  76. // Ensure the credential can be correctly shown: it must be the case
  77. // that invites_remaining not be 0
  78. if let Some(invites_remaining) = L.invites_remaining {
  79. if invites_remaining == Scalar::ZERO {
  80. return Err(CredentialError::NoInvitationsRemaining);
  81. }
  82. } else {
  83. return Err(CredentialError::InvalidField(
  84. String::from("invites_remaining"),
  85. String::from("None"),
  86. ));
  87. }
  88. // The buckets in the Lox and Bucket Reachability credentials have
  89. // to match
  90. if L.bucket.is_some_and(|b| b != B.bucket.unwrap()) {
  91. return Err(CredentialError::CredentialMismatch);
  92. }
  93. // The Bucket Reachability credential has to be dated today
  94. let reach_date: u32 = match scalar_u32(&B.date.unwrap()) {
  95. Some(v) => v,
  96. None => {
  97. return Err(CredentialError::InvalidField(
  98. String::from("date"),
  99. String::from("could not be converted to u32"),
  100. ))
  101. }
  102. };
  103. if reach_date != today {
  104. return Err(CredentialError::InvalidField(
  105. String::from("date"),
  106. String::from("reachability credential must be generated today"),
  107. ));
  108. }
  109. //TODO check all values are not None
  110. let mut I = Invitation::using_pubkey(&inv_pub);
  111. let mut N = Lox::using_pubkey(&lox_pubkeys);
  112. N.bucket = L.bucket;
  113. N.trust_level = L.trust_level;
  114. N.level_since = L.level_since;
  115. N.invites_remaining = Some(L.invites_remaining.unwrap() - Scalar::ONE);
  116. N.blockages = L.blockages;
  117. I.bucket = L.bucket;
  118. I.blockages = L.blockages;
  119. match issue_invite::prepare(&mut rng, &L, &B, I, N) {
  120. Ok(req_state) => Ok(req_state),
  121. Err(e) => Err(CredentialError::CMZError(e)),
  122. }
  123. }
  124. #[cfg(feature = "bridgeauth")]
  125. impl BridgeAuth {
  126. pub fn handle_issue_invite(
  127. &mut self,
  128. req: issue_invite::Request,
  129. ) -> Result<issue_invite::Reply, CredentialError> {
  130. let mut rng = rand::thread_rng();
  131. let reqbytes = req.as_bytes();
  132. let recvreq = issue_invite::Request::try_from(&reqbytes[..]).unwrap();
  133. let today = self.today();
  134. match issue_invite::handle(
  135. &mut rng,
  136. recvreq,
  137. |L: &mut Lox, B: &mut BucketReachability, I: &mut Invitation, N: &mut Lox| {
  138. L.set_privkey(&self.lox_priv);
  139. B.set_privkey(&self.reachability_priv);
  140. I.set_privkey(&self.invitation_priv);
  141. N.set_privkey(&self.lox_priv);
  142. if B.date.is_some_and(|b| b != today.into()) {
  143. return Err(CMZError::RevealAttrMissing("date", "not today"));
  144. }
  145. L.bucket = B.bucket;
  146. N.bucket = L.bucket;
  147. N.trust_level = L.trust_level;
  148. N.level_since = Some(today.into());
  149. N.invites_remaining = Some(L.invites_remaining.unwrap() - Scalar::ONE);
  150. N.blockages = L.blockages;
  151. I.bucket = N.bucket;
  152. I.blockages = L.blockages;
  153. Ok(())
  154. },
  155. |_L: &Lox, _B: &BucketReachability, I: &Invitation, _N: &Lox| {
  156. if self.inv_id_filter.filter(&I.inv_id.unwrap()) == SeenType::Seen {
  157. return Err(CMZError::RevealAttrMissing("id", "Credential Expired"));
  158. }
  159. Ok(())
  160. },
  161. ) {
  162. Ok((response, (_L_issuer, _B_issuer, _I_issuer, _N_issuer))) => Ok(response),
  163. Err(e) => Err(CredentialError::CMZError(e)),
  164. }
  165. }
  166. }
  167. pub fn handle_response(
  168. state: issue_invite::ClientState,
  169. rep: issue_invite::Reply,
  170. ) -> Result<(Invitation, Lox), CMZError> {
  171. let replybytes = rep.as_bytes();
  172. let recvreply = issue_invite::Reply::try_from(&replybytes[..]).unwrap();
  173. match state.finalize(recvreply) {
  174. Ok(creds) => Ok(creds),
  175. Err(_e) => Err(CMZError::Unknown),
  176. }
  177. }