diff --git a/crates/lox-library/Cargo.toml b/crates/lox-library/Cargo.toml index 248d432..9a2ffcb 100644 --- a/crates/lox-library/Cargo.toml +++ b/crates/lox-library/Cargo.toml @@ -17,6 +17,7 @@ hex_fmt = "0.3" aes-gcm = "0.8" base64 = "0.13" time = "0.2" +subtle = "2.4" [features] default = ["u64_backend"] diff --git a/crates/lox-library/src/bridge_table.rs b/crates/lox-library/src/bridge_table.rs index 8429f52..550d278 100644 --- a/crates/lox-library/src/bridge_table.rs +++ b/crates/lox-library/src/bridge_table.rs @@ -13,6 +13,7 @@ use aes_gcm::Aes128Gcm; use curve25519_dalek::scalar::Scalar; use rand::RngCore; use std::convert::TryInto; +use subtle::ConstantTimeEq; /// Each bridge information line is serialized into this many bytes pub const BRIDGE_BYTES: usize = 220; @@ -194,6 +195,16 @@ impl BridgeTable { plaintext.as_slice().try_into().unwrap(), )) } + + /// Decrypt an individual encrypted bucket, given its id and key + pub fn decrypt_bucket_id( + &self, + id: u32, + key: &[u8; 16], + ) -> Result<[BridgeLine; MAX_BRIDGES_PER_BUCKET], aead::Error> { + let encbucket = self.encbuckets[id as usize]; + BridgeTable::decrypt_bucket(key, &encbucket) + } } // Unit tests that require access to the testing-only function @@ -225,18 +236,15 @@ mod tests { btable.encrypt_table(); // Try to decrypt a 1-bridge bucket let key7 = btable.keys[7]; - let encbucket7 = btable.encbuckets[7]; - let bucket7 = BridgeTable::decrypt_bucket(&key7, &encbucket7)?; + let bucket7 = btable.decrypt_bucket_id(7, &key7)?; println!("bucket 7 = {:?}", bucket7); // Try to decrypt a 3-bridge bucket let key24 = btable.keys[24]; - let encbucket24 = btable.encbuckets[24]; - let bucket24 = BridgeTable::decrypt_bucket(&key24, &encbucket24)?; + let bucket24 = btable.decrypt_bucket_id(24, &key24)?; println!("bucket 24 = {:?}", bucket24); // Try to decrypt a bucket with the wrong key let key12 = btable.keys[12]; - let encbucket15 = btable.encbuckets[15]; - let res = BridgeTable::decrypt_bucket(&key12, &encbucket15).unwrap_err(); + let res = btable.decrypt_bucket_id(15, &key12).unwrap_err(); println!("bucket key mismatch = {:?}", res); Ok(()) } @@ -252,3 +260,16 @@ pub fn to_scalar(id: u32, key: [u8; 16]) -> Scalar { // This cannot fail, since we're only using the low 20 bytes of b Scalar::from_canonical_bytes(b).unwrap() } + +/// Convert a Scalar attribute to an id and key if possible +pub fn from_scalar(s: Scalar) -> Result<(u32, [u8; 16]), aead::Error> { + // Check that the top 12 bytes of the Scalar are 0 + let sbytes = s.as_bytes(); + if sbytes[20..].ct_eq(&[0u8; 12]).unwrap_u8() == 0 { + return Err(aead::Error); + } + let id = u32::from_le_bytes(sbytes[16..20].try_into().unwrap()); + let mut key: [u8; 16] = [0; 16]; + key.copy_from_slice(&sbytes[..16]); + Ok((id, key)) +} diff --git a/crates/lox-library/src/lib.rs b/crates/lox-library/src/lib.rs index a81bcee..1972ea2 100644 --- a/crates/lox-library/src/lib.rs +++ b/crates/lox-library/src/lib.rs @@ -277,6 +277,11 @@ mod tests { let resp = ba.handle_open_invite(req).unwrap(); let cred = open_invite::handle_response(state, resp, &ba.lox_pub, &ba.migration_pub).unwrap(); + + // Check that we can use the credential to read a bucket + let (id, key) = bridge_table::from_scalar(cred.bucket).unwrap(); + let bucket = ba.bridge_table.decrypt_bucket_id(id, &key).unwrap(); println!("cred = {:?}", cred); + println!("bucket = {:?}", bucket); } }