| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- /*! A module for the protocol for a user to request the issuing of an
- Invitation credential they can pass to someone they know.
- They are allowed to do this as long as their current Lox credentials has
- a non-zero "invites_remaining" attribute (which will be decreased by
- one), and they have a Bucket Reachability credential for their current
- bucket and today's date. (Such credentials are placed daily in the
- encrypted bridge table.)
- The user presents their current Lox credential:
- - id: revealed
- - bucket: blinded
- - trust_level: blinded
- - level_since: blinded
- - invites_remaining: blinded, but proved in ZK that it's not zero
- - blockages: blinded
- and a Bucket Reachability credential:
- - date: revealed to be today
- - bucket: blinded, but proved in ZK that it's the same as in the Lox
- credential above
- and a new Lox credential to be issued:
- - id: jointly chosen by the user and BA
- - bucket: blinded, but proved in ZK that it's the same as in the Lox
- credential above
- - trust_level: blinded, but proved in ZK that it's the same as in the
- Lox credential above
- - level_since: blinded, but proved in ZK that it's the same as in the
- Lox credential above
- - invites_remaining: blinded, but proved in ZK that it's one less than
- the number in the Lox credential above
- - blockages: blinded, but proved in ZK that it's the same as in the
- Lox credential above
- and a new Invitation credential to be issued:
- - inv_id: jointly chosen by the user and BA
- - date: revealed to be today
- - bucket: blinded, but proved in ZK that it's the same as in the Lox
- credential above
- - blockages: blinded, but proved in ZK that it's the same as in the Lox
- credential above
- */
- #[cfg(feature = "bridgeauth")]
- use super::super::dup_filter::SeenType;
- #[cfg(feature = "bridgeauth")]
- use super::super::BridgeAuth;
- use super::super::{scalar_u32, Scalar, G};
- use super::errors::CredentialError;
- use crate::lox_creds::{BucketReachability, Invitation, Lox};
- use cmz::*;
- use group::Group;
- use rand_core::RngCore;
- use sha2::Sha512;
- /// Invitations must be used within this many days of being issued.
- /// Note that if you change this number to be larger than 15, you must
- /// also add bits to the zero knowledge proof.
- pub const INVITATION_EXPIRY: u32 = 15;
- muCMZProtocol! { issue_invite,
- [L: Lox {id: R, bucket: H, trust_level: H, level_since: H, invites_remaining: H, blockages: H}, B: BucketReachability { date: R, bucket: H } ],
- [ 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 }],
- L.bucket = B.bucket,
- //L.invites_remaining > 0,
- N.bucket = L.bucket,
- N.trust_level = L.trust_level,
- N.level_since = L.level_since,
- N.invites_remaining = L.invites_remaining - Scalar::ONE,
- N.blockages = L.blockages,
- I. bucket = L.bucket,
- I.blockages = L.blockages
- }
- pub fn request(
- L: Lox,
- lox_pubkeys: CMZPubkey<G>,
- B: BucketReachability,
- inv_pub: CMZPubkey<G>,
- today: u32,
- ) -> Result<(issue_invite::Request, issue_invite::ClientState), CredentialError> {
- let mut rng = rand::thread_rng();
- cmz_group_init(G::hash_from_bytes::<Sha512>(b"CMZ Generator A"));
- // Ensure the credential can be correctly shown: it must be the case
- // that invites_remaining not be 0
- if let Some(invites_remaining) = L.invites_remaining {
- if invites_remaining == Scalar::ZERO {
- return Err(CredentialError::NoInvitationsRemaining);
- }
- } else {
- return Err(CredentialError::InvalidField(
- String::from("invites_remaining"),
- String::from("None"),
- ));
- }
- // The buckets in the Lox and Bucket Reachability credentials have
- // to match
- if L.bucket.is_some_and(|b| b != B.bucket.unwrap()) {
- return Err(CredentialError::CredentialMismatch);
- }
- // The Bucket Reachability credential has to be dated today
- let reach_date: u32 = match scalar_u32(&B.date.unwrap()) {
- Some(v) => v,
- None => {
- return Err(CredentialError::InvalidField(
- String::from("date"),
- String::from("could not be converted to u32"),
- ))
- }
- };
- if reach_date != today {
- return Err(CredentialError::InvalidField(
- String::from("date"),
- String::from("reachability credential must be generated today"),
- ));
- }
- //TODO check all values are not None
- let mut I = Invitation::using_pubkey(&inv_pub);
- let mut N = Lox::using_pubkey(&lox_pubkeys);
- N.bucket = L.bucket;
- N.trust_level = L.trust_level;
- N.level_since = L.level_since;
- N.invites_remaining = Some(L.invites_remaining.unwrap() - Scalar::ONE);
- N.blockages = L.blockages;
- I.bucket = L.bucket;
- I.blockages = L.blockages;
- match issue_invite::prepare(&mut rng, &L, &B, I, N) {
- Ok(req_state) => Ok(req_state),
- Err(e) => Err(CredentialError::CMZError(e)),
- }
- }
- #[cfg(feature = "bridgeauth")]
- impl BridgeAuth {
- pub fn handle_issue_invite(
- &mut self,
- req: issue_invite::Request,
- ) -> Result<issue_invite::Reply, CredentialError> {
- let mut rng = rand::thread_rng();
- let reqbytes = req.as_bytes();
- let recvreq = issue_invite::Request::try_from(&reqbytes[..]).unwrap();
- let today = self.today();
- match issue_invite::handle(
- &mut rng,
- recvreq,
- |L: &mut Lox, B: &mut BucketReachability, I: &mut Invitation, N: &mut Lox| {
- L.set_privkey(&self.lox_priv);
- B.set_privkey(&self.reachability_priv);
- I.set_privkey(&self.invitation_priv);
- N.set_privkey(&self.lox_priv);
- if B.date.is_some_and(|b| b != today.into()) {
- return Err(CMZError::RevealAttrMissing("date", "not today"));
- }
- L.bucket = B.bucket;
- N.bucket = L.bucket;
- N.trust_level = L.trust_level;
- N.level_since = Some(today.into());
- N.invites_remaining = Some(L.invites_remaining.unwrap() - Scalar::ONE);
- N.blockages = L.blockages;
- I.bucket = N.bucket;
- I.blockages = L.blockages;
- Ok(())
- },
- |_L: &Lox, _B: &BucketReachability, I: &Invitation, _N: &Lox| {
- if self.inv_id_filter.filter(&I.inv_id.unwrap()) == SeenType::Seen {
- return Err(CMZError::RevealAttrMissing("id", "Credential Expired"));
- }
- Ok(())
- },
- ) {
- Ok((response, (_L_issuer, _B_issuer, _I_issuer, _N_issuer))) => Ok(response),
- Err(e) => Err(CredentialError::CMZError(e)),
- }
- }
- }
- pub fn handle_response(
- state: issue_invite::ClientState,
- rep: issue_invite::Reply,
- ) -> Result<(Invitation, Lox), CMZError> {
- let replybytes = rep.as_bytes();
- let recvreply = issue_invite::Reply::try_from(&replybytes[..]).unwrap();
- match state.finalize(recvreply) {
- Ok(creds) => Ok(creds),
- Err(_e) => Err(CMZError::Unknown),
- }
- }
|