The response message of the trust promotion protocol
This commit is contained in:
parent
2ffa2c6ab0
commit
03de724a8c
|
@ -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"
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.insert(3 * i, 15 + i);
|
||||||
ba.migration_table.table.push((3 * i + 1, 15 + i));
|
ba.migration_table.table.insert(3 * i + 1, 15 + i);
|
||||||
ba.migration_table.table.push((3 * i + 2, 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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue