Browse Source

The response message of the trust promotion protocol

Ian Goldberg 3 years ago
parent
commit
d4b2b95a70
7 changed files with 224 additions and 11 deletions
  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]
 [dependencies]
 curve25519-dalek = { package = "curve25519-dalek-ng", version = "3", default-features = false, features = ["serde", "std"] }
 curve25519-dalek = { package = "curve25519-dalek-ng", version = "3", default-features = false, features = ["serde", "std"] }
 ed25519-dalek = "1"
 ed25519-dalek = "1"
-zkp = { version = "0.8", features = ["debug-transcript"] }
+# zkp = { version = "0.8", features = ["debug-transcript"] }
+zkp = "0.8"
 bincode = "1"
 bincode = "1"
 rand = "0.7"
 rand = "0.7"
 serde = "1"
 serde = "1"

+ 2 - 2
src/bridge_table.rs

@@ -252,11 +252,11 @@ mod tests {
 }
 }
 
 
 /// Convert an id and key to a Scalar attribute
 /// 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];
     let mut b: [u8; 32] = [0; 32];
     // b is a little-endian representation of the Scalar; put the key in
     // 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.
     // 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());
     b[16..20].copy_from_slice(&id.to_le_bytes());
     // This cannot fail, since we're only using the low 20 bytes of b
     // This cannot fail, since we're only using the low 20 bytes of b
     Scalar::from_canonical_bytes(b).unwrap()
     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
 the id and the bucket decrytpion key, but the table just contains the
 bucket ids.) */
 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
 /// The migration table
 #[derive(Default, Debug)]
 #[derive(Default, Debug)]
 pub struct MigrationTable {
 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
         // Create the bucket attribute (Scalar), which is a combination
         // of the bucket id (u32) and the bucket's decryption key ([u8; 16])
         // of the bucket id (u32) and the bucket's decryption key ([u8; 16])
         let bucket_key = self.bridge_table.keys[bucket_id];
         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
         // Create the level_since attribute (Scalar), which is today's
         // Julian date
         // Julian date

+ 4 - 3
src/tests.rs

@@ -69,9 +69,9 @@ fn test_trust_promotion() {
         ];
         ];
         ba.bridge_table.new_bucket(bucket);
         ba.bridge_table.new_bucket(bucket);
         // Add the allowed migrations to the migration table
         // 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
     // Create the encrypted bridge table
     ba.bridge_table.encrypt_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 (promreq, promstate) = trust_promotion::request(&cred, &ba.lox_pub, ba.today()).unwrap();
     let resp = ba.handle_trust_promotion(promreq).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::ProofError;
 use zkp::Transcript;
 use zkp::Transcript;
 
 
+use std::collections::HashMap;
+
 use super::cred;
 use super::cred;
+use super::dup_filter::SeenType;
+use super::migration_table;
 use super::{pt_dbl, scalar_dbl, scalar_u64};
 use super::{pt_dbl, scalar_dbl, scalar_u64};
 use super::{BridgeAuth, IssuerPubKey};
 use super::{BridgeAuth, IssuerPubKey};
 use super::{CMZ_A, CMZ_A_TABLE, CMZ_B, CMZ_B_TABLE};
 use super::{CMZ_A, CMZ_A_TABLE, CMZ_B, CMZ_B_TABLE};
@@ -97,6 +101,18 @@ pub struct State {
     bucket: Scalar,
     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! {
 define_proof! {
     requestproof,
     requestproof,
     "Trust Promotion Request",
     "Trust Promotion Request",
@@ -395,9 +411,10 @@ pub fn request(
 
 
 impl BridgeAuth {
 impl BridgeAuth {
     /// Receive a trust promotion request
     /// 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 A: &RistrettoPoint = &CMZ_A;
         let B: &RistrettoPoint = &CMZ_B;
         let B: &RistrettoPoint = &CMZ_B;
+        let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
 
 
         if req.P.is_identity() {
         if req.P.is_identity() {
             return Err(ProofError::VerificationFailure);
             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;
 use lox::dup_filter::SeenType::{Fresh, Seen};
 use lox::dup_filter::SeenType::{Fresh, Seen};
-use lox::BridgeAuth;
 use lox::BridgeDb;
 use lox::BridgeDb;
 
 
 use curve25519_dalek::scalar::Scalar;
 use curve25519_dalek::scalar::Scalar;