trust_promotion.rs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. /*! A module for the protocol for the user to get promoted from
  2. untrusted (trust level 0) to trusted (trust level 1).
  3. They are allowed to do this as long as UNTRUSTED_INTERVAL days have
  4. passed since they obtained their level 0 Lox credential, and their
  5. bridge (level 0 users get put in a one-bridge bucket) has not been
  6. blocked. (Blocked bridges in one-bridge buckets will have their entries
  7. removed from the bridge authority's trust_promotion table.)
  8. The user presents their current Lox credential:
  9. - id: revealed
  10. - bucket: blinded
  11. - trust_level: revealed to be 0
  12. - level_since: blinded, but proved in ZK that it's at least
  13. UNTRUSTED_INTERVAL days ago
  14. - invites_remaining: revealed to be 0
  15. - blockages: revealed to be 0
  16. They will receive in return the encrypted MAC (Pk, EncQk) for their
  17. implicit Migration Key credential with attributes id and bucket,
  18. along with a HashMap of encrypted Migration credentials. For each
  19. (from_i, to_i) in the BA's migration list, there will be an entry in
  20. the HashMap with key H1(id, from_attr_i, Qk_i) and value
  21. Enc_{H2(id, from_attr_i, Qk_i)}(to_attr_i, P_i, Q_i). Here H1 and H2
  22. are the first 16 bytes and the second 16 bytes respectively of the
  23. SHA256 hash of the input, P_i and Q_i are a MAC on the Migration
  24. credential with attributes id, from_attr_i, and to_attr_i. Qk_i is the
  25. value EncQk would decrypt to if bucket were equal to from_attr_i. */
  26. #[cfg(feature = "bridgeauth")]
  27. use super::super::dup_filter::SeenType;
  28. #[cfg(feature = "bridgeauth")]
  29. use super::super::BridgeAuth;
  30. use super::super::{scalar_u32, G};
  31. use super::errors::CredentialError;
  32. use crate::lox_creds::{Lox, Migration, MigrationKey};
  33. use crate::migration_table;
  34. use crate::migration_table::ENC_MIGRATION_BYTES;
  35. #[cfg(feature = "bridgeauth")]
  36. use crate::migration_table::WNAF_SIZE;
  37. use cmz::*;
  38. use group::Group;
  39. #[cfg(feature = "bridgeauth")]
  40. use group::WnafBase;
  41. use rand::{CryptoRng, RngCore};
  42. use sha2::Sha512;
  43. use std::collections::HashMap;
  44. const SESSION_ID: &[u8] = b"trust_promo";
  45. /// The minimum number of days a user has to be at trust level 0
  46. /// (untrusted) with their (single) bridge unblocked before they can
  47. /// move to level 1.
  48. ///
  49. /// The implementation also puts an upper bound of UNTRUSTED_INTERVAL +
  50. /// 511 days, which is not unreasonable; we want users to be engaging
  51. /// with the system in order to move up trust levels.
  52. pub const UNTRUSTED_INTERVAL: u32 = 30;
  53. muCMZProtocol! { trust_promotion<credential_expiry, eligibility_max_age>,
  54. L: Lox { id: R, bucket: H, trust_level: R, level_since: H, invites_remaining: R, blockages: R },
  55. M: MigrationKey { lox_id: R, from_bucket: H},
  56. M.lox_id = L.id,
  57. M.from_bucket = L.bucket,
  58. (credential_expiry..eligibility_max_age).contains(L.level_since),
  59. }
  60. pub fn request(
  61. rng: &mut (impl CryptoRng + RngCore),
  62. L: Lox,
  63. migkey_pubkeys: CMZPubkey<G>,
  64. today: u32,
  65. ) -> Result<(trust_promotion::Request, trust_promotion::ClientState), CredentialError> {
  66. cmz_group_init(G::hash_from_bytes::<Sha512>(b"CMZ Generator A"));
  67. // Ensure that the credenials can be correctly shown; that is, the
  68. // ids match and the Lox credential bucket matches the Migration
  69. // credential from_bucket
  70. if L.id.is_none() {
  71. return Err(CredentialError::CredentialMismatch);
  72. }
  73. // This protocol only allows migrating from trust level 0 to trust
  74. // level 1
  75. if let Some(ls) = L.level_since {
  76. let level_since = match scalar_u32(&ls) {
  77. Some(v) => v,
  78. None => {
  79. return Err(CredentialError::InvalidField(
  80. String::from("level_since"),
  81. String::from("could not be converted to u32"),
  82. ))
  83. }
  84. };
  85. if level_since + UNTRUSTED_INTERVAL > today {
  86. return Err(CredentialError::TimeThresholdNotMet(
  87. level_since + UNTRUSTED_INTERVAL - today,
  88. ));
  89. }
  90. let diffdays = today - (level_since + UNTRUSTED_INTERVAL);
  91. if diffdays > 511 {
  92. return Err(CredentialError::CredentialExpired);
  93. }
  94. }
  95. let eligibility_max_age = today - UNTRUSTED_INTERVAL;
  96. let params = trust_promotion::Params {
  97. credential_expiry: (eligibility_max_age - 511).into(),
  98. eligibility_max_age: eligibility_max_age.into(),
  99. };
  100. let mut M = MigrationKey::using_pubkey(&migkey_pubkeys);
  101. M.lox_id = L.id;
  102. M.from_bucket = L.bucket;
  103. match trust_promotion::prepare(rng, SESSION_ID, &L, M, &params) {
  104. Ok(req_state) => Ok(req_state),
  105. Err(e) => Err(CredentialError::CMZError(e)),
  106. }
  107. }
  108. #[cfg(feature = "bridgeauth")]
  109. impl BridgeAuth {
  110. pub fn handle_trust_promotion(
  111. &mut self,
  112. req: trust_promotion::Request,
  113. ) -> Result<
  114. (
  115. trust_promotion::Reply,
  116. HashMap<[u8; 16], [u8; ENC_MIGRATION_BYTES]>,
  117. ),
  118. CredentialError,
  119. > {
  120. let mut rng = rand::thread_rng();
  121. let reqbytes = req.as_bytes();
  122. let recvreq = trust_promotion::Request::try_from(&reqbytes[..]).unwrap();
  123. let today = self.today();
  124. match trust_promotion::handle(
  125. &mut rng,
  126. SESSION_ID,
  127. recvreq,
  128. |L: &mut Lox, M: &mut MigrationKey| {
  129. L.set_privkey(&self.lox_priv);
  130. M.set_privkey(&self.migrationkey_priv);
  131. let eligibility_max_age = today - UNTRUSTED_INTERVAL;
  132. Ok(trust_promotion::Params {
  133. credential_expiry: (eligibility_max_age - 511).into(),
  134. eligibility_max_age: eligibility_max_age.into(),
  135. })
  136. },
  137. |L: &Lox, _M: &MigrationKey| {
  138. if self.id_filter.check(&L.id.unwrap()) == SeenType::Seen
  139. || self.trust_promotion_filter.filter(&L.id.unwrap()) == SeenType::Seen
  140. {
  141. return Err(CMZError::RevealAttrMissing("id", "Credential Expired"));
  142. }
  143. Ok(())
  144. },
  145. ) {
  146. Ok((response, (L_issuer, M_issuer))) => {
  147. let Pktable: WnafBase<G, WNAF_SIZE> = WnafBase::new(M_issuer.MAC.P);
  148. let enc_migration_table = self.trustup_migration_table.encrypt_table(
  149. L_issuer.id.unwrap(),
  150. &self.bridge_table,
  151. &Pktable,
  152. &self.migration_priv,
  153. &self.migrationkey_priv,
  154. );
  155. Ok((response, enc_migration_table))
  156. }
  157. Err(e) => Err(CredentialError::CMZError(e)),
  158. }
  159. }
  160. }
  161. pub fn handle_response(
  162. migration_pubkey: CMZPubkey<G>,
  163. state: trust_promotion::ClientState,
  164. rep: trust_promotion::Reply,
  165. enc_migration_table: HashMap<[u8; 16], [u8; ENC_MIGRATION_BYTES]>,
  166. ) -> Result<Migration, CMZError> {
  167. let replybytes = rep.as_bytes();
  168. let recvreply = trust_promotion::Reply::try_from(&replybytes[..]).unwrap();
  169. let migkey = match state.finalize(recvreply) {
  170. Ok(cred) => cred,
  171. Err(_e) => return Err(CMZError::Unknown),
  172. };
  173. match migration_table::decrypt_cred(
  174. migkey,
  175. migration_table::MigrationType::TrustUpgrade,
  176. migration_pubkey,
  177. &enc_migration_table,
  178. ) {
  179. Some(cred) => Ok(cred),
  180. None => Err(CMZError::Unknown),
  181. }
  182. }
  183. #[cfg(all(test, feature = "bridgeauth"))]
  184. mod tests {
  185. use super::*;
  186. use crate::mock_auth::TestHarness;
  187. use crate::proto::{open_invite, trust_promotion};
  188. #[test]
  189. fn test_trust_promotion() {
  190. let mut th = TestHarness::new();
  191. let rng = &mut rand::thread_rng();
  192. let open_invitation_request = open_invite::request(rng, th.ba.lox_pub.clone());
  193. assert!(
  194. open_invitation_request.is_ok(),
  195. "Open invitation request should succeed"
  196. );
  197. let (request, client_state) = open_invitation_request.unwrap();
  198. let invite = th.bdb.invite();
  199. let open_invitation_response = th.ba.open_invitation(request, &invite.unwrap());
  200. assert!(
  201. open_invitation_response.is_ok(),
  202. "Open invitation response from server should succeed"
  203. );
  204. let (response, _) = open_invitation_response.unwrap();
  205. let creds = open_invite::handle_response(client_state, response);
  206. println!("{}", th.ba.today());
  207. assert!(creds.is_ok(), "Handle response should succeed");
  208. th.advance_days((UNTRUSTED_INTERVAL + 1).try_into().unwrap());
  209. println!("{}", th.ba.today());
  210. let trust_promo_request = trust_promotion::request(
  211. rng,
  212. creds.unwrap(),
  213. th.ba.migrationkey_pub.clone(),
  214. th.ba.today(),
  215. );
  216. assert!(
  217. trust_promo_request.is_ok(),
  218. "Trust Promotion request should succeed"
  219. );
  220. let (tp_request, tp_client_state) = trust_promo_request.unwrap();
  221. let trust_promo_response = th.ba.handle_trust_promotion(tp_request);
  222. assert!(
  223. trust_promo_response.is_ok(),
  224. "Trust promotion response from server should succeed"
  225. );
  226. let (response, enc) = trust_promo_response.unwrap();
  227. let creds = trust_promotion::handle_response(
  228. th.ba.migration_pub.clone(),
  229. tp_client_state,
  230. response,
  231. enc,
  232. );
  233. assert!(creds.is_ok(), "Handle response should succeed");
  234. th.verify_migration(&creds.unwrap());
  235. }
  236. }