From 03de724a8c07658a2d8bfb526b286f5eb54275de Mon Sep 17 00:00:00 2001 From: Ian Goldberg Date: Fri, 30 Apr 2021 11:58:31 -0400 Subject: [PATCH] The response message of the trust promotion protocol --- crates/lox-library/Cargo.toml | 3 +- crates/lox-library/src/bridge_table.rs | 4 +- crates/lox-library/src/migration_table.rs | 157 +++++++++++++++++++++- crates/lox-library/src/open_invite.rs | 2 +- crates/lox-library/src/tests.rs | 7 +- crates/lox-library/src/trust_promotion.rs | 61 ++++++++- crates/lox-library/tests/tests.rs | 1 - 7 files changed, 224 insertions(+), 11 deletions(-) diff --git a/crates/lox-library/Cargo.toml b/crates/lox-library/Cargo.toml index a8cca5a..ea88540 100644 --- a/crates/lox-library/Cargo.toml +++ b/crates/lox-library/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" diff --git a/crates/lox-library/src/bridge_table.rs b/crates/lox-library/src/bridge_table.rs index 3abc640..39b8683 100644 --- a/crates/lox-library/src/bridge_table.rs +++ b/crates/lox-library/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() diff --git a/crates/lox-library/src/migration_table.rs b/crates/lox-library/src/migration_table.rs index 0937c76..ac13f83 100644 --- a/crates/lox-library/src/migration_table.rs +++ b/crates/lox-library/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, +} + +/// 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 = 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() + } } diff --git a/crates/lox-library/src/open_invite.rs b/crates/lox-library/src/open_invite.rs index cef5dcd..b5e47ed 100644 --- a/crates/lox-library/src/open_invite.rs +++ b/crates/lox-library/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 diff --git a/crates/lox-library/src/tests.rs b/crates/lox-library/src/tests.rs index 2c09007..4b30929 100644 --- a/crates/lox-library/src/tests.rs +++ b/crates/lox-library/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); } diff --git a/crates/lox-library/src/trust_promotion.rs b/crates/lox-library/src/trust_promotion.rs index 2fc9074..3f0bad2 100644 --- a/crates/lox-library/src/trust_promotion.rs +++ b/crates/lox-library/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 { 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, + ), + }) } } diff --git a/crates/lox-library/tests/tests.rs b/crates/lox-library/tests/tests.rs index 163bac5..cdd1abd 100644 --- a/crates/lox-library/tests/tests.rs +++ b/crates/lox-library/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;