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 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(),

View File

@ -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")]

View File

@ -163,15 +163,11 @@ impl TableStore {
self.flush().await;
}
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);
};
pub fn maybe_unprotect_device_encryption_key(
&self,
dek_bytes: &[u8],
device_encryption_key_password: &str,
) -> EyreResult<TypedSharedSecret> {
// 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<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(
&self,
device_encryption_key: Option<TypedSharedSecret>,
) -> 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(())
}

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::*;
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;

View File

@ -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;

View File

@ -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::<String>::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::<String>::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);

View File

@ -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::*;

View File

@ -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::*;

View File

@ -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
}

View File

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