migration_table.rs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. /*! The migration table.
  2. This is a table listing pairs of (from_bucket_id, to_bucket_id). A pair
  3. in this table indicates that a user with a Lox credential containing
  4. from_bucket_id (and possibly meeting other conditions as well) is
  5. entitled to exchange their credential for one with to_bucket_id. (Note
  6. that the credentials contain the bucket attributes, which include both
  7. the id and the bucket decryption key, but the table just contains the
  8. bucket ids.) */
  9. use aes_gcm::aead::{generic_array::GenericArray, Aead};
  10. use aes_gcm::{Aes128Gcm, KeyInit};
  11. use cmz::*;
  12. use curve25519_dalek::ristretto::CompressedRistretto;
  13. use ff::PrimeField;
  14. use group::{WnafBase, WnafScalar};
  15. use rand::RngCore;
  16. use sha2::Digest;
  17. use sha2::Sha256;
  18. use std::collections::HashMap;
  19. #[cfg(feature = "bridgeauth")]
  20. use super::bridge_table;
  21. use super::lox_creds::{Migration, MigrationKey};
  22. use super::{Scalar, G};
  23. use serde::{Deserialize, Serialize};
  24. pub const WNAF_SIZE: usize = 6;
  25. /// Each (plaintext) entry in the returned migration table is serialized
  26. /// into this many bytes
  27. pub const MIGRATION_BYTES: usize = 96;
  28. /// The size of an encrypted entry in the returned migration table
  29. pub const ENC_MIGRATION_BYTES: usize = MIGRATION_BYTES + 12 + 16;
  30. /// The type of migration table: TrustUpgrade is for migrations from
  31. /// untrusted (level 0) 1-bridge buckets to trusted (level 1) 3-bridge
  32. /// buckets. Blockage is for migrations that drop you down two levels
  33. /// (level 3 to 1, level 4 to 2) because the bridges in your current
  34. /// bucket were blocked.
  35. pub enum MigrationType {
  36. TrustUpgrade,
  37. Blockage,
  38. }
  39. impl From<MigrationType> for u128 {
  40. /// Convert a MigrationType into the Scalar value that represents
  41. /// it in the Migration credential
  42. fn from(m: MigrationType) -> Self {
  43. match m {
  44. MigrationType::TrustUpgrade => 0u32,
  45. MigrationType::Blockage => 1u32,
  46. }
  47. .into()
  48. }
  49. }
  50. /// The migration table
  51. #[derive(Default, Debug, Serialize, Deserialize)]
  52. //#[cfg(feature = "bridgeauth")]
  53. pub struct MigrationTable {
  54. pub table: HashMap<u32, u32>,
  55. pub migration_type: Scalar,
  56. }
  57. /// Create an encrypted Migration credential for returning to the user
  58. /// in the trust promotion protocol.
  59. ///
  60. /// Given the attributes of a Migration credential, produce a serialized
  61. /// version (containing only the to_bucket and the MAC, since the
  62. /// receiver will already know the id and from_bucket), encrypted with
  63. /// H2(id, from_bucket, Qk), for the Qk portion of the MAC on the
  64. /// corresponding Migration Key credential (with fixed Pk, given as a
  65. /// precomputed multiplication table). Return the label H1(id,
  66. /// from_attr_i, Qk_i) and the encrypted Migration credential. H1 and
  67. /// H2 are the first 16 bytes and the second 16 bytes respectively of
  68. /// the SHA256 hash of the input.
  69. //#[cfg(feature = "bridgeauth")]
  70. pub fn encrypt_cred(
  71. id: Scalar,
  72. from_bucket: Scalar,
  73. to_bucket: Scalar,
  74. migration_type: Scalar,
  75. Pktable: &WnafBase<G, WNAF_SIZE>,
  76. migration_priv: &CMZPrivkey<G>,
  77. migrationkey_priv: &CMZPrivkey<G>,
  78. ) -> ([u8; 16], [u8; ENC_MIGRATION_BYTES]) {
  79. let mut rng = rand::thread_rng();
  80. // Compute the Migration Key credential MAC Qk
  81. // This is done automatically but how do we use the same Pktable?
  82. let mut K = MigrationKey::using_privkey(migrationkey_priv);
  83. K.lox_id = Some(id);
  84. K.from_bucket = Some(from_bucket);
  85. let coeff: Scalar = K.compute_MAC_coeff(migrationkey_priv).unwrap();
  86. K.MAC.Q = Pktable * &WnafScalar::new(&coeff);
  87. // Compute a MAC (P, Q) on the Migration credential
  88. let mut M = Migration::using_privkey(migration_priv);
  89. M.lox_id = Some(id);
  90. M.from_bucket = Some(from_bucket);
  91. M.to_bucket = Some(to_bucket);
  92. M.migration_type = Some(migration_type);
  93. let _ = M.create_MAC(&mut rng, migration_priv);
  94. // Serialize (to_bucket, P, Q)
  95. let mut credbytes: [u8; MIGRATION_BYTES] = [0; MIGRATION_BYTES];
  96. credbytes[0..32].copy_from_slice(to_bucket.as_bytes());
  97. credbytes[32..64].copy_from_slice(M.MAC.P.compress().as_bytes());
  98. credbytes[64..].copy_from_slice(M.MAC.Q.compress().as_bytes());
  99. // Pick a random nonce
  100. let mut noncebytes: [u8; 12] = [0; 12];
  101. rng.fill_bytes(&mut noncebytes);
  102. let nonce = GenericArray::from_slice(&noncebytes);
  103. // Compute the hash of (id, from_bucket, Qk)
  104. let mut hasher = Sha256::new();
  105. hasher.update(id.as_bytes());
  106. hasher.update(from_bucket.as_bytes());
  107. hasher.update(K.MAC.Q.compress().as_bytes());
  108. let fullhash = hasher.finalize();
  109. // Create the encryption key from the 2nd half of the hash
  110. let aeskey = GenericArray::from_slice(&fullhash[16..]);
  111. // Encrypt
  112. let cipher = Aes128Gcm::new(aeskey);
  113. let ciphertext: Vec<u8> = cipher.encrypt(nonce, credbytes.as_ref()).unwrap();
  114. let mut enccredbytes: [u8; ENC_MIGRATION_BYTES] = [0; ENC_MIGRATION_BYTES];
  115. enccredbytes[..12].copy_from_slice(&noncebytes);
  116. enccredbytes[12..].copy_from_slice(ciphertext.as_slice());
  117. // Use the first half of the above hash as the label
  118. let mut label: [u8; 16] = [0; 16];
  119. label[..].copy_from_slice(&fullhash[..16]);
  120. (label, enccredbytes)
  121. }
  122. /// Create an encrypted Migration credential for returning to the user
  123. /// in the trust promotion protocol, given the ids of the from and to
  124. /// buckets, and the migration type, and using a BridgeTable to get the
  125. /// bucket keys.
  126. ///
  127. /// Otherwise the same as encrypt_cred, above, except it returns an
  128. /// Option in case the passed ids were invalid.
  129. #[cfg(feature = "bridgeauth")]
  130. #[allow(clippy::too_many_arguments)]
  131. pub fn encrypt_cred_ids(
  132. id: Scalar,
  133. from_id: u32,
  134. to_id: u32,
  135. migration_type: Scalar,
  136. bridgetable: &bridge_table::BridgeTable,
  137. Pktable: &WnafBase<G, WNAF_SIZE>,
  138. migration_priv: &CMZPrivkey<G>,
  139. migrationkey_priv: &CMZPrivkey<G>,
  140. ) -> Option<([u8; 16], [u8; ENC_MIGRATION_BYTES])> {
  141. // Look up the bucket keys and form the attributes (Scalars)
  142. let fromkey = bridgetable.keys.get(&from_id)?;
  143. let tokey = bridgetable.keys.get(&to_id)?;
  144. Some(encrypt_cred(
  145. id,
  146. bridge_table::to_scalar(from_id, fromkey),
  147. bridge_table::to_scalar(to_id, tokey),
  148. migration_type,
  149. Pktable,
  150. migration_priv,
  151. migrationkey_priv,
  152. ))
  153. }
  154. #[cfg(feature = "bridgeauth")]
  155. impl MigrationTable {
  156. /// Create a MigrationTable of the given MigrationType
  157. pub fn new(table_type: MigrationType) -> Self {
  158. Self {
  159. table: Default::default(),
  160. migration_type: Scalar::from_u128(table_type.into()),
  161. }
  162. }
  163. /// For each entry in the MigrationTable, use encrypt_cred_ids to
  164. /// produce an entry in an output HashMap (from labels to encrypted
  165. /// Migration credentials).
  166. pub fn encrypt_table(
  167. &self,
  168. id: Scalar,
  169. bridgetable: &bridge_table::BridgeTable,
  170. Pktable: &WnafBase<G, WNAF_SIZE>,
  171. migration_priv: &CMZPrivkey<G>,
  172. migrationkey_priv: &CMZPrivkey<G>,
  173. ) -> HashMap<[u8; 16], [u8; ENC_MIGRATION_BYTES]> {
  174. self.table
  175. .iter()
  176. .filter_map(|(from_id, to_id)| {
  177. encrypt_cred_ids(
  178. id,
  179. *from_id,
  180. *to_id,
  181. self.migration_type,
  182. bridgetable,
  183. Pktable,
  184. migration_priv,
  185. migrationkey_priv,
  186. )
  187. })
  188. .collect()
  189. }
  190. }
  191. /// Decrypt an encrypted Migration credential given Qk, the known
  192. /// attributes id and from_bucket for the Migration credential as well
  193. /// as the known migration type, and a HashMap mapping labels to
  194. /// ciphertexts.
  195. pub fn decrypt_cred(
  196. mk_cred: MigrationKey,
  197. migration_type: MigrationType,
  198. migration_pubkey: CMZPubkey<G>,
  199. enc_migration_table: &HashMap<[u8; 16], [u8; ENC_MIGRATION_BYTES]>,
  200. ) -> Option<Migration> {
  201. // Compute the hash of (id, from_bucket, Qk)
  202. let mut hasher = Sha256::new();
  203. hasher.update(mk_cred.lox_id.unwrap().as_bytes());
  204. hasher.update(mk_cred.from_bucket.unwrap().as_bytes());
  205. hasher.update(mk_cred.MAC.Q.compress().as_bytes());
  206. let fullhash = hasher.finalize();
  207. // Use the first half of the above hash as the label
  208. let mut label: [u8; 16] = [0; 16];
  209. label[..].copy_from_slice(&fullhash[..16]);
  210. // Look up the label in the HashMap
  211. let ciphertext = enc_migration_table.get(&label)?;
  212. // Create the decryption key from the 2nd half of the hash
  213. let aeskey = GenericArray::from_slice(&fullhash[16..]);
  214. // Decrypt
  215. let nonce = GenericArray::from_slice(&ciphertext[..12]);
  216. let cipher = Aes128Gcm::new(aeskey);
  217. let plaintext: Vec<u8> = match cipher.decrypt(nonce, ciphertext[12..].as_ref()) {
  218. Ok(v) => v,
  219. Err(_) => return None,
  220. };
  221. let plaintextbytes = plaintext.as_slice();
  222. let mut to_bucket_bytes: [u8; 32] = [0; 32];
  223. to_bucket_bytes.copy_from_slice(&plaintextbytes[..32]);
  224. let to_bucket = Scalar::from_bytes_mod_order(to_bucket_bytes);
  225. let rng = &mut rand::thread_rng();
  226. let r = Scalar::random(rng);
  227. let P = r * CompressedRistretto::from_slice(&plaintextbytes[32..64])
  228. .expect("Unable to extract P from bucket")
  229. .decompress()?;
  230. let Q = r * CompressedRistretto::from_slice(&plaintextbytes[64..])
  231. .expect("Unable to extract Q from bucket")
  232. .decompress()?;
  233. let mut M = Migration::using_pubkey(&migration_pubkey);
  234. M.MAC.P = P;
  235. M.MAC.Q = Q;
  236. M.lox_id = mk_cred.lox_id;
  237. M.from_bucket = mk_cred.from_bucket;
  238. M.to_bucket = Some(to_bucket);
  239. M.migration_type = Some(Scalar::from_u128(migration_type.into()));
  240. Some(M)
  241. }