From 5f8b440d84f9ed4fc390c7bc1ea457c77c3a9515 Mon Sep 17 00:00:00 2001 From: John Smith Date: Fri, 26 May 2023 19:37:03 +0100 Subject: [PATCH] test work --- ...qrl21wlR1IjCA13sIOjw7Byk8ruCfPcnVBRxMLHCrk | Bin 0 -> 24576 bytes ...rpdAUI1bcti0_vJinSu42N9w-vNQRfWBGaR8DLGnAY | Bin 0 -> 24576 bytes ...azGk6a59NnYJyQ-s5bQu3GogeYnRpkHJss5vba1khA | Bin 0 -> 24576 bytes .../src/routing_table/tests/test_serialize.rs | 6 +- veilid-core/src/table_store/mod.rs | 2 + veilid-core/src/table_store/table_store.rs | 135 +++++++++++++++--- veilid-core/src/table_store/tests/mod.rs | 1 + .../tests}/test_table_store.rs | 53 ++++++- veilid-core/src/tests/common/mod.rs | 1 - .../src/tests/common/test_veilid_config.rs | 26 +++- veilid-core/src/tests/mod.rs | 1 + veilid-core/src/tests/native/mod.rs | 1 + veilid-core/src/veilid_config.rs | 3 + veilid-wasm/tests/web.rs | 4 + 14 files changed, 196 insertions(+), 37 deletions(-) create mode 100644 veilid-core/0qrl21wlR1IjCA13sIOjw7Byk8ruCfPcnVBRxMLHCrk create mode 100644 veilid-core/BrpdAUI1bcti0_vJinSu42N9w-vNQRfWBGaR8DLGnAY create mode 100644 veilid-core/bazGk6a59NnYJyQ-s5bQu3GogeYnRpkHJss5vba1khA create mode 100644 veilid-core/src/table_store/tests/mod.rs rename veilid-core/src/{tests/common => table_store/tests}/test_table_store.rs (75%) diff --git a/veilid-core/0qrl21wlR1IjCA13sIOjw7Byk8ruCfPcnVBRxMLHCrk b/veilid-core/0qrl21wlR1IjCA13sIOjw7Byk8ruCfPcnVBRxMLHCrk new file mode 100644 index 0000000000000000000000000000000000000000..7fc33331d4e0c8680c25babde7849df4b542c0af GIT binary patch literal 24576 zcmeI(%WB&|6oBCwUzEl)R(Dzm!z^RsUP51>vJu79aS~O6m{5%BC}1i}B$i6L%;R;{ z_vo^u&}Bx71x>)Kt}6aP!(8lh1xJv#3vAMPjNDjMjO{ao%? zwk>a+P4G!}oyLo_Y#NVtv6=km$P0}A%Z6b&_V&oj;}?OKog%k?R{W;+bd}~Wp*IQw z2q1s}0tg_000IagfB*sr)JEXBYzf7Xga=U%&KE zK>z^+5I_I{1Q0*~0R#|00D&3{6wm*Y|JV2e6B+^tAbvJu79aS~O6m{5%BC}1i}B$i6L%;R;{ z_vo^u&}Bx71x>)Kt}6aP!(8lh1xJv#3vAMPjNDjMjO{ao%? zwk>a+P4G!}oyLo_Y#NVtv6=km$P0}A%Z6b&_V&oj;}?OKog%k?R{W;+bd}~Wp*IQw z2q1s}0tg_000IagfB*sr)JEXBYzf7Xga=U%&KE zK>z^+5I_I{1Q0*~0R#|00D&3{6wm*Y|JV2e6B+^tAbvJu79aS~O6m{5%BC}1i}B$i6L%;R;{ z_vo^u&}Bx71x>)Kt}6aP!(8lh1xJv#3vAMPjNDjMjO{ao%? zwk>a+P4G!}oyLo_Y#NVtv6=km$P0}A%Z6b&_V&oj;}?OKog%k?R{W;+bd}~Wp*IQw z2q1s}0tg_000IagfB*sr)JEXBYzf7Xga=U%&KE zK>z^+5I_I{1Q0*~0R#|00D&3{6wm*Y|JV2e6B+^tAb routing_table::RoutingTable { let block_store = BlockStore::new(veilid_config.clone()); let protected_store = ProtectedStore::new(veilid_config.clone()); let table_store = TableStore::new(veilid_config.clone(), protected_store.clone()); - let crypto = Crypto::new( - veilid_config.clone(), - table_store.clone(), - protected_store.clone(), - ); + let crypto = Crypto::new(veilid_config.clone(), table_store.clone()); let storage_manager = storage_manager::StorageManager::new( veilid_config.clone(), crypto.clone(), diff --git a/veilid-core/src/table_store/mod.rs b/veilid-core/src/table_store/mod.rs index 16ab5483..1e2f27e8 100644 --- a/veilid-core/src/table_store/mod.rs +++ b/veilid-core/src/table_store/mod.rs @@ -5,6 +5,8 @@ mod table_store; pub use table_db::*; pub use table_store::*; +pub mod tests; + #[cfg(target_arch = "wasm32")] mod wasm; #[cfg(target_arch = "wasm32")] diff --git a/veilid-core/src/table_store/table_store.rs b/veilid-core/src/table_store/table_store.rs index 3f0c3574..cd35dacb 100644 --- a/veilid-core/src/table_store/table_store.rs +++ b/veilid-core/src/table_store/table_store.rs @@ -163,15 +163,11 @@ impl TableStore { self.flush().await; } - async fn load_device_encryption_key(&self) -> EyreResult> { - let dek_bytes: Option> = self - .protected_store - .load_user_secret("device_encryption_key") - .await?; - let Some(dek_bytes) = dek_bytes else { - return Ok(None); - }; - + pub fn maybe_unprotect_device_encryption_key( + &self, + dek_bytes: &[u8], + device_encryption_key_password: &str, + ) -> EyreResult { // Ensure the key is at least as long as necessary if unencrypted if dek_bytes.len() < (4 + SHARED_SECRET_LENGTH) { bail!("device encryption key is not valid"); @@ -184,11 +180,6 @@ impl TableStore { bail!("unsupported cryptosystem"); }; - // Decrypt encryption key if we have it - let device_encryption_key_password = { - let c = self.config.get(); - c.protected_store.device_encryption_key_password.clone() - }; if !device_encryption_key_password.is_empty() { if dek_bytes.len() != (4 + SHARED_SECRET_LENGTH + vcrypto.aead_overhead() + NONCE_LENGTH) @@ -209,28 +200,126 @@ impl TableStore { None, ) .wrap_err("failed to decrypt device encryption key")?; - return Ok(Some(TypedSharedSecret::new( + return Ok(TypedSharedSecret::new( kind, SharedSecret::try_from(unprotected_key.as_slice()) .wrap_err("invalid shared secret")?, - ))); + )); } - Ok(Some(TypedSharedSecret::new( + Ok(TypedSharedSecret::new( kind, SharedSecret::try_from(&dek_bytes[4..])?, - ))) + )) + } + + pub fn maybe_protect_device_encryption_key( + &self, + dek: TypedSharedSecret, + device_encryption_key_password: &str, + ) -> EyreResult> { + // Check if we are to protect the key + if device_encryption_key_password.is_empty() { + // Return the unprotected key bytes + let mut out = Vec::with_capacity(4 + SHARED_SECRET_LENGTH); + out.extend_from_slice(&dek.kind.0); + out.extend_from_slice(&dek.value.bytes); + return Ok(out); + } + + // Get cryptosystem + let crypto = self.inner.lock().crypto.as_ref().unwrap().clone(); + let Some(vcrypto) = crypto.get(dek.kind) else { + bail!("unsupported cryptosystem"); + }; + + let nonce = vcrypto.random_nonce(); + let shared_secret = vcrypto + .derive_shared_secret(device_encryption_key_password.as_bytes(), &nonce.bytes) + .wrap_err("failed to derive shared secret")?; + let mut protected_key = vcrypto + .encrypt_aead( + &dek.value.bytes, + &Nonce::try_from(nonce).wrap_err("invalid nonce")?, + &shared_secret, + None, + ) + .wrap_err("failed to decrypt device encryption key")?; + let mut out = + Vec::with_capacity(4 + SHARED_SECRET_LENGTH + vcrypto.aead_overhead() + NONCE_LENGTH); + out.extend_from_slice(&dek.kind.0); + out.append(&mut protected_key); + out.extend_from_slice(&nonce.bytes); + assert!(out.len() == 4 + SHARED_SECRET_LENGTH + vcrypto.aead_overhead() + NONCE_LENGTH); + Ok(out) + } + + async fn load_device_encryption_key(&self) -> EyreResult> { + let dek_bytes: Option> = self + .protected_store + .load_user_secret("device_encryption_key") + .await?; + let Some(dek_bytes) = dek_bytes else { + return Ok(None); + }; + + // Get device encryption key protection password if we have it + let device_encryption_key_password = { + let c = self.config.get(); + c.protected_store.device_encryption_key_password.clone() + }; + + Ok(Some(self.maybe_unprotect_device_encryption_key( + &dek_bytes, + &device_encryption_key_password, + )?)) } async fn save_device_encryption_key( &self, device_encryption_key: Option, ) -> EyreResult<()> { - // Save the new device encryption key - self.protected_store - .save_user_secret_json("device_encryption_key", &device_encryption_key) - .await?; + let Some(device_encryption_key) = device_encryption_key else { + // Remove the device encryption key + let existed = self + .protected_store + .remove_user_secret("device_encryption_key") + .await?; + trace!("removed device encryption key. existed: {}", existed); + return Ok(()); + }; -xxxx + // Get new device encryption key protection password if we are changing it + let new_device_encryption_key_password = { + let c = self.config.get(); + c.protected_store.new_device_encryption_key_password.clone() + }; + let device_encryption_key_password = + if let Some(new_device_encryption_key_password) = new_device_encryption_key_password { + // Change password + self.config + .with_mut(|c| { + c.protected_store.device_encryption_key_password = + new_device_encryption_key_password.clone(); + Ok(new_device_encryption_key_password) + }) + .unwrap() + } else { + // Get device encryption key protection password if we have it + let c = self.config.get(); + c.protected_store.device_encryption_key_password.clone() + }; + + let dek_bytes = self.maybe_protect_device_encryption_key( + device_encryption_key, + &device_encryption_key_password, + )?; + + // Save the new device encryption key + let existed = self + .protected_store + .save_user_secret("device_encryption_key", &dek_bytes) + .await?; + trace!("saving device encryption key. existed: {}", existed); Ok(()) } diff --git a/veilid-core/src/table_store/tests/mod.rs b/veilid-core/src/table_store/tests/mod.rs new file mode 100644 index 00000000..e749760f --- /dev/null +++ b/veilid-core/src/table_store/tests/mod.rs @@ -0,0 +1 @@ +pub mod test_table_store; diff --git a/veilid-core/src/tests/common/test_table_store.rs b/veilid-core/src/table_store/tests/test_table_store.rs similarity index 75% rename from veilid-core/src/tests/common/test_table_store.rs rename to veilid-core/src/table_store/tests/test_table_store.rs index 7118fb27..41bfd022 100644 --- a/veilid-core/src/tests/common/test_table_store.rs +++ b/veilid-core/src/table_store/tests/test_table_store.rs @@ -1,4 +1,4 @@ -use super::test_veilid_config::*; +use crate::tests::test_veilid_config::*; use crate::*; async fn startup() -> VeilidAPI { @@ -208,6 +208,56 @@ pub async fn test_json(vcrypto: CryptoSystemVersion, ts: TableStore) { ); } +pub async fn test_protect_unprotect(vcrypto: CryptoSystemVersion, ts: TableStore) { + trace!("test_protect_unprotect"); + + let dek1 = TypedSharedSecret::new( + vcrypto.kind(), + SharedSecret::new([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ]), + ); + let dek2 = TypedSharedSecret::new( + vcrypto.kind(), + SharedSecret::new([ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0xFF, + ]), + ); + let dek3 = TypedSharedSecret::new( + vcrypto.kind(), + SharedSecret::new([0x80u8; SHARED_SECRET_LENGTH]), + ); + + let deks = [dek1, dek2, dek3]; + let passwords = ["", " ", " ", "12345678", "|/\\!@#$%^&*()_+", "Ⓜ️", "🔥🔥♾️"]; + + for dek in deks { + for password in passwords { + let dek_bytes = ts + .maybe_protect_device_encryption_key(dek, password) + .expect(&format!("protect: dek: '{}' pw: '{}'", dek, password)); + let unprotected = ts + .maybe_unprotect_device_encryption_key(&dek_bytes, password) + .expect(&format!("unprotect: dek: '{}' pw: '{}'", dek, password)); + assert_eq!(unprotected, dek); + let invalid_password = format!("{}x", password); + let _ = ts + .maybe_unprotect_device_encryption_key(&dek_bytes, &invalid_password) + .expect_err(&format!( + "invalid_password: dek: '{}' pw: '{}'", + dek, &invalid_password + )); + if password != "" { + let _ = ts + .maybe_unprotect_device_encryption_key(&dek_bytes, "") + .expect_err(&format!("empty_password: dek: '{}' pw: ''", dek)); + } + } + } +} + pub async fn test_all() { let api = startup().await; let crypto = api.crypto().unwrap(); @@ -215,6 +265,7 @@ pub async fn test_all() { for ck in VALID_CRYPTO_KINDS { let vcrypto = crypto.get(ck).unwrap(); + test_protect_unprotect(vcrypto.clone(), ts.clone()).await; test_delete_open_delete(ts.clone()).await; test_store_delete_load(ts.clone()).await; test_rkyv(vcrypto.clone(), ts.clone()).await; diff --git a/veilid-core/src/tests/common/mod.rs b/veilid-core/src/tests/common/mod.rs index f0fbc066..13d151cd 100644 --- a/veilid-core/src/tests/common/mod.rs +++ b/veilid-core/src/tests/common/mod.rs @@ -1,5 +1,4 @@ pub mod test_host_interface; pub mod test_protected_store; -pub mod test_table_store; pub mod test_veilid_config; pub mod test_veilid_core; diff --git a/veilid-core/src/tests/common/test_veilid_config.rs b/veilid-core/src/tests/common/test_veilid_config.rs index e2670f94..c8458d43 100644 --- a/veilid-core/src/tests/common/test_veilid_config.rs +++ b/veilid-core/src/tests/common/test_veilid_config.rs @@ -166,7 +166,7 @@ pub fn setup_veilid_core() -> (UpdateCallback, ConfigCallback) { fn config_callback(key: String) -> ConfigCallbackReturn { match key.as_str() { - "program_name" => Ok(Box::new(String::from("Veilid"))), + "program_name" => Ok(Box::new(String::from("VeilidCoreTests"))), "namespace" => Ok(Box::new(String::from(""))), "capabilities.protocol_udp" => Ok(Box::new(true)), "capabilities.protocol_connect_tcp" => Ok(Box::new(true)), @@ -176,13 +176,17 @@ fn config_callback(key: String) -> ConfigCallbackReturn { "capabilities.protocol_connect_wss" => Ok(Box::new(true)), "capabilities.protocol_accept_wss" => Ok(Box::new(true)), "table_store.directory" => Ok(Box::new(get_table_store_path())), - "table_store.delete" => Ok(Box::new(false)), + "table_store.delete" => Ok(Box::new(true)), "block_store.directory" => Ok(Box::new(get_block_store_path())), - "block_store.delete" => Ok(Box::new(false)), + "block_store.delete" => Ok(Box::new(true)), "protected_store.allow_insecure_fallback" => Ok(Box::new(true)), "protected_store.always_use_insecure_storage" => Ok(Box::new(false)), "protected_store.directory" => Ok(Box::new(get_protected_store_path())), - "protected_store.delete" => Ok(Box::new(false)), + "protected_store.delete" => Ok(Box::new(true)), + "protected_store.device_encryption_key_password" => Ok(Box::new("".to_owned())), + "protected_store.new_device_encryption_key_password" => { + Ok(Box::new(Option::::None)) + } "network.connection_initial_timeout_ms" => Ok(Box::new(2_000u32)), "network.connection_inactivity_timeout_ms" => Ok(Box::new(60_000u32)), "network.max_connections_per_ip4" => Ok(Box::new(8u32)), @@ -302,13 +306,21 @@ pub async fn test_config() { assert_eq!(inner.capabilities.protocol_connect_wss, true); assert_eq!(inner.capabilities.protocol_accept_wss, true); assert_eq!(inner.table_store.directory, get_table_store_path()); - assert_eq!(inner.table_store.delete, false); + assert_eq!(inner.table_store.delete, true); assert_eq!(inner.block_store.directory, get_block_store_path()); - assert_eq!(inner.block_store.delete, false); + assert_eq!(inner.block_store.delete, true); assert_eq!(inner.protected_store.allow_insecure_fallback, true); assert_eq!(inner.protected_store.always_use_insecure_storage, false); assert_eq!(inner.protected_store.directory, get_protected_store_path()); - assert_eq!(inner.protected_store.delete, false); + assert_eq!(inner.protected_store.delete, true); + assert_eq!( + inner.protected_store.device_encryption_key_password, + "".to_owned() + ); + assert_eq!( + inner.protected_store.new_device_encryption_key_password, + Option::::None + ); assert_eq!(inner.network.connection_initial_timeout_ms, 2_000u32); assert_eq!(inner.network.connection_inactivity_timeout_ms, 60_000u32); assert_eq!(inner.network.max_connections_per_ip4, 8u32); diff --git a/veilid-core/src/tests/mod.rs b/veilid-core/src/tests/mod.rs index 8e9d815f..e82fa3cc 100644 --- a/veilid-core/src/tests/mod.rs +++ b/veilid-core/src/tests/mod.rs @@ -13,4 +13,5 @@ pub use common::*; pub use crypto::tests::*; pub use network_manager::tests::*; pub use routing_table::tests::*; +pub use table_store::tests::*; pub use veilid_api::tests::*; diff --git a/veilid-core/src/tests/native/mod.rs b/veilid-core/src/tests/native/mod.rs index 911af7c3..f2c5ab07 100644 --- a/veilid-core/src/tests/native/mod.rs +++ b/veilid-core/src/tests/native/mod.rs @@ -3,6 +3,7 @@ use crate::crypto::tests::*; use crate::network_manager::tests::*; use crate::routing_table; +use crate::table_store::tests::*; use crate::tests::common::*; use crate::veilid_api; use crate::*; diff --git a/veilid-core/src/veilid_config.rs b/veilid-core/src/veilid_config.rs index 8b00a40d..68c06e6f 100644 --- a/veilid-core/src/veilid_config.rs +++ b/veilid-core/src/veilid_config.rs @@ -734,6 +734,9 @@ impl VeilidConfig { // Remove secrets safe_cfg.network.routing_table.node_id_secret = TypedSecretSet::new(); + safe_cfg.protected_store.device_encryption_key_password = "".to_owned(); + safe_cfg.protected_store.new_device_encryption_key_password = None; + safe_cfg } diff --git a/veilid-wasm/tests/web.rs b/veilid-wasm/tests/web.rs index 4ef8a5bc..5cfbfd26 100644 --- a/veilid-wasm/tests/web.rs +++ b/veilid-wasm/tests/web.rs @@ -1,4 +1,8 @@ //! Test suite for the Web and headless browsers. + +//XXXXXXXXXXXXXXX +//XXX DOES NOT WORK. + #![cfg(target_arch = "wasm32")] extern crate alloc;