#[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::{CryptoRng, RngCore};
use sha2::Sha512;
const SESSION_ID: &[u8] = b"issue_invite";
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 - 1,
N.blockages = L.blockages,
I.bucket = L.bucket,
I.blockages = L.blockages
}
pub fn request(
rng: &mut (impl CryptoRng + RngCore),
L: Lox,
B: BucketReachability,
inv_pub: CMZPubkey<G>,
today: u32,
) -> Result<(issue_invite::Request, issue_invite::ClientState), CredentialError> {
cmz_group_init(G::hash_from_bytes::<Sha512>(b"CMZ Generator A"));
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"),
));
}
if L.bucket.is_some_and(|b| b != B.bucket.unwrap()) {
return Err(CredentialError::CredentialMismatch);
}
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"),
));
}
let mut I: Invitation = Invitation::using_pubkey(&inv_pub);
I.inv_id = Some(Scalar::random(rng));
I.date = Some(today.into());
I.bucket = L.bucket;
I.blockages = L.blockages;
let mut N: Lox = Lox::using_pubkey(L.get_pubkey());
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;
match issue_invite::prepare(rng, SESSION_ID, &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,
SESSION_ID,
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"));
}
I.date = Some(today.into());
Ok(())
},
|L: &Lox, _B: &BucketReachability, _I: &Invitation, _N: &Lox| {
if self.id_filter.filter(&L.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),
}
}
#[cfg(all(test, feature = "bridgeauth"))]
mod tests {
use crate::mock_auth::TestHarness;
#[test]
fn test_issue_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());
th.verify_lox(&issue_invite_cred.clone().1);
th.verify_invitation(&issue_invite_cred.clone().0);
}
}