diff --git a/crates/lox-library/src/lib.rs b/crates/lox-library/src/lib.rs index f6ca7f7..934a159 100644 --- a/crates/lox-library/src/lib.rs +++ b/crates/lox-library/src/lib.rs @@ -269,6 +269,17 @@ impl BridgeAuth { * cred.P; return Q == cred.Q; } + + #[cfg(test)] + /// Verify the MAC on a Migration credential + pub fn verify_migration(&self, cred: &cred::Migration) -> bool { + let Q = (self.migration_priv.x[0] + + cred.lox_id * self.migration_priv.x[1] + + cred.from_bucket * self.migration_priv.x[2] + + cred.to_bucket * self.migration_priv.x[3]) + * cred.P; + return Q == cred.Q; + } } /// Try to extract a u64 from a Scalar diff --git a/crates/lox-library/src/migration_table.rs b/crates/lox-library/src/migration_table.rs index ac13f83..081da54 100644 --- a/crates/lox-library/src/migration_table.rs +++ b/crates/lox-library/src/migration_table.rs @@ -8,7 +8,9 @@ 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::CompressedRistretto; use curve25519_dalek::ristretto::RistrettoBasepointTable; +use curve25519_dalek::ristretto::RistrettoPoint; use curve25519_dalek::scalar::Scalar; use sha2::Digest; @@ -21,6 +23,7 @@ use rand::RngCore; use std::collections::HashMap; use super::bridge_table; +use super::cred::Migration; use super::IssuerPrivKey; use super::CMZ_B_TABLE; @@ -168,3 +171,52 @@ impl MigrationTable { .collect() } } + +/// Decrypt an encrypted Migration credential given Qk, the known +/// attributes id and from_bucket for the Migration credential, and a +/// HashMap mapping labels to ciphertexts. +pub fn decrypt_cred( + Qk: &RistrettoPoint, + lox_id: &Scalar, + from_bucket: &Scalar, + enc_migration_table: &HashMap<[u8; 16], [u8; ENC_MIGRATION_BYTES]>, +) -> Option { + // Compute the hash of (id, from_bucket, Qk) + let mut hasher = Sha256::new(); + hasher.update(&lox_id.as_bytes()[..]); + hasher.update(&from_bucket.as_bytes()[..]); + hasher.update(&Qk.compress().as_bytes()[..]); + let fullhash = hasher.finalize(); + + // Use the first half of the above hash as the label + let mut label: [u8; 16] = [0; 16]; + label[..].copy_from_slice(&fullhash[..16]); + + // Look up the label in the HashMap + let ciphertext = enc_migration_table.get(&label)?; + + // Create the decryption key from the 2nd half of the hash + let aeskey = GenericArray::from_slice(&fullhash[16..]); + + // Decrypt + let nonce = GenericArray::from_slice(&ciphertext[..12]); + let cipher = Aes128Gcm::new(aeskey); + let plaintext: Vec = match cipher.decrypt(&nonce, ciphertext[12..].as_ref()) { + Ok(v) => v, + Err(_) => return None, + }; + let plaintextbytes = plaintext.as_slice(); + let mut to_bucket_bytes: [u8; 32] = [0; 32]; + to_bucket_bytes.copy_from_slice(&plaintextbytes[..32]); + let to_bucket = Scalar::from_bytes_mod_order(to_bucket_bytes); + let P = CompressedRistretto::from_slice(&plaintextbytes[32..64]).decompress()?; + let Q = CompressedRistretto::from_slice(&plaintextbytes[64..]).decompress()?; + + Some(Migration { + P, + Q, + lox_id: *lox_id, + from_bucket: *from_bucket, + to_bucket, + }) +} diff --git a/crates/lox-library/src/tests.rs b/crates/lox-library/src/tests.rs index 4b30929..c7d31cb 100644 --- a/crates/lox-library/src/tests.rs +++ b/crates/lox-library/src/tests.rs @@ -89,6 +89,13 @@ fn test_trust_promotion() { ba.advance_days(47); let (promreq, promstate) = trust_promotion::request(&cred, &ba.lox_pub, ba.today()).unwrap(); - let resp = ba.handle_trust_promotion(promreq).unwrap(); - println!("resp = {:?}", resp); + let promresp = ba.handle_trust_promotion(promreq).unwrap(); + let migcred = trust_promotion::handle_response(promstate, promresp).unwrap(); + println!("resp = {:?}", migcred); + assert!(ba.verify_migration(&migcred)); + // Check that we can use the to_bucket in the Migration credenital + // to read a bucket + let (id, key) = bridge_table::from_scalar(migcred.to_bucket).unwrap(); + let bucket = ba.bridge_table.decrypt_bucket_id(id, &key).unwrap(); + println!("bucket = {:?}", bucket); } diff --git a/crates/lox-library/src/trust_promotion.rs b/crates/lox-library/src/trust_promotion.rs index 3f0bad2..93ee51f 100644 --- a/crates/lox-library/src/trust_promotion.rs +++ b/crates/lox-library/src/trust_promotion.rs @@ -527,3 +527,23 @@ impl BridgeAuth { }) } } + +/// Handle the response to the request, producing a Migration credential +/// if successful. +/// +/// The Migration credential can then be used in the migration protocol +/// to actually upgrade to trust level 1. +pub fn handle_response(state: State, resp: Response) -> Result { + if resp.Pk.is_identity() { + return Err(ProofError::VerificationFailure); + } + + // Decrypt the MAC on the Migration Key credential + let Qk = resp.EncQk.1 - (state.d * resp.EncQk.0); + + // Use Qk to locate and decrypt the Migration credential + match migration_table::decrypt_cred(&Qk, &state.id, &state.bucket, &resp.enc_migration_table) { + Some(m) => Ok(m), + None => Err(ProofError::VerificationFailure), + } +}