lox-extensions 0.1.0

A reputation-based bridge distribution system that provides privacy protection to users and their social graph while remaining open to all users.
Documentation
/*! A module for the protocol for the user to migrate from one bucket to
another and change trust level from untrusted (trust level 0) to trusted
(trust level 1).

The user presents their current Lox credential:

- id: revealed
- bucket: blinded
- trust_level: revealed to be 0
- level_since: blinded
- invites_remaining: revealed to be 0
- blockages: revealed to be 0

and a Migration credential:

- id: revealed as the same as the Lox credential id above
- from_bucket: blinded, but proved in ZK that it's the same as the
  bucket in the Lox credential above
- to_bucket: blinded

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 the to_bucket
  in the Migration credential above
- trust_level: 1
- level_since: today
- invites_remaining: 0
- blockages: 0

*/

#[cfg(feature = "bridgeauth")]
use super::super::dup_filter::SeenType;
#[cfg(feature = "bridgeauth")]
use super::super::BridgeAuth;
use super::super::{Scalar, G};
use super::errors::CredentialError;
use crate::lox_creds::{Lox, Migration};
#[cfg(feature = "bridgeauth")]
use crate::migration_table::MigrationType;
#[cfg(feature = "bridgeauth")]
use crate::scalar_u32;
use cmz::*;
#[cfg(feature = "bridgeauth")]
use ff::PrimeField;
use group::Group;
use rand::{CryptoRng, RngCore};
use sha2::Sha512;

const SESSION_ID: &[u8] = b"migration";

muCMZProtocol! { migration,
    [ L: Lox { id: R, bucket: H, trust_level: R, level_since: H, invites_remaining: R, blockages: R },
    M: Migration { lox_id: R, from_bucket: H, to_bucket: H, migration_type: I} ],
    N: Lox {id: J, bucket: H, trust_level: I, level_since: S, invites_remaining: I, blockages: I },
    L.id = M.lox_id,
    L.bucket = M.from_bucket,
    N.bucket = M.to_bucket,
}

pub fn request(
    rng: &mut (impl CryptoRng + RngCore),
    L: Lox,
    M: Migration,
) -> Result<(migration::Request, migration::ClientState), CredentialError> {
    cmz_group_init(G::hash_from_bytes::<Sha512>(b"CMZ Generator A"));

    // Ensure that the credenials can be correctly shown; that is, the
    // ids match and the Lox credential bucket matches the Migration
    // credential from_bucket
    if L.id.is_some_and(|i| i != M.lox_id.unwrap()) {
        return Err(CredentialError::CredentialMismatch);
    }

    // This protocol only allows migrating from trust level 0 to trust
    // level 1
    if L.trust_level.is_some_and(|t| t != Scalar::ZERO) {
        return Err(CredentialError::InvalidField(
            String::from("trust_level"),
            String::from("must be zero"),
        ));
    }

    if L.bucket.is_some_and(|b| b != M.from_bucket.unwrap()) {
        return Err(CredentialError::CredentialMismatch);
    }

    let mut N = Lox::using_pubkey(L.get_pubkey());
    N.bucket = M.to_bucket;
    N.trust_level = Some(Scalar::ONE);
    N.invites_remaining = Some(Scalar::ZERO);
    N.blockages = Some(Scalar::ZERO);

    match migration::prepare(rng, SESSION_ID, &L, &M, N) {
        Ok(req_state) => Ok(req_state),
        Err(e) => Err(CredentialError::CMZError(e)),
    }
}

#[cfg(feature = "bridgeauth")]
impl BridgeAuth {
    pub fn handle_migration(
        &mut self,
        req: migration::Request,
    ) -> Result<migration::Reply, CredentialError> {
        let mut rng = rand::thread_rng();
        let reqbytes = req.as_bytes();
        let recvreq = migration::Request::try_from(&reqbytes[..]).unwrap();

        let today = self.today();
        match migration::handle(
            &mut rng,
            SESSION_ID,
            recvreq,
            |L: &mut Lox, M: &mut Migration, N: &mut Lox| {
                match scalar_u32(&L.trust_level.unwrap()) {
                    Some(v) if v as usize == 0 => v,
                    _ => {
                        // This error should be improved i.e., InvalidAttr and the type
                        // with a description
                        return Err(CMZError::RevealAttrMissing(
                            "migration",
                            "Could not be converted to u32 or trust_level not 0",
                        ));
                    }
                };
                L.set_privkey(&self.lox_priv);
                M.set_privkey(&self.migration_priv);
                N.set_privkey(&self.lox_priv);
                M.migration_type = Some(Scalar::from_u128(MigrationType::TrustUpgrade.into()));
                N.trust_level = Some(Scalar::ONE);
                N.level_since = Some(today.into());
                N.invites_remaining = Some(Scalar::ZERO);
                N.blockages = Some(Scalar::ZERO);
                Ok(())
            },
            |L: &Lox, _M: &Migration, _N: &Lox| {
                if self.id_filter.filter(&L.id.unwrap()) == SeenType::Seen {
                    return Err(CMZError::RevealAttrMissing("id", "Credential Expired"));
                }
                Ok(())
            },
        ) {
            Ok((response, (_L_issuer, _M_isser, _N_issuer))) => Ok(response),
            Err(e) => Err(CredentialError::CMZError(e)),
        }
    }
}

pub fn handle_response(
    state: migration::ClientState,
    rep: migration::Reply,
) -> Result<Lox, CMZError> {
    let replybytes = rep.as_bytes();
    let recvreply = migration::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;

    #[test]
    fn test_trust_migration() {
        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());
        th.verify_lox(&lox_cred);
    }
}