test work

This commit is contained in:
John Smith 2023-05-26 19:37:03 +01:00
parent 5b0bfcef48
commit 5f8b440d84
14 changed files with 196 additions and 37 deletions

View File

@ -5,11 +5,7 @@ fn fake_routing_table() -> routing_table::RoutingTable {
let block_store = BlockStore::new(veilid_config.clone()); let block_store = BlockStore::new(veilid_config.clone());
let protected_store = ProtectedStore::new(veilid_config.clone()); let protected_store = ProtectedStore::new(veilid_config.clone());
let table_store = TableStore::new(veilid_config.clone(), protected_store.clone()); let table_store = TableStore::new(veilid_config.clone(), protected_store.clone());
let crypto = Crypto::new( let crypto = Crypto::new(veilid_config.clone(), table_store.clone());
veilid_config.clone(),
table_store.clone(),
protected_store.clone(),
);
let storage_manager = storage_manager::StorageManager::new( let storage_manager = storage_manager::StorageManager::new(
veilid_config.clone(), veilid_config.clone(),
crypto.clone(), crypto.clone(),

View File

@ -5,6 +5,8 @@ mod table_store;
pub use table_db::*; pub use table_db::*;
pub use table_store::*; pub use table_store::*;
pub mod tests;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
mod wasm; mod wasm;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]

View File

@ -163,15 +163,11 @@ impl TableStore {
self.flush().await; self.flush().await;
} }
async fn load_device_encryption_key(&self) -> EyreResult<Option<TypedSharedSecret>> { pub fn maybe_unprotect_device_encryption_key(
let dek_bytes: Option<Vec<u8>> = self &self,
.protected_store dek_bytes: &[u8],
.load_user_secret("device_encryption_key") device_encryption_key_password: &str,
.await?; ) -> EyreResult<TypedSharedSecret> {
let Some(dek_bytes) = dek_bytes else {
return Ok(None);
};
// Ensure the key is at least as long as necessary if unencrypted // Ensure the key is at least as long as necessary if unencrypted
if dek_bytes.len() < (4 + SHARED_SECRET_LENGTH) { if dek_bytes.len() < (4 + SHARED_SECRET_LENGTH) {
bail!("device encryption key is not valid"); bail!("device encryption key is not valid");
@ -184,11 +180,6 @@ impl TableStore {
bail!("unsupported cryptosystem"); 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 !device_encryption_key_password.is_empty() {
if dek_bytes.len() if dek_bytes.len()
!= (4 + SHARED_SECRET_LENGTH + vcrypto.aead_overhead() + NONCE_LENGTH) != (4 + SHARED_SECRET_LENGTH + vcrypto.aead_overhead() + NONCE_LENGTH)
@ -209,28 +200,126 @@ impl TableStore {
None, None,
) )
.wrap_err("failed to decrypt device encryption key")?; .wrap_err("failed to decrypt device encryption key")?;
return Ok(Some(TypedSharedSecret::new( return Ok(TypedSharedSecret::new(
kind, kind,
SharedSecret::try_from(unprotected_key.as_slice()) SharedSecret::try_from(unprotected_key.as_slice())
.wrap_err("invalid shared secret")?, .wrap_err("invalid shared secret")?,
))); ));
} }
Ok(Some(TypedSharedSecret::new( Ok(TypedSharedSecret::new(
kind, kind,
SharedSecret::try_from(&dek_bytes[4..])?, SharedSecret::try_from(&dek_bytes[4..])?,
))) ))
}
pub fn maybe_protect_device_encryption_key(
&self,
dek: TypedSharedSecret,
device_encryption_key_password: &str,
) -> EyreResult<Vec<u8>> {
// 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<Option<TypedSharedSecret>> {
let dek_bytes: Option<Vec<u8>> = 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( async fn save_device_encryption_key(
&self, &self,
device_encryption_key: Option<TypedSharedSecret>, device_encryption_key: Option<TypedSharedSecret>,
) -> EyreResult<()> { ) -> EyreResult<()> {
// Save the new device encryption key let Some(device_encryption_key) = device_encryption_key else {
self.protected_store // Remove the device encryption key
.save_user_secret_json("device_encryption_key", &device_encryption_key) let existed = self
.await?; .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(()) Ok(())
} }

View File

@ -0,0 +1 @@
pub mod test_table_store;

View File

@ -1,4 +1,4 @@
use super::test_veilid_config::*; use crate::tests::test_veilid_config::*;
use crate::*; use crate::*;
async fn startup() -> VeilidAPI { 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() { pub async fn test_all() {
let api = startup().await; let api = startup().await;
let crypto = api.crypto().unwrap(); let crypto = api.crypto().unwrap();
@ -215,6 +265,7 @@ pub async fn test_all() {
for ck in VALID_CRYPTO_KINDS { for ck in VALID_CRYPTO_KINDS {
let vcrypto = crypto.get(ck).unwrap(); let vcrypto = crypto.get(ck).unwrap();
test_protect_unprotect(vcrypto.clone(), ts.clone()).await;
test_delete_open_delete(ts.clone()).await; test_delete_open_delete(ts.clone()).await;
test_store_delete_load(ts.clone()).await; test_store_delete_load(ts.clone()).await;
test_rkyv(vcrypto.clone(), ts.clone()).await; test_rkyv(vcrypto.clone(), ts.clone()).await;

View File

@ -1,5 +1,4 @@
pub mod test_host_interface; pub mod test_host_interface;
pub mod test_protected_store; pub mod test_protected_store;
pub mod test_table_store;
pub mod test_veilid_config; pub mod test_veilid_config;
pub mod test_veilid_core; pub mod test_veilid_core;

View File

@ -166,7 +166,7 @@ pub fn setup_veilid_core() -> (UpdateCallback, ConfigCallback) {
fn config_callback(key: String) -> ConfigCallbackReturn { fn config_callback(key: String) -> ConfigCallbackReturn {
match key.as_str() { 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(""))), "namespace" => Ok(Box::new(String::from(""))),
"capabilities.protocol_udp" => Ok(Box::new(true)), "capabilities.protocol_udp" => Ok(Box::new(true)),
"capabilities.protocol_connect_tcp" => 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_connect_wss" => Ok(Box::new(true)),
"capabilities.protocol_accept_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.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.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.allow_insecure_fallback" => Ok(Box::new(true)),
"protected_store.always_use_insecure_storage" => Ok(Box::new(false)), "protected_store.always_use_insecure_storage" => Ok(Box::new(false)),
"protected_store.directory" => Ok(Box::new(get_protected_store_path())), "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::<String>::None))
}
"network.connection_initial_timeout_ms" => Ok(Box::new(2_000u32)), "network.connection_initial_timeout_ms" => Ok(Box::new(2_000u32)),
"network.connection_inactivity_timeout_ms" => Ok(Box::new(60_000u32)), "network.connection_inactivity_timeout_ms" => Ok(Box::new(60_000u32)),
"network.max_connections_per_ip4" => Ok(Box::new(8u32)), "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_connect_wss, true);
assert_eq!(inner.capabilities.protocol_accept_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.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.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.allow_insecure_fallback, true);
assert_eq!(inner.protected_store.always_use_insecure_storage, false); 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.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::<String>::None
);
assert_eq!(inner.network.connection_initial_timeout_ms, 2_000u32); assert_eq!(inner.network.connection_initial_timeout_ms, 2_000u32);
assert_eq!(inner.network.connection_inactivity_timeout_ms, 60_000u32); assert_eq!(inner.network.connection_inactivity_timeout_ms, 60_000u32);
assert_eq!(inner.network.max_connections_per_ip4, 8u32); assert_eq!(inner.network.max_connections_per_ip4, 8u32);

View File

@ -13,4 +13,5 @@ pub use common::*;
pub use crypto::tests::*; pub use crypto::tests::*;
pub use network_manager::tests::*; pub use network_manager::tests::*;
pub use routing_table::tests::*; pub use routing_table::tests::*;
pub use table_store::tests::*;
pub use veilid_api::tests::*; pub use veilid_api::tests::*;

View File

@ -3,6 +3,7 @@
use crate::crypto::tests::*; use crate::crypto::tests::*;
use crate::network_manager::tests::*; use crate::network_manager::tests::*;
use crate::routing_table; use crate::routing_table;
use crate::table_store::tests::*;
use crate::tests::common::*; use crate::tests::common::*;
use crate::veilid_api; use crate::veilid_api;
use crate::*; use crate::*;

View File

@ -734,6 +734,9 @@ impl VeilidConfig {
// Remove secrets // Remove secrets
safe_cfg.network.routing_table.node_id_secret = TypedSecretSet::new(); 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 safe_cfg
} }

View File

@ -1,4 +1,8 @@
//! Test suite for the Web and headless browsers. //! Test suite for the Web and headless browsers.
//XXXXXXXXXXXXXXX
//XXX DOES NOT WORK.
#![cfg(target_arch = "wasm32")] #![cfg(target_arch = "wasm32")]
extern crate alloc; extern crate alloc;