| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- /*! A module for the protocol for the user to get promoted from
- untrusted (trust level 0) to trusted (trust level 1).
- They are allowed to do this as long as UNTRUSTED_INTERVAL days have
- passed since they obtained their level 0 Lox credential, and their
- bridge (level 0 users get put in a one-bridge bucket) has not been
- blocked. (Blocked bridges in one-bridge buckets will have their entries
- removed from the bridge authority's trust_promotion table.)
- The user presents their current Lox credential:
- - id: revealed
- - bucket: blinded
- - trust_level: revealed to be 0
- - level_since: blinded, but proved in ZK that it's at least
- UNTRUSTED_INTERVAL days ago
- - invites_remaining: revealed to be 0
- - blockages: revealed to be 0
- They will receive in return the encrypted MAC (Pk, EncQk) for their
- implicit Migration Key credential with attributes id and bucket,
- along with a HashMap of encrypted Migration credentials. For each
- (from_i, to_i) in the BA's migration list, there will be an entry in
- the HashMap with key H1(id, from_attr_i, Qk_i) and value
- Enc_{H2(id, from_attr_i, Qk_i)}(to_attr_i, P_i, Q_i). Here H1 and H2
- are the first 16 bytes and the second 16 bytes respectively of the
- SHA256 hash of the input, P_i and Q_i are a MAC on the Migration
- credential with attributes id, from_attr_i, and to_attr_i. Qk_i is the
- value EncQk would decrypt to if bucket were equal to from_attr_i. */
- #[cfg(feature = "bridgeauth")]
- use super::super::dup_filter::SeenType;
- #[cfg(feature = "bridgeauth")]
- use super::super::BridgeAuth;
- use super::super::{scalar_u32, G};
- use super::errors::CredentialError;
- use crate::lox_creds::{Lox, Migration, MigrationKey};
- use crate::migration_table;
- use crate::migration_table::ENC_MIGRATION_BYTES;
- #[cfg(feature = "bridgeauth")]
- use crate::migration_table::WNAF_SIZE;
- use cmz::*;
- use group::Group;
- #[cfg(feature = "bridgeauth")]
- use group::WnafBase;
- use rand::{CryptoRng, RngCore};
- use sha2::Sha512;
- use std::collections::HashMap;
- const SESSION_ID: &[u8] = b"trust_promo";
- /// The minimum number of days a user has to be at trust level 0
- /// (untrusted) with their (single) bridge unblocked before they can
- /// move to level 1.
- ///
- /// The implementation also puts an upper bound of UNTRUSTED_INTERVAL +
- /// 511 days, which is not unreasonable; we want users to be engaging
- /// with the system in order to move up trust levels.
- pub const UNTRUSTED_INTERVAL: u32 = 30;
- muCMZProtocol! { trust_promotion<credential_expiry, eligibility_max_age>,
- L: Lox { id: R, bucket: H, trust_level: R, level_since: H, invites_remaining: R, blockages: R },
- M: MigrationKey { lox_id: R, from_bucket: H},
- M.lox_id = L.id,
- M.from_bucket = L.bucket,
- (credential_expiry..eligibility_max_age).contains(L.level_since),
- }
- pub fn request(
- rng: &mut (impl CryptoRng + RngCore),
- L: Lox,
- migkey_pubkeys: CMZPubkey<G>,
- today: u32,
- ) -> Result<(trust_promotion::Request, trust_promotion::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_none() {
- return Err(CredentialError::CredentialMismatch);
- }
- // This protocol only allows migrating from trust level 0 to trust
- // level 1
- if let Some(ls) = L.level_since {
- let level_since = match scalar_u32(&ls) {
- Some(v) => v,
- None => {
- return Err(CredentialError::InvalidField(
- String::from("level_since"),
- String::from("could not be converted to u32"),
- ))
- }
- };
- if level_since + UNTRUSTED_INTERVAL > today {
- return Err(CredentialError::TimeThresholdNotMet(
- level_since + UNTRUSTED_INTERVAL - today,
- ));
- }
- let diffdays = today - (level_since + UNTRUSTED_INTERVAL);
- if diffdays > 511 {
- return Err(CredentialError::CredentialExpired);
- }
- }
- let eligibility_max_age = today - UNTRUSTED_INTERVAL;
- let params = trust_promotion::Params {
- credential_expiry: (eligibility_max_age - 511).into(),
- eligibility_max_age: eligibility_max_age.into(),
- };
- let mut M = MigrationKey::using_pubkey(&migkey_pubkeys);
- M.lox_id = L.id;
- M.from_bucket = L.bucket;
- match trust_promotion::prepare(rng, SESSION_ID, &L, M, ¶ms) {
- Ok(req_state) => Ok(req_state),
- Err(e) => Err(CredentialError::CMZError(e)),
- }
- }
- #[cfg(feature = "bridgeauth")]
- impl BridgeAuth {
- pub fn handle_trust_promotion(
- &mut self,
- req: trust_promotion::Request,
- ) -> Result<
- (
- trust_promotion::Reply,
- HashMap<[u8; 16], [u8; ENC_MIGRATION_BYTES]>,
- ),
- CredentialError,
- > {
- let mut rng = rand::thread_rng();
- let reqbytes = req.as_bytes();
- let recvreq = trust_promotion::Request::try_from(&reqbytes[..]).unwrap();
- let today = self.today();
- match trust_promotion::handle(
- &mut rng,
- SESSION_ID,
- recvreq,
- |L: &mut Lox, M: &mut MigrationKey| {
- L.set_privkey(&self.lox_priv);
- M.set_privkey(&self.migrationkey_priv);
- let eligibility_max_age = today - UNTRUSTED_INTERVAL;
- Ok(trust_promotion::Params {
- credential_expiry: (eligibility_max_age - 511).into(),
- eligibility_max_age: eligibility_max_age.into(),
- })
- },
- |L: &Lox, _M: &MigrationKey| {
- if self.id_filter.check(&L.id.unwrap()) == SeenType::Seen
- || self.trust_promotion_filter.filter(&L.id.unwrap()) == SeenType::Seen
- {
- return Err(CMZError::RevealAttrMissing("id", "Credential Expired"));
- }
- Ok(())
- },
- ) {
- Ok((response, (L_issuer, M_issuer))) => {
- let Pktable: WnafBase<G, WNAF_SIZE> = WnafBase::new(M_issuer.MAC.P);
- let enc_migration_table = self.trustup_migration_table.encrypt_table(
- L_issuer.id.unwrap(),
- &self.bridge_table,
- &Pktable,
- &self.migration_priv,
- &self.migrationkey_priv,
- );
- Ok((response, enc_migration_table))
- }
- Err(e) => Err(CredentialError::CMZError(e)),
- }
- }
- }
- pub fn handle_response(
- migration_pubkey: CMZPubkey<G>,
- state: trust_promotion::ClientState,
- rep: trust_promotion::Reply,
- enc_migration_table: HashMap<[u8; 16], [u8; ENC_MIGRATION_BYTES]>,
- ) -> Result<Migration, CMZError> {
- let replybytes = rep.as_bytes();
- let recvreply = trust_promotion::Reply::try_from(&replybytes[..]).unwrap();
- let migkey = match state.finalize(recvreply) {
- Ok(cred) => cred,
- Err(_e) => return Err(CMZError::Unknown),
- };
- match migration_table::decrypt_cred(
- migkey,
- migration_table::MigrationType::TrustUpgrade,
- migration_pubkey,
- &enc_migration_table,
- ) {
- Some(cred) => Ok(cred),
- None => Err(CMZError::Unknown),
- }
- }
- #[cfg(all(test, feature = "bridgeauth"))]
- mod tests {
- use super::*;
- use crate::mock_auth::TestHarness;
- use crate::proto::{open_invite, trust_promotion};
- #[test]
- fn test_trust_promotion() {
- let mut th = TestHarness::new();
- let rng = &mut rand::thread_rng();
- let open_invitation_request = open_invite::request(rng, th.ba.lox_pub.clone());
- assert!(
- open_invitation_request.is_ok(),
- "Open invitation request should succeed"
- );
- let (request, client_state) = open_invitation_request.unwrap();
- let invite = th.bdb.invite();
- let open_invitation_response = th.ba.open_invitation(request, &invite.unwrap());
- assert!(
- open_invitation_response.is_ok(),
- "Open invitation response from server should succeed"
- );
- let (response, _) = open_invitation_response.unwrap();
- let creds = open_invite::handle_response(client_state, response);
- println!("{}", th.ba.today());
- assert!(creds.is_ok(), "Handle response should succeed");
- th.advance_days((UNTRUSTED_INTERVAL + 1).try_into().unwrap());
- println!("{}", th.ba.today());
- let trust_promo_request = trust_promotion::request(
- rng,
- creds.unwrap(),
- th.ba.migrationkey_pub.clone(),
- th.ba.today(),
- );
- assert!(
- trust_promo_request.is_ok(),
- "Trust Promotion request should succeed"
- );
- let (tp_request, tp_client_state) = trust_promo_request.unwrap();
- let trust_promo_response = th.ba.handle_trust_promotion(tp_request);
- assert!(
- trust_promo_response.is_ok(),
- "Trust promotion response from server should succeed"
- );
- let (response, enc) = trust_promo_response.unwrap();
- let creds = trust_promotion::handle_response(
- th.ba.migration_pub.clone(),
- tp_client_state,
- response,
- enc,
- );
- assert!(creds.is_ok(), "Handle response should succeed");
- th.verify_migration(&creds.unwrap());
- }
- }
|