migration_table.rs 9.0 KB

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