/*! A module for the protocol for a user to request the issuing of an updated * invitation credential after a key rotation has occurred The user presents their current Invitation credential: - id: revealed - date: blinded - bucket: blinded - blockages: blinded and a new Invitation credential to be issued: - id: jointly chosen by the user and BA - date: blinded, but proved in ZK that it's the same as in the invitation date above - bucket: blinded, but proved in ZK that it's the same as in the Invitation credential above - blockages: blinded, but proved in ZK that it's the same as in the Invitation credential above */ #[cfg(feature = "bridgeauth")] use super::super::dup_filter::SeenType; #[cfg(feature = "bridgeauth")] use super::super::BridgeAuth; use super::super::G; use super::errors::CredentialError; use crate::lox_creds::Invitation; use cmz::*; use group::Group; use rand::{CryptoRng, RngCore}; use sha2::Sha512; const SESSION_ID: &[u8] = b"update_invite"; muCMZProtocol! { update_invite, I: Invitation { inv_id: R, date: H, bucket: H, blockages: H }, N: Invitation { inv_id: J, date: H, bucket: H, blockages: H }, I.date = N.date, I.bucket = N.bucket, I.blockages = N.blockages, } pub fn request( rng: &mut (impl CryptoRng + RngCore), I: Invitation, new_pubkeys: CMZPubkey, ) -> Result<(update_invite::Request, update_invite::ClientState), CredentialError> { cmz_group_init(G::hash_from_bytes::(b"CMZ Generator A")); let mut N = Invitation::using_pubkey(&new_pubkeys); N.date = I.date; N.bucket = I.bucket; N.blockages = I.blockages; match update_invite::prepare(&mut *rng, SESSION_ID, &I, N) { Ok(req_state) => Ok(req_state), Err(e) => Err(CredentialError::CMZError(e)), } } #[cfg(feature = "bridgeauth")] impl BridgeAuth { pub fn handle_update_invite( &mut self, old_pub_key: CMZPubkey, req: update_invite::Request, ) -> Result { let mut rng = rand::thread_rng(); // Both of these must be true and should be true after rotate_lox_keys is called if self.old_keys.invitation_keys.is_empty() || self.old_filters.invitation_filter.is_empty() { return Err(CredentialError::CredentialMismatch); } let reqbytes = req.as_bytes(); let recvreq = update_invite::Request::try_from(&reqbytes[..]).unwrap(); match update_invite::handle( &mut rng, SESSION_ID, recvreq, |I: &mut Invitation, N: &mut Invitation| { // calling this function will automatically use the most recent old private key for // verification and the new private key for issuing. // Recompute the "error factors" using knowledge of our own // (the issuer's) outdated private key instead of knowledge of the // hidden attributes let old_keys = match self .old_keys .invitation_keys .iter() .find(|x| x.pub_key == old_pub_key) { Some(old_keys) => old_keys, None => return Err(CMZError::RevealAttrMissing("Key", "Mismatch")), }; let old_priv_key = old_keys.priv_key.clone(); I.set_privkey(&old_priv_key); N.set_privkey(&self.invitation_priv); Ok(()) }, |I: &Invitation, _N: &Invitation| { let index = match self .old_keys .invitation_keys .iter() .position(|x| x.pub_key == old_pub_key) { Some(index) => index, None => return Err(CMZError::RevealAttrMissing("Key", "Mismatch")), }; if self .old_filters .invitation_filter .get_mut(index) .unwrap() .filter(&I.inv_id.unwrap()) == SeenType::Seen { return Err(CMZError::RevealAttrMissing("id", "Credential Expired")); } Ok(()) }, ) { Ok((response, (_I_issuer, _N_issuer))) => Ok(response), Err(e) => Err(CredentialError::CMZError(e)), } } } pub fn handle_response( state: update_invite::ClientState, rep: update_invite::Reply, ) -> Result { let replybytes = rep.as_bytes(); let recvreply = update_invite::Reply::try_from(&replybytes[..]).unwrap(); match state.finalize(recvreply) { Ok(cred) => Ok(cred), Err(_e) => Err(CMZError::Unknown), } } #[cfg(all(test, feature = "bridgeauth"))] mod tests { use crate::mock_auth::TestHarness; use crate::proto::update_invite; #[test] fn test_update_invite() { let mut th = TestHarness::new(); let rng = &mut rand::thread_rng(); let invite = th.bdb.invite().unwrap(); let mut lox_cred = th.open_invite(rng, &invite); let mig_cred = th.trust_promotion(rng, lox_cred.clone()); lox_cred = th.migration(rng, lox_cred.clone(), mig_cred.clone()); lox_cred = th.level_up(rng, lox_cred.clone()); let issue_invite_cred = th.issue_invite(rng, lox_cred.clone()); let old_pub = th.ba.invitation_pub.clone(); th.ba.rotate_invitation_keys(rng); let update_invite_request = update_invite::request( rng, issue_invite_cred.clone().0, th.ba.invitation_pub.clone(), ); assert!( update_invite_request.is_ok(), "Update invitation request should succeed" ); let (request, client_state) = update_invite_request.unwrap(); let update_invite_response = th.ba.handle_update_invite(old_pub, request); assert!( update_invite_response.is_ok(), "Update invite response from server should succeed" ); let response = update_invite_response.unwrap(); let creds = update_invite::handle_response(client_state, response); assert!(creds.is_ok(), "Handle response should succeed"); th.verify_invitation(&creds.unwrap()); } }