Bladeren bron

The response message of the trust promotion protocol

Ian Goldberg 3 jaren geleden
bovenliggende
commit
d4b2b95a70
7 gewijzigde bestanden met toevoegingen van 224 en 11 verwijderingen
  1. 2 1
      Cargo.toml
  2. 2 2
      src/bridge_table.rs
  3. 156 1
      src/migration_table.rs
  4. 1 1
      src/open_invite.rs
  5. 4 3
      src/tests.rs
  6. 59 2
      src/trust_promotion.rs
  7. 0 1
      tests/tests.rs

+ 2 - 1
Cargo.toml

@@ -7,7 +7,8 @@ edition = "2018"
 [dependencies]
 curve25519-dalek = { package = "curve25519-dalek-ng", version = "3", default-features = false, features = ["serde", "std"] }
 ed25519-dalek = "1"
-zkp = { version = "0.8", features = ["debug-transcript"] }
+# zkp = { version = "0.8", features = ["debug-transcript"] }
+zkp = "0.8"
 bincode = "1"
 rand = "0.7"
 serde = "1"

+ 2 - 2
src/bridge_table.rs

@@ -252,11 +252,11 @@ mod tests {
 }
 
 /// Convert an id and key to a Scalar attribute
-pub fn to_scalar(id: u32, key: [u8; 16]) -> Scalar {
+pub fn to_scalar(id: u32, key: &[u8; 16]) -> Scalar {
     let mut b: [u8; 32] = [0; 32];
     // b is a little-endian representation of the Scalar; put the key in
     // the low 16 bytes, and the id in the next 4 bytes.
-    b[0..16].copy_from_slice(&key);
+    b[0..16].copy_from_slice(key);
     b[16..20].copy_from_slice(&id.to_le_bytes());
     // This cannot fail, since we're only using the low 20 bytes of b
     Scalar::from_canonical_bytes(b).unwrap()

+ 156 - 1
src/migration_table.rs

@@ -8,8 +8,163 @@ that the credentials contain the bucket attributes, which include both
 the id and the bucket decrytpion key, but the table just contains the
 bucket ids.) */
 
+use curve25519_dalek::ristretto::RistrettoBasepointTable;
+use curve25519_dalek::scalar::Scalar;
+
+use sha2::Digest;
+use sha2::Sha256;
+
+use aes_gcm::aead::{generic_array::GenericArray, Aead, NewAead};
+use aes_gcm::Aes128Gcm;
+use rand::RngCore;
+
+use std::collections::HashMap;
+
+use super::bridge_table;
+use super::IssuerPrivKey;
+use super::CMZ_B_TABLE;
+
+/// Each (plaintext) entry in the returned migration table is serialized
+/// into this many bytes
+pub const MIGRATION_BYTES: usize = 96;
+
+/// The size of an encrypted entry in the returned migration table
+pub const ENC_MIGRATION_BYTES: usize = MIGRATION_BYTES + 12 + 16;
+
 /// The migration table
 #[derive(Default, Debug)]
 pub struct MigrationTable {
-    pub table: Vec<(u32, u32)>,
+    pub table: HashMap<u32, u32>,
+}
+
+/// Create an encrypted Migration credential for returning to the user
+/// in the trust promotion protocol.
+///
+/// Given the attributes of a Migration credential, produce a serialized
+/// version (containing only the to_bucket and the MAC, since the
+/// receiver will already know the id and from_bucket), encrypted with
+/// H2(id, from_bucket, Qk), for the Qk portion of the MAC on the
+/// corresponding Migration Key credential (with fixed Pk, given as a
+/// precomputed multiplication table).  Return the label H1(id,
+/// from_attr_i, Qk_i) and the encrypted Migration credential.  H1 and
+/// H2 are the first 16 bytes and the second 16 bytes respectively of
+/// the SHA256 hash of the input.
+pub fn encrypt_cred(
+    id: &Scalar,
+    from_bucket: &Scalar,
+    to_bucket: &Scalar,
+    Pktable: &RistrettoBasepointTable,
+    migration_priv: &IssuerPrivKey,
+    migrationkey_priv: &IssuerPrivKey,
+) -> ([u8; 16], [u8; ENC_MIGRATION_BYTES]) {
+    let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
+
+    let mut rng = rand::thread_rng();
+
+    // Compute the Migration Key credential MAC Qk
+    let Qk = &(migrationkey_priv.x[0]
+        + migrationkey_priv.x[1] * id
+        + migrationkey_priv.x[2] * from_bucket)
+        * Pktable;
+
+    // Compute a MAC (P, Q) on the Migration credential
+    let b = Scalar::random(&mut rng);
+    let P = &b * Btable;
+    let Q = &(b
+        * (migration_priv.x[0]
+            + migration_priv.x[1] * id
+            + migration_priv.x[2] * from_bucket
+            + migration_priv.x[3] * to_bucket))
+        * Btable;
+
+    // Serialize (to_bucket, P, Q)
+    let mut credbytes: [u8; MIGRATION_BYTES] = [0; MIGRATION_BYTES];
+    credbytes[0..32].copy_from_slice(&to_bucket.as_bytes()[..]);
+    credbytes[32..64].copy_from_slice(&P.compress().as_bytes()[..]);
+    credbytes[64..].copy_from_slice(&Q.compress().as_bytes()[..]);
+
+    // Pick a random nonce
+    let mut noncebytes: [u8; 12] = [0; 12];
+    rng.fill_bytes(&mut noncebytes);
+    let nonce = GenericArray::from_slice(&noncebytes);
+
+    // Compute the hash of (id, from_bucket, Qk)
+    let mut hasher = Sha256::new();
+    hasher.update(&id.as_bytes()[..]);
+    hasher.update(&from_bucket.as_bytes()[..]);
+    hasher.update(&Qk.compress().as_bytes()[..]);
+    let fullhash = hasher.finalize();
+
+    // Create the encryption key from the 2nd half of the hash
+    let aeskey = GenericArray::from_slice(&fullhash[16..]);
+    // Encrypt
+    let cipher = Aes128Gcm::new(aeskey);
+    let ciphertext: Vec<u8> = cipher.encrypt(&nonce, credbytes.as_ref()).unwrap();
+    let mut enccredbytes: [u8; ENC_MIGRATION_BYTES] = [0; ENC_MIGRATION_BYTES];
+    enccredbytes[..12].copy_from_slice(&noncebytes);
+    enccredbytes[12..].copy_from_slice(ciphertext.as_slice());
+
+    // Use the first half of the above hash as the label
+    let mut label: [u8; 16] = [0; 16];
+    label[..].copy_from_slice(&fullhash[..16]);
+
+    (label, enccredbytes)
+}
+
+/// Create an encrypted Migration credential for returning to the user
+/// in the trust promotion protocol, given the ids of the from and to
+/// buckets, and using a BridgeTable to get the bucket keys.
+///
+/// Otherwise the same as encrypt_cred, above, except it returns an
+/// Option in case the passed ids were invalid.
+pub fn encrypt_cred_ids(
+    id: &Scalar,
+    from_id: u32,
+    to_id: u32,
+    bridgetable: &bridge_table::BridgeTable,
+    Pktable: &RistrettoBasepointTable,
+    migration_priv: &IssuerPrivKey,
+    migrationkey_priv: &IssuerPrivKey,
+) -> Option<([u8; 16], [u8; ENC_MIGRATION_BYTES])> {
+    // Look up the bucket keys and form the attributes (Scalars)
+    let fromkey = bridgetable.keys.get(from_id as usize)?;
+    let tokey = bridgetable.keys.get(to_id as usize)?;
+    Some(encrypt_cred(
+        id,
+        &bridge_table::to_scalar(from_id, fromkey),
+        &bridge_table::to_scalar(to_id, tokey),
+        Pktable,
+        migration_priv,
+        migrationkey_priv,
+    ))
+}
+
+impl MigrationTable {
+    /// For each entry in the MigrationTable, use encrypt_cred_ids to
+    /// produce an entry in an output HashMap (from labels to encrypted
+    /// Migration credentials).
+    pub fn encrypt_table(
+        &self,
+        id: &Scalar,
+        bridgetable: &bridge_table::BridgeTable,
+        Pktable: &RistrettoBasepointTable,
+        migration_priv: &IssuerPrivKey,
+        migrationkey_priv: &IssuerPrivKey,
+    ) -> HashMap<[u8; 16], [u8; ENC_MIGRATION_BYTES]> {
+        self.table
+            .iter()
+            .map(|(from_id, to_id)| {
+                encrypt_cred_ids(
+                    id,
+                    *from_id,
+                    *to_id,
+                    bridgetable,
+                    Pktable,
+                    migration_priv,
+                    migrationkey_priv,
+                )
+                .unwrap()
+            })
+            .collect()
+    }
 }

+ 1 - 1
src/open_invite.rs

@@ -203,7 +203,7 @@ impl BridgeAuth {
         // Create the bucket attribute (Scalar), which is a combination
         // of the bucket id (u32) and the bucket's decryption key ([u8; 16])
         let bucket_key = self.bridge_table.keys[bucket_id];
-        let bucket: Scalar = bridge_table::to_scalar(bucket_id_u32, bucket_key);
+        let bucket: Scalar = bridge_table::to_scalar(bucket_id_u32, &bucket_key);
 
         // Create the level_since attribute (Scalar), which is today's
         // Julian date

+ 4 - 3
src/tests.rs

@@ -69,9 +69,9 @@ fn test_trust_promotion() {
         ];
         ba.bridge_table.new_bucket(bucket);
         // Add the allowed migrations to the migration table
-        ba.migration_table.table.push((3 * i, 15 + i));
-        ba.migration_table.table.push((3 * i + 1, 15 + i));
-        ba.migration_table.table.push((3 * i + 2, 15 + i));
+        ba.migration_table.table.insert(3 * i, 15 + i);
+        ba.migration_table.table.insert(3 * i + 1, 15 + i);
+        ba.migration_table.table.insert(3 * i + 2, 15 + i);
     }
     // Create the encrypted bridge table
     ba.bridge_table.encrypt_table();
@@ -90,4 +90,5 @@ fn test_trust_promotion() {
 
     let (promreq, promstate) = trust_promotion::request(&cred, &ba.lox_pub, ba.today()).unwrap();
     let resp = ba.handle_trust_promotion(promreq).unwrap();
+    println!("resp = {:?}", resp);
 }

+ 59 - 2
src/trust_promotion.rs

@@ -36,7 +36,11 @@ use zkp::CompactProof;
 use zkp::ProofError;
 use zkp::Transcript;
 
+use std::collections::HashMap;
+
 use super::cred;
+use super::dup_filter::SeenType;
+use super::migration_table;
 use super::{pt_dbl, scalar_dbl, scalar_u64};
 use super::{BridgeAuth, IssuerPubKey};
 use super::{CMZ_A, CMZ_A_TABLE, CMZ_B, CMZ_B_TABLE};
@@ -97,6 +101,18 @@ pub struct State {
     bucket: Scalar,
 }
 
+#[derive(Debug)]
+pub struct Response {
+    // The encrypted MAC for the Migration Key credential
+    Pk: RistrettoPoint,
+    EncQk: (RistrettoPoint, RistrettoPoint),
+
+    // A table of encrypted Migration credentials; the encryption keys
+    // are formed from the possible values of Qk (the decrypted form of
+    // EncQk)
+    enc_migration_table: HashMap<[u8; 16], [u8; migration_table::ENC_MIGRATION_BYTES]>,
+}
+
 define_proof! {
     requestproof,
     "Trust Promotion Request",
@@ -395,9 +411,10 @@ pub fn request(
 
 impl BridgeAuth {
     /// Receive a trust promotion request
-    pub fn handle_trust_promotion(&mut self, req: Request) -> Result<(), ProofError> {
+    pub fn handle_trust_promotion(&mut self, req: Request) -> Result<Response, ProofError> {
         let A: &RistrettoPoint = &CMZ_A;
         let B: &RistrettoPoint = &CMZ_B;
+        let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
 
         if req.P.is_identity() {
             return Err(ProofError::VerificationFailure);
@@ -467,6 +484,46 @@ impl BridgeAuth {
             },
         )?;
 
-        Ok(())
+        // Ensure the id has not been seen before, either in the general
+        // if filter, or the filter specifically for trust promotion.
+        // Add the id to the latter, but not the former.
+        if self.id_filter.check(&req.id) == SeenType::Seen
+            || self.trust_promotion_filter.filter(&req.id) == SeenType::Seen
+        {
+            return Err(ProofError::VerificationFailure);
+        }
+
+        // Compute the encrypted MAC (Pk, EncQk) for the Migration Key
+        // credential.
+
+        // Compute the MAC on the visible attributes
+        let mut rng = rand::thread_rng();
+        let b = Scalar::random(&mut rng);
+        let Pk = &b * Btable;
+        let Pktable = RistrettoBasepointTable::create(&Pk);
+        let Qid = &(self.migrationkey_priv.x[0] + self.migrationkey_priv.x[1] * req.id) * &Pktable;
+
+        // El Gamal encrypt it to the public key req.D
+        let s = Scalar::random(&mut rng);
+        let EncQkid = (&s * Btable, Qid + s * req.D);
+
+        // Homomorphically compute the part of the MAC corresponding to
+        // the blinded attributes
+        let tbucket = self.migrationkey_priv.x[2] * b;
+        let EncQkBucket = (tbucket * req.EncBucket.0, tbucket * req.EncBucket.1);
+
+        let EncQk = (EncQkid.0 + EncQkBucket.0, EncQkid.1 + EncQkBucket.1);
+
+        Ok(Response {
+            Pk,
+            EncQk,
+            enc_migration_table: self.migration_table.encrypt_table(
+                &req.id,
+                &self.bridge_table,
+                &Pktable,
+                &self.migration_priv,
+                &self.migrationkey_priv,
+            ),
+        })
     }
 }

+ 0 - 1
tests/tests.rs

@@ -1,6 +1,5 @@
 use lox::dup_filter;
 use lox::dup_filter::SeenType::{Fresh, Seen};
-use lox::BridgeAuth;
 use lox::BridgeDb;
 
 use curve25519_dalek::scalar::Scalar;