migration_table.rs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  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::RistrettoBasepointTable;
  10. use curve25519_dalek::scalar::Scalar;
  11. use sha2::Digest;
  12. use sha2::Sha256;
  13. use aes_gcm::aead::{generic_array::GenericArray, Aead, NewAead};
  14. use aes_gcm::Aes128Gcm;
  15. use rand::RngCore;
  16. use std::collections::HashMap;
  17. use super::bridge_table;
  18. use super::IssuerPrivKey;
  19. use super::CMZ_B_TABLE;
  20. /// Each (plaintext) entry in the returned migration table is serialized
  21. /// into this many bytes
  22. pub const MIGRATION_BYTES: usize = 96;
  23. /// The size of an encrypted entry in the returned migration table
  24. pub const ENC_MIGRATION_BYTES: usize = MIGRATION_BYTES + 12 + 16;
  25. /// The migration table
  26. #[derive(Default, Debug)]
  27. pub struct MigrationTable {
  28. pub table: HashMap<u32, u32>,
  29. }
  30. /// Create an encrypted Migration credential for returning to the user
  31. /// in the trust promotion protocol.
  32. ///
  33. /// Given the attributes of a Migration credential, produce a serialized
  34. /// version (containing only the to_bucket and the MAC, since the
  35. /// receiver will already know the id and from_bucket), encrypted with
  36. /// H2(id, from_bucket, Qk), for the Qk portion of the MAC on the
  37. /// corresponding Migration Key credential (with fixed Pk, given as a
  38. /// precomputed multiplication table). Return the label H1(id,
  39. /// from_attr_i, Qk_i) and the encrypted Migration credential. H1 and
  40. /// H2 are the first 16 bytes and the second 16 bytes respectively of
  41. /// the SHA256 hash of the input.
  42. pub fn encrypt_cred(
  43. id: &Scalar,
  44. from_bucket: &Scalar,
  45. to_bucket: &Scalar,
  46. Pktable: &RistrettoBasepointTable,
  47. migration_priv: &IssuerPrivKey,
  48. migrationkey_priv: &IssuerPrivKey,
  49. ) -> ([u8; 16], [u8; ENC_MIGRATION_BYTES]) {
  50. let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
  51. let mut rng = rand::thread_rng();
  52. // Compute the Migration Key credential MAC Qk
  53. let Qk = &(migrationkey_priv.x[0]
  54. + migrationkey_priv.x[1] * id
  55. + migrationkey_priv.x[2] * from_bucket)
  56. * Pktable;
  57. // Compute a MAC (P, Q) on the Migration credential
  58. let b = Scalar::random(&mut rng);
  59. let P = &b * Btable;
  60. let Q = &(b
  61. * (migration_priv.x[0]
  62. + migration_priv.x[1] * id
  63. + migration_priv.x[2] * from_bucket
  64. + migration_priv.x[3] * to_bucket))
  65. * Btable;
  66. // Serialize (to_bucket, P, Q)
  67. let mut credbytes: [u8; MIGRATION_BYTES] = [0; MIGRATION_BYTES];
  68. credbytes[0..32].copy_from_slice(&to_bucket.as_bytes()[..]);
  69. credbytes[32..64].copy_from_slice(&P.compress().as_bytes()[..]);
  70. credbytes[64..].copy_from_slice(&Q.compress().as_bytes()[..]);
  71. // Pick a random nonce
  72. let mut noncebytes: [u8; 12] = [0; 12];
  73. rng.fill_bytes(&mut noncebytes);
  74. let nonce = GenericArray::from_slice(&noncebytes);
  75. // Compute the hash of (id, from_bucket, Qk)
  76. let mut hasher = Sha256::new();
  77. hasher.update(&id.as_bytes()[..]);
  78. hasher.update(&from_bucket.as_bytes()[..]);
  79. hasher.update(&Qk.compress().as_bytes()[..]);
  80. let fullhash = hasher.finalize();
  81. // Create the encryption key from the 2nd half of the hash
  82. let aeskey = GenericArray::from_slice(&fullhash[16..]);
  83. // Encrypt
  84. let cipher = Aes128Gcm::new(aeskey);
  85. let ciphertext: Vec<u8> = cipher.encrypt(&nonce, credbytes.as_ref()).unwrap();
  86. let mut enccredbytes: [u8; ENC_MIGRATION_BYTES] = [0; ENC_MIGRATION_BYTES];
  87. enccredbytes[..12].copy_from_slice(&noncebytes);
  88. enccredbytes[12..].copy_from_slice(ciphertext.as_slice());
  89. // Use the first half of the above hash as the label
  90. let mut label: [u8; 16] = [0; 16];
  91. label[..].copy_from_slice(&fullhash[..16]);
  92. (label, enccredbytes)
  93. }
  94. /// Create an encrypted Migration credential for returning to the user
  95. /// in the trust promotion protocol, given the ids of the from and to
  96. /// buckets, and using a BridgeTable to get the bucket keys.
  97. ///
  98. /// Otherwise the same as encrypt_cred, above, except it returns an
  99. /// Option in case the passed ids were invalid.
  100. pub fn encrypt_cred_ids(
  101. id: &Scalar,
  102. from_id: u32,
  103. to_id: u32,
  104. bridgetable: &bridge_table::BridgeTable,
  105. Pktable: &RistrettoBasepointTable,
  106. migration_priv: &IssuerPrivKey,
  107. migrationkey_priv: &IssuerPrivKey,
  108. ) -> Option<([u8; 16], [u8; ENC_MIGRATION_BYTES])> {
  109. // Look up the bucket keys and form the attributes (Scalars)
  110. let fromkey = bridgetable.keys.get(from_id as usize)?;
  111. let tokey = bridgetable.keys.get(to_id as usize)?;
  112. Some(encrypt_cred(
  113. id,
  114. &bridge_table::to_scalar(from_id, fromkey),
  115. &bridge_table::to_scalar(to_id, tokey),
  116. Pktable,
  117. migration_priv,
  118. migrationkey_priv,
  119. ))
  120. }
  121. impl MigrationTable {
  122. /// For each entry in the MigrationTable, use encrypt_cred_ids to
  123. /// produce an entry in an output HashMap (from labels to encrypted
  124. /// Migration credentials).
  125. pub fn encrypt_table(
  126. &self,
  127. id: &Scalar,
  128. bridgetable: &bridge_table::BridgeTable,
  129. Pktable: &RistrettoBasepointTable,
  130. migration_priv: &IssuerPrivKey,
  131. migrationkey_priv: &IssuerPrivKey,
  132. ) -> HashMap<[u8; 16], [u8; ENC_MIGRATION_BYTES]> {
  133. self.table
  134. .iter()
  135. .map(|(from_id, to_id)| {
  136. encrypt_cred_ids(
  137. id,
  138. *from_id,
  139. *to_id,
  140. bridgetable,
  141. Pktable,
  142. migration_priv,
  143. migrationkey_priv,
  144. )
  145. .unwrap()
  146. })
  147. .collect()
  148. }
  149. }