issue_invite.rs 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  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: hidden
  11. - trust_level: hidden
  12. - level_since: hidden
  13. - invites_remaining: hidden, but proved in ZK that it's not zero
  14. - blockages: hidden
  15. and a Bucket Reachability credential:
  16. - date: revealed to be today
  17. - bucket: hidden, 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: hidden, but proved in ZK that it's the same as in the Lox
  22. credential above
  23. - trust_level: hidden, but proved in ZK that it's the same as in the
  24. Lox credential above
  25. - level_since: hidden, but proved in ZK that it's the same as in the
  26. Lox credential above
  27. - invites_remaining: hidden, but proved in ZK that it's one less than
  28. the number in the Lox credential above
  29. - blockages: hidden, 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: selected by the server to be today's date
  34. - bucket: hidden, but proved in ZK that it's the same as in the Lox
  35. credential above
  36. - blockages: hidden, 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::{CryptoRng, RngCore};
  49. use sha2::Sha512;
  50. const SESSION_ID: &[u8] = b"issue_invite";
  51. /// Invitations must be used within this many days of being issued.
  52. /// Note that if you change this number to be larger than 15, you must
  53. /// also add bits to the zero knowledge proof.
  54. pub const INVITATION_EXPIRY: u32 = 15;
  55. muCMZProtocol! { issue_invite,
  56. [L: Lox {id: R, bucket: H, trust_level: H, level_since: H, invites_remaining: H, blockages: H}, B: BucketReachability { date: R, bucket: H } ],
  57. [ 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 }],
  58. L.bucket = B.bucket,
  59. L.invites_remaining != 0,
  60. N.bucket = L.bucket,
  61. N.trust_level = L.trust_level,
  62. N.level_since = L.level_since,
  63. N.invites_remaining = L.invites_remaining - 1,
  64. N.blockages = L.blockages,
  65. I.bucket = L.bucket,
  66. I.blockages = L.blockages
  67. }
  68. pub fn request(
  69. rng: &mut (impl CryptoRng + RngCore),
  70. L: Lox,
  71. lox_pubkeys: CMZPubkey<G>,
  72. B: BucketReachability,
  73. inv_pub: CMZPubkey<G>,
  74. today: u32,
  75. ) -> Result<(issue_invite::Request, issue_invite::ClientState), CredentialError> {
  76. cmz_group_init(G::hash_from_bytes::<Sha512>(b"CMZ Generator A"));
  77. // Ensure the credential can be correctly shown: it must be the case
  78. // that invites_remaining not be 0
  79. if let Some(invites_remaining) = L.invites_remaining {
  80. if invites_remaining == Scalar::ZERO {
  81. return Err(CredentialError::NoInvitationsRemaining);
  82. }
  83. } else {
  84. return Err(CredentialError::InvalidField(
  85. String::from("invites_remaining"),
  86. String::from("None"),
  87. ));
  88. }
  89. // The buckets in the Lox and Bucket Reachability credentials have
  90. // to match
  91. if L.bucket.is_some_and(|b| b != B.bucket.unwrap()) {
  92. return Err(CredentialError::CredentialMismatch);
  93. }
  94. // The Bucket Reachability credential has to be dated today
  95. let reach_date: u32 = match scalar_u32(&B.date.unwrap()) {
  96. Some(v) => v,
  97. None => {
  98. return Err(CredentialError::InvalidField(
  99. String::from("date"),
  100. String::from("could not be converted to u32"),
  101. ))
  102. }
  103. };
  104. if reach_date != today {
  105. return Err(CredentialError::InvalidField(
  106. String::from("date"),
  107. String::from("reachability credential must be generated today"),
  108. ));
  109. }
  110. let mut I: Invitation = Invitation::using_pubkey(&inv_pub);
  111. I.inv_id = Some(Scalar::random(rng));
  112. I.date = Some(today.into());
  113. I.bucket = L.bucket;
  114. I.blockages = L.blockages;
  115. let mut N: Lox = Lox::using_pubkey(&lox_pubkeys);
  116. N.id = Some(Scalar::random(rng));
  117. N.bucket = L.bucket;
  118. N.trust_level = L.trust_level;
  119. N.level_since = L.level_since;
  120. N.invites_remaining = Some(L.invites_remaining.unwrap() - Scalar::ONE);
  121. N.blockages = L.blockages;
  122. match issue_invite::prepare(rng, SESSION_ID, &L, &B, I, N) {
  123. Ok(req_state) => Ok(req_state),
  124. Err(e) => Err(CredentialError::CMZError(e)),
  125. }
  126. }
  127. #[cfg(feature = "bridgeauth")]
  128. impl BridgeAuth {
  129. pub fn handle_issue_invite(
  130. &mut self,
  131. req: issue_invite::Request,
  132. ) -> Result<issue_invite::Reply, CredentialError> {
  133. let mut rng = rand::thread_rng();
  134. let reqbytes = req.as_bytes();
  135. let recvreq = issue_invite::Request::try_from(&reqbytes[..]).unwrap();
  136. let today = self.today();
  137. match issue_invite::handle(
  138. &mut rng,
  139. SESSION_ID,
  140. recvreq,
  141. |L: &mut Lox, B: &mut BucketReachability, I: &mut Invitation, N: &mut Lox| {
  142. L.set_privkey(&self.lox_priv);
  143. B.set_privkey(&self.reachability_priv);
  144. I.set_privkey(&self.invitation_priv);
  145. N.set_privkey(&self.lox_priv);
  146. if B.date.is_some_and(|b| b != today.into()) {
  147. return Err(CMZError::RevealAttrMissing("date", "not today"));
  148. }
  149. I.date = Some(today.into());
  150. Ok(())
  151. },
  152. |L: &Lox, _B: &BucketReachability, _I: &Invitation, _N: &Lox| {
  153. if self.id_filter.filter(&L.id.unwrap()) == SeenType::Seen {
  154. return Err(CMZError::RevealAttrMissing("id", "Credential Expired"));
  155. }
  156. Ok(())
  157. },
  158. ) {
  159. Ok((response, (_L_issuer, _B_issuer, _I_issuer, _N_issuer))) => Ok(response),
  160. Err(e) => Err(CredentialError::CMZError(e)),
  161. }
  162. }
  163. }
  164. pub fn handle_response(
  165. state: issue_invite::ClientState,
  166. rep: issue_invite::Reply,
  167. ) -> Result<(Invitation, Lox), CMZError> {
  168. let replybytes = rep.as_bytes();
  169. let recvreply = issue_invite::Reply::try_from(&replybytes[..]).unwrap();
  170. match state.finalize(recvreply) {
  171. Ok(creds) => Ok(creds),
  172. Err(_e) => Err(CMZError::Unknown),
  173. }
  174. }
  175. #[cfg(all(test, feature = "bridgeauth"))]
  176. mod tests {
  177. use super::*;
  178. use crate::mock_auth::TestHarness;
  179. #[test]
  180. fn test_issue_invite() {
  181. let mut th = TestHarness::new();
  182. let rng = &mut rand::thread_rng();
  183. let invite = th.bdb.invite().unwrap();
  184. let mut lox_cred = th.open_invite(rng, &invite);
  185. let mig_cred = th.trust_promotion(rng, lox_cred.clone());
  186. lox_cred = th.migration(rng, lox_cred.clone(), mig_cred.clone());
  187. lox_cred = th.level_up(rng, lox_cred.clone());
  188. let issue_invite_cred = th.issue_invite(rng, lox_cred.clone());
  189. th.verify_lox(&issue_invite_cred.clone().1);
  190. th.verify_invitation(&issue_invite_cred.clone().0);
  191. }
  192. }