diff --git a/.vscode/launch.json b/.vscode/launch.json index 1de6a0a9..04647bda 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -106,6 +106,26 @@ }, "args": ["${selectedText}"], "cwd": "${workspaceFolder}/external/keyvaluedb/keyvaluedb-sqlite" + }, + + { + "type": "lldb", + "request": "launch", + "name": "Debug keyring unit test", + "cargo": { + "args": [ + "test", + "--no-run", + "--manifest-path", + "external/keyring-rs/Cargo.toml" + ], + "filter": { + "kind": "staticlib", + "name": "keyring" + } + }, + "args": ["${selectedText}"], + "cwd": "${workspaceFolder}/external/keyring-rs" } ] } diff --git a/Cargo.lock b/Cargo.lock index 7e544974..dffc8d25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1407,6 +1407,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs4" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cef5c93884e5cef757f63446122c2f420713c3e03f85540d09485b9415983b4a" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "funty" version = "1.1.0" @@ -1831,6 +1841,7 @@ dependencies = [ "core-foundation 0.9.2", "core-foundation-sys 0.8.3", "directories 4.0.1", + "fs4", "jni", "keychain-services", "lazy_static", @@ -1846,6 +1857,7 @@ dependencies = [ "serde_cbor", "serial_test 0.5.1", "simplelog", + "snailquote", "tempfile", "winapi", ] @@ -3372,6 +3384,16 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" +[[package]] +name = "snailquote" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec62a949bda7f15800481a711909f946e1204f2460f89210eaf7f57730f88f86" +dependencies = [ + "thiserror", + "unicode_categories", +] + [[package]] name = "socket2" version = "0.4.2" @@ -3682,6 +3704,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + [[package]] name = "universal-hash" version = "0.4.1" diff --git a/external/keyring-rs b/external/keyring-rs index dadfe40c..8dd37fa4 160000 --- a/external/keyring-rs +++ b/external/keyring-rs @@ -1 +1 @@ -Subproject commit dadfe40c70d7679a56e36e22240920c78acd0f06 +Subproject commit 8dd37fa4c629f80951cd78f8cc245317b44bdb05 diff --git a/veilid-core/src/intf/mod.rs b/veilid-core/src/intf/mod.rs index 418b5856..67774dfc 100644 --- a/veilid-core/src/intf/mod.rs +++ b/veilid-core/src/intf/mod.rs @@ -1,7 +1,5 @@ mod table_db; -mod user_secret; use crate::xx::*; -pub use user_secret::*; #[cfg(target_arch = "wasm32")] mod wasm; diff --git a/veilid-core/src/intf/native/protected_store.rs b/veilid-core/src/intf/native/protected_store.rs index b7aa75ce..50e286cf 100644 --- a/veilid-core/src/intf/native/protected_store.rs +++ b/veilid-core/src/intf/native/protected_store.rs @@ -1,57 +1,159 @@ -use cfg_if::*; -use keyring::{Keyring, KeyringError}; +use crate::xx::*; +use crate::*; +use data_encoding::BASE64URL_NOPAD; +use keyring::*; +use std::path::Path; +use std::result::Result; -fn keyring_name(namespace: &str) -> String { - if namespace.is_empty() { - "veilid".to_owned() - } else { - format!("veilid_{}", namespace) - } +pub struct ProtectedStoreInner { + keyring_manager: Option, } -fn get_keyring<'a>(krname: &'a str, key: &'a str) -> Keyring<'a> { - cfg_if! { - if #[cfg(target_os = "android")] { - let agopt = super::utils::android::ANDROID_GLOBALS.lock(); - let ag = agopt.as_ref().unwrap(); - let vm = ag.vm.attach_current_thread().unwrap().get_java_vm().unwrap(); // cmon jni, no clone for javavm - let ctx = ag.ctx.clone(); - Keyring::new("veilid", krname, key, (vm, ctx)) - } else { - Keyring::new("veilid", krname, key) +#[derive(Clone)] +pub struct ProtectedStore { + config: VeilidConfig, + inner: Arc>, +} + +impl ProtectedStore { + fn new_inner() -> ProtectedStoreInner { + ProtectedStoreInner { + keyring_manager: None, } } -} -pub async fn save_user_secret_string( - namespace: &str, - key: &str, - value: &str, -) -> Result { - let krname = keyring_name(namespace); - let kr = get_keyring(krname.as_str(), key); - let existed = kr.get_password().is_ok(); - kr.set_password(value) - .map_err(|e| format!("Failed to save user secret: {}", e))?; - Ok(existed) -} + pub fn new(config: VeilidConfig) -> Self { + Self { + config, + inner: Arc::new(Mutex::new(Self::new_inner())), + } + } -pub async fn load_user_secret_string(namespace: &str, key: &str) -> Result, String> { - let krname = keyring_name(namespace); - let kr = get_keyring(krname.as_str(), key); - match kr.get_password() { - Ok(v) => Ok(Some(v)), - Err(KeyringError::NoPasswordFound) => Ok(None), - Err(e) => Err(format!("Failed to load user secret: {}", e)), - } -} - -pub async fn remove_user_secret_string(namespace: &str, key: &str) -> Result { - let krname = keyring_name(namespace); - let kr = get_keyring(krname.as_str(), key); - match kr.delete_password() { - Ok(_) => Ok(true), - Err(KeyringError::NoPasswordFound) => Ok(false), - Err(e) => Err(format!("Failed to remove user secret: {}", e)), + pub async fn init(&self) -> Result<(), String> { + let c = self.config.get(); + let mut inner = self.inner.lock(); + if !c.protected_store.always_use_insecure_storage { + inner.keyring_manager = KeyringManager::new_secure(&c.program_name).ok(); + } + if (c.protected_store.always_use_insecure_storage + || c.protected_store.allow_insecure_fallback) + && inner.keyring_manager.is_none() + { + let insecure_fallback_directory = + Path::new(&c.protected_store.insecure_fallback_directory); + let insecure_keyring_file = insecure_fallback_directory + .to_owned() + .join("insecure_keyring"); + inner.keyring_manager = Some( + KeyringManager::new_insecure(&c.program_name, &insecure_keyring_file) + .map_err(map_to_string) + .map_err(logthru_pstore!(error))?, + ); + } + if inner.keyring_manager.is_none() { + return Err("Could not initialize the protected store.".to_owned()); + } + + Ok(()) + } + + pub async fn terminate(&self) { + *self.inner.lock() = Self::new_inner(); + } + + fn service_name(&self) -> String { + let c = self.config.get(); + if c.namespace.is_empty() { + "veilid_protected_store".to_owned() + } else { + format!("veilid_protected_store_{}", c.namespace) + } + } + + pub async fn save_user_secret_string(&self, key: &str, value: &str) -> Result { + let inner = self.inner.lock(); + inner + .keyring_manager + .as_ref() + .ok_or_else(|| "Protected store not initialized".to_owned())? + .with_keyring(&self.service_name(), key, |kr| { + let existed = kr.get_value().is_ok(); + kr.set_value(value) + .map_err(|e| format!("Failed to save user secret: {}", e))?; + Ok(existed) + }) + .map_err(map_to_string) + .map_err(logthru_pstore!()) + } + + pub async fn load_user_secret_string(&self, key: &str) -> Result, String> { + let inner = self.inner.lock(); + match inner + .keyring_manager + .as_ref() + .ok_or_else(|| "Protected store not initialized".to_owned())? + .with_keyring(&self.service_name(), key, |kr| kr.get_value()) + .map_err(logthru_pstore!()) + { + Ok(v) => Ok(Some(v)), + Err(KeyringError::NoPasswordFound) => Ok(None), + Err(e) => Err(format!("Failed to load user secret: {}", e)), + } + } + + pub async fn remove_user_secret_string(&self, key: &str) -> Result { + let inner = self.inner.lock(); + match inner + .keyring_manager + .as_ref() + .ok_or_else(|| "Protected store not initialized".to_owned())? + .with_keyring(&self.service_name(), key, |kr| kr.delete_value()) + .map_err(logthru_pstore!()) + { + Ok(_) => Ok(true), + Err(KeyringError::NoPasswordFound) => Ok(false), + Err(e) => Err(format!("Failed to remove user secret: {}", e)), + } + } + + pub async fn save_user_secret(&self, key: &str, value: &[u8]) -> Result { + let mut s = BASE64URL_NOPAD.encode(value); + s.push('!'); + + self.save_user_secret_string(key, s.as_str()).await + } + + pub async fn load_user_secret(&self, key: &str) -> Result>, String> { + let mut s = match self.load_user_secret_string(key).await? { + Some(s) => s, + None => { + return Ok(None); + } + }; + + if s.pop() != Some('!') { + return Err("User secret is not a buffer".to_owned()); + } + + let mut bytes = Vec::::new(); + let res = BASE64URL_NOPAD.decode_len(s.len()); + match res { + Ok(l) => { + bytes.resize(l, 0u8); + } + Err(_) => { + return Err("Failed to decode".to_owned()); + } + } + + let res = BASE64URL_NOPAD.decode_mut(s.as_bytes(), &mut bytes); + match res { + Ok(_) => Ok(Some(bytes)), + Err(_) => Err("Failed to decode".to_owned()), + } + } + + pub async fn remove_user_secret(&self, key: &str) -> Result { + self.remove_user_secret_string(key).await } } diff --git a/veilid-core/src/intf/native/table_store.rs b/veilid-core/src/intf/native/table_store.rs index eab40dc0..1d3e979a 100644 --- a/veilid-core/src/intf/native/table_store.rs +++ b/veilid-core/src/intf/native/table_store.rs @@ -5,25 +5,25 @@ use keyvaluedb_sqlite::*; use std::path::PathBuf; struct TableStoreInner { - config: VeilidConfig, opened: BTreeMap>>, } #[derive(Clone)] pub struct TableStore { + config: VeilidConfig, inner: Arc>, } impl TableStore { - fn new_inner(config: VeilidConfig) -> TableStoreInner { + fn new_inner() -> TableStoreInner { TableStoreInner { - config, opened: BTreeMap::new(), } } pub fn new(config: VeilidConfig) -> Self { Self { - inner: Arc::new(Mutex::new(Self::new_inner(config))), + config, + inner: Arc::new(Mutex::new(Self::new_inner())), } } @@ -45,15 +45,15 @@ impl TableStore { } } - fn get_dbpath(inner: &TableStoreInner, table: &str) -> Result { + fn get_dbpath(&self, table: &str) -> Result { if !table .chars() .all(|c| char::is_alphanumeric(c) || c == '_' || c == '-') { return Err(format!("table name '{}' is invalid", table)); } - let c = inner.config.get(); - let tablestoredir = c.tablestore.directory.clone(); + let c = self.config.get(); + let tablestoredir = c.table_store.directory.clone(); std::fs::create_dir_all(&tablestoredir) .map_err(|e| format!("failed to create tablestore path: {}", e))?; @@ -61,14 +61,14 @@ impl TableStore { Ok(dbpath) } - fn get_table_name(inner: &TableStoreInner, table: &str) -> Result { + fn get_table_name(&self, table: &str) -> Result { if !table .chars() .all(|c| char::is_alphanumeric(c) || c == '_' || c == '-') { return Err(format!("table name '{}' is invalid", table)); } - let c = inner.config.get(); + let c = self.config.get(); let namespace = c.namespace.clone(); Ok(if namespace.is_empty() { table.to_string() @@ -78,9 +78,9 @@ impl TableStore { } pub async fn open(&self, name: &str, column_count: u32) -> Result { - let mut inner = self.inner.lock(); - let table_name = Self::get_table_name(&*inner, name)?; + let table_name = self.get_table_name(name)?; + let mut inner = self.inner.lock(); if let Some(table_db_weak_inner) = inner.opened.get(&table_name) { match TableDB::try_new_from_weak_inner(table_db_weak_inner.clone()) { Some(tdb) => { @@ -92,7 +92,7 @@ impl TableStore { }; } - let dbpath = Self::get_dbpath(&inner, &table_name)?; + let dbpath = self.get_dbpath(&table_name)?; let cfg = DatabaseConfig::with_columns(column_count); let db = Database::open(&dbpath, cfg).map_err(|e| format!("failed to open tabledb: {}", e))?; @@ -110,13 +110,13 @@ impl TableStore { } pub async fn delete(&self, name: &str) -> Result { - let inner = self.inner.lock(); - let table_name = Self::get_table_name(&*inner, name)?; + let table_name = self.get_table_name(name)?; + let inner = self.inner.lock(); if inner.opened.contains_key(&table_name) { return Err("Not deleting table that is still opened".to_owned()); } - let dbpath = Self::get_dbpath(&inner, &table_name)?; + let dbpath = self.get_dbpath(&table_name)?; let ret = std::fs::remove_file(dbpath).is_ok(); Ok(ret) } diff --git a/veilid-core/src/intf/user_secret.rs b/veilid-core/src/intf/user_secret.rs deleted file mode 100644 index f7d4af34..00000000 --- a/veilid-core/src/intf/user_secret.rs +++ /dev/null @@ -1,43 +0,0 @@ -use super::*; -use data_encoding::BASE64URL_NOPAD; - -pub async fn save_user_secret(namespace: &str, key: &str, value: &[u8]) -> Result { - let mut s = BASE64URL_NOPAD.encode(value); - s.push('!'); - - save_user_secret_string(namespace, key, s.as_str()).await -} - -pub async fn load_user_secret(namespace: &str, key: &str) -> Result>, String> { - let mut s = match load_user_secret_string(namespace, key).await? { - Some(s) => s, - None => { - return Ok(None); - } - }; - - if s.pop() != Some('!') { - return Err("User secret is not a buffer".to_owned()); - } - - let mut bytes = Vec::::new(); - let res = BASE64URL_NOPAD.decode_len(s.len()); - match res { - Ok(l) => { - bytes.resize(l, 0u8); - } - Err(_) => { - return Err("Failed to decode".to_owned()); - } - } - - let res = BASE64URL_NOPAD.decode_mut(s.as_bytes(), &mut bytes); - match res { - Ok(_) => Ok(Some(bytes)), - Err(_) => Err("Failed to decode".to_owned()), - } -} - -pub async fn remove_user_secret(namespace: &str, key: &str) -> Result { - remove_user_secret_string(namespace, key).await -} diff --git a/veilid-core/src/intf/wasm/table_store.rs b/veilid-core/src/intf/wasm/table_store.rs index dbec92cc..e0f7fe95 100644 --- a/veilid-core/src/intf/wasm/table_store.rs +++ b/veilid-core/src/intf/wasm/table_store.rs @@ -4,25 +4,25 @@ use crate::*; use keyvaluedb_web::*; struct TableStoreInner { - config: VeilidConfig, opened: BTreeMap>>, } #[derive(Clone)] pub struct TableStore { + config: VeilidConfig, inner: Arc>, } impl TableStore { - fn new_inner(config: VeilidConfig) -> TableStoreInner { + fn new_inner() -> TableStoreInner { TableStoreInner { - config, opened: BTreeMap::new(), } } pub fn new(config: VeilidConfig) -> Self { Self { - inner: Arc::new(Mutex::new(Self::new_inner(config))), + config, + inner: Arc::new(Mutex::new(Self::new_inner())), } } @@ -47,7 +47,7 @@ impl TableStore { } } - fn get_table_name(inner: &TableStoreInner, table: &str) -> Result { + fn get_table_name(&self, table: &str) -> Result { if !table .chars() .all(|c| char::is_alphanumeric(c) || c == '_' || c == '-') @@ -64,9 +64,9 @@ impl TableStore { } pub async fn open(&self, name: &str, column_count: u32) -> Result { - let mut inner = self.inner.lock(); - let table_name = Self::get_table_name(&*inner, name)?; + let table_name = self.get_table_name(name)?; + let mut inner = self.inner.lock(); if let Some(table_db_weak_inner) = inner.opened.get(&table_name) { match TableDB::try_new_from_weak_inner(table_db_weak_inner.clone()) { Some(tdb) => { @@ -91,9 +91,9 @@ impl TableStore { pub async fn delete(&self, name: &str) -> Result { trace!("TableStore::delete {}", name); + let table_name = self.get_table_name(name)?; + let inner = self.inner.lock(); - let table_name = Self::get_table_name(&*inner, name)?; - if inner.opened.contains_key(&table_name) { trace!( "TableStore::delete {}: Not deleting, still open.", diff --git a/veilid-core/src/tests/common/test_host_interface.rs b/veilid-core/src/tests/common/test_host_interface.rs index 578c2f39..0fc92950 100644 --- a/veilid-core/src/tests/common/test_host_interface.rs +++ b/veilid-core/src/tests/common/test_host_interface.rs @@ -432,6 +432,10 @@ pub async fn test_split_url() { pub async fn test_protected_store() { info!("testing protected store"); + +xxx move into its own test + + let _ = intf::remove_user_secret("test", "_test_key").await; let _ = intf::remove_user_secret("test", "_test_broken").await; diff --git a/veilid-core/src/tests/common/test_veilid_config.rs b/veilid-core/src/tests/common/test_veilid_config.rs index 779df04a..121745d0 100644 --- a/veilid-core/src/tests/common/test_veilid_config.rs +++ b/veilid-core/src/tests/common/test_veilid_config.rs @@ -94,11 +94,20 @@ cfg_if! { out } - pub fn get_tablestore_path() -> String { + pub fn get_table_store_path() -> String { let mut out = get_data_dir(); std::fs::create_dir_all(&out).unwrap(); - out.push("tablestore"); + out.push("table_store"); + + out.into_os_string().into_string().unwrap() + } + + pub fn get_protected_store_path() -> String { + let mut out = get_data_dir(); + std::fs::create_dir_all(&out).unwrap(); + + out.push("protected_store"); out.into_os_string().into_string().unwrap() } @@ -149,6 +158,7 @@ pub fn setup_veilid_core() -> VeilidCoreSetup { pub fn config_callback(key: String) -> Result, String> { match key.as_str() { + "program_name" => Ok(Box::new(String::from("Veilid"))), "namespace" => Ok(Box::new(String::from(""))), "capabilities.protocol_udp" => Ok(Box::new(true)), "capabilities.protocol_connect_tcp" => Ok(Box::new(true)), @@ -157,7 +167,10 @@ pub fn config_callback(key: String) -> Result, String> { "capabilities.protocol_accept_ws" => Ok(Box::new(true)), "capabilities.protocol_connect_wss" => Ok(Box::new(true)), "capabilities.protocol_accept_wss" => Ok(Box::new(true)), - "tablestore.directory" => Ok(Box::new(get_tablestore_path())), + "tablestore.directory" => Ok(Box::new(get_table_store_path())), + "protected_store.allow_insecure_fallback" => Ok(Box::new(true)), + "protected_store.always_use_insecure_storage" => Ok(Box::new(false)), + "protected_store.insecure_fallback_directory" => Ok(Box::new(get_protected_store_path())), "network.max_connections" => Ok(Box::new(16u32)), "network.connection_initial_timeout" => Ok(Box::new(2_000_000u64)), "network.node_id" => Ok(Box::new(dht::key::DHTKey::default())), @@ -240,6 +253,7 @@ pub async fn test_config() { } } let inner = vc.get(); + assert_eq!(inner.program_name, String::from("Veilid")); assert_eq!(inner.namespace, String::from("")); assert_eq!(inner.capabilities.protocol_udp, true); assert_eq!(inner.capabilities.protocol_connect_tcp, true); @@ -248,7 +262,13 @@ pub async fn test_config() { assert_eq!(inner.capabilities.protocol_accept_ws, true); assert_eq!(inner.capabilities.protocol_connect_wss, true); assert_eq!(inner.capabilities.protocol_accept_wss, true); - assert_eq!(inner.tablestore.directory, get_tablestore_path()); + assert_eq!(inner.table_store.directory, get_table_store_path()); + assert_eq!(inner.protected_store.allow_insecure_fallback, true); + assert_eq!(inner.protected_store.always_use_insecure_storage, false); + assert_eq!( + inner.protected_store.insecure_fallback_directory, + get_protected_store_path() + ); assert_eq!(inner.network.max_connections, 16); assert_eq!(inner.network.connection_initial_timeout, 2_000_000u64); assert!(inner.network.node_id.valid); diff --git a/veilid-core/src/veilid_config.rs b/veilid-core/src/veilid_config.rs index bfa82de9..f32fb484 100644 --- a/veilid-core/src/veilid_config.rs +++ b/veilid-core/src/veilid_config.rs @@ -143,6 +143,13 @@ pub struct VeilidConfigTableStore { pub directory: String, } +#[derive(Default, Clone)] +pub struct VeilidConfigProtectedStore { + pub allow_insecure_fallback: bool, + pub always_use_insecure_storage: bool, + pub insecure_fallback_directory: String, +} + #[derive(Default, Clone)] pub struct VeilidConfigCapabilities { pub protocol_udp: bool, @@ -156,9 +163,11 @@ pub struct VeilidConfigCapabilities { #[derive(Default, Clone)] pub struct VeilidConfigInner { + pub program_name: String, pub namespace: String, pub capabilities: VeilidConfigCapabilities, - pub tablestore: VeilidConfigTableStore, + pub protected_store: VeilidConfigProtectedStore, + pub table_store: VeilidConfigTableStore, pub network: VeilidConfigNetwork, } @@ -197,6 +206,7 @@ impl VeilidConfig { { let mut inner = self.inner.write(); + get_config!(inner.program_name); get_config!(inner.namespace); get_config!(inner.capabilities.protocol_udp); get_config!(inner.capabilities.protocol_connect_tcp); @@ -205,7 +215,10 @@ impl VeilidConfig { get_config!(inner.capabilities.protocol_accept_ws); get_config!(inner.capabilities.protocol_connect_wss); get_config!(inner.capabilities.protocol_accept_wss); - get_config!(inner.tablestore.directory); + get_config!(inner.table_store.directory); + get_config!(inner.protected_store.allow_insecure_fallback); + get_config!(inner.protected_store.always_use_insecure_storage); + get_config!(inner.protected_store.insecure_fallback_directory); get_config!(inner.network.node_id); get_config!(inner.network.node_id_secret); get_config!(inner.network.max_connections); @@ -271,11 +284,6 @@ impl VeilidConfig { get_config!(inner.network.leases.max_client_signal_leases); get_config!(inner.network.leases.max_client_relay_leases); } - - // Initialize node id as early as possible because it is used - // for encryption purposes all over the program - self.init_node_id().await?; - // Validate settings self.validate().await?; @@ -292,6 +300,11 @@ impl VeilidConfig { async fn validate(&self) -> Result<(), String> { let inner = self.inner.read(); + + if inner.program_name.is_empty() { + return Err("Program name must not be empty in 'program_name'".to_owned()); + } + // if inner.network.protocol.udp.enabled { // // Validate UDP settings // } @@ -367,16 +380,16 @@ impl VeilidConfig { } // Get the node id from config if one is specified - async fn init_node_id(&self) -> Result<(), String> { + // Must be done -after- protected store startup + pub async fn init_node_id(&self, protected_store: intf::ProtectedStore) -> Result<(), String> { let mut inner = self.inner.write(); - let namespace = inner.namespace.clone(); let mut node_id = inner.network.node_id; let mut node_id_secret = inner.network.node_id_secret; // See if node id was previously stored in the protected store if !node_id.valid { debug!("pulling node id from storage"); - if let Some(s) = intf::load_user_secret_string(namespace.as_str(), "node_id").await? { + if let Some(s) = protected_store.load_user_secret_string("node_id").await? { debug!("node id found in storage"); node_id = key::DHTKey::try_decode(s.as_str())? } else { @@ -387,8 +400,9 @@ impl VeilidConfig { // See if node id secret was previously stored in the protected store if !node_id_secret.valid { debug!("pulling node id secret from storage"); - if let Some(s) = - intf::load_user_secret_string(namespace.as_str(), "node_id_secret").await? + if let Some(s) = protected_store + .load_user_secret_string("node_id_secret") + .await? { debug!("node id secret found in storage"); node_id_secret = key::DHTKeySecret::try_decode(s.as_str())? @@ -416,14 +430,12 @@ impl VeilidConfig { // info!("Node Id Secret is {}", node_id_secret.encode()); // Save the node id / secret in storage - intf::save_user_secret_string(namespace.as_str(), "node_id", node_id.encode().as_str()) + protected_store + .save_user_secret_string("node_id", node_id.encode().as_str()) + .await?; + protected_store + .save_user_secret_string("node_id_secret", node_id_secret.encode().as_str()) .await?; - intf::save_user_secret_string( - namespace.as_str(), - "node_id_secret", - node_id_secret.encode().as_str(), - ) - .await?; inner.network.node_id = node_id; inner.network.node_id_secret = node_id_secret; diff --git a/veilid-core/src/veilid_core.rs b/veilid-core/src/veilid_core.rs index e1471ffb..4c1d2e9f 100644 --- a/veilid-core/src/veilid_core.rs +++ b/veilid-core/src/veilid_core.rs @@ -33,6 +33,7 @@ pub struct VeilidCoreSetup { struct VeilidCoreInner { config: Option, + protected_store: Option, table_store: Option, crypto: Option, attachment_manager: Option, @@ -55,6 +56,7 @@ impl VeilidCore { VeilidCoreInner { config: None, table_store: None, + protected_store: None, crypto: None, attachment_manager: None, api: VeilidAPIWeak::default(), @@ -110,8 +112,17 @@ impl VeilidCore { config.init(setup.config_callback).await?; inner.config = Some(config.clone()); + // Set up protected store + trace!("VeilidCore::internal_startup init protected store"); + let protected_store = ProtectedStore::new(config.clone()); + protected_store.init().await?; + inner.protected_store = Some(protected_store.clone()); + + // Init node id from config now that protected store is set up + config.init_node_id(protected_store).await?; + // Set up tablestore - trace!("VeilidCore::internal_startup init tablestore"); + trace!("VeilidCore::internal_startup init table store"); let table_store = TableStore::new(config.clone()); table_store.init().await?; inner.table_store = Some(table_store.clone()); @@ -187,12 +198,18 @@ impl VeilidCore { inner.crypto = None; } - // Shut down tablestore + // Shut down table store if let Some(table_store) = &inner.table_store { table_store.terminate().await; inner.table_store = None; } + // Shut down protected store + if let Some(protected_store) = &inner.protected_store { + protected_store.terminate().await; + inner.protected_store = None; + } + // Shut down config if let Some(config) = &inner.config { config.terminate().await; diff --git a/veilid-core/src/xx/log_thru.rs b/veilid-core/src/xx/log_thru.rs index 5d0cc1b1..ee2ba8a9 100644 --- a/veilid-core/src/xx/log_thru.rs +++ b/veilid-core/src/xx/log_thru.rs @@ -134,7 +134,6 @@ macro_rules! logthru_rpc { logthru!($($level)? "rpc", $fmt, $($arg),+) } } - #[macro_export] macro_rules! logthru_rtab { ($($level:ident)?) => { @@ -147,6 +146,18 @@ macro_rules! logthru_rtab { logthru!($($level)? "rtab", $fmt, $($arg),+) } } +#[macro_export] +macro_rules! logthru_pstore { + ($($level:ident)?) => { + logthru!($($level)? "pstore") + }; + ($($level:ident)? $text:literal) => { + logthru!($($level)? "pstore", $text) + }; + ($($level:ident)? $fmt:literal, $($arg:expr),+) => { + logthru!($($level)? "pstore", $fmt, $($arg),+) + } +} #[macro_export] macro_rules! logthru { diff --git a/veilid-server/src/settings.rs b/veilid-server/src/settings.rs index d1e24b55..f1dbfa42 100644 --- a/veilid-server/src/settings.rs +++ b/veilid-server/src/settings.rs @@ -35,8 +35,12 @@ logging: testing: subnode_index: 0 core: - tablestore: - directory: "%TABLESTORE_DIRECTORY%" + protected_store: + allow_insecure_fallback: true, + always_use_insecure_storage: false, + insecure_fallback_directory: "%INSECURE_FALLBACK_DIRECTORY%", + table_store: + directory: "%TABLE_STORE_DIRECTORY%" network: max_connections: 16 connection_initial_timeout: 2000000 @@ -117,8 +121,12 @@ core: "#, ) .replace( - "%TABLESTORE_DIRECTORY%", + "%TABLE_STORE_DIRECTORY%", &Settings::get_default_table_store_path().to_string_lossy(), + ) + .replace( + "%INSECURE_FALLBACK_DIRECTORY%", + &Settings::get_default_protected_store_insecure_fallback_directory().to_string_lossy(), ); cfg.merge(config::File::from_str( &default_config, @@ -526,9 +534,17 @@ pub struct TableStore { pub directory: PathBuf, } +#[derive(Debug, Deserialize, Serialize)] +pub struct ProtectedStore { + pub allow_insecure_fallback: bool, + pub always_use_insecure_storage: bool, + pub insecure_fallback_directory: PathBuf, +} + #[derive(Debug, Deserialize, Serialize)] pub struct Core { - pub tablestore: TableStore, + pub protected_store: ProtectedStore, + pub table_store: TableStore, pub network: Network, } @@ -673,7 +689,21 @@ impl Settings { } else { default_config_path = PathBuf::from("./"); } - default_config_path.push("tablestore"); + default_config_path.push("table_store"); + + default_config_path + } + + pub fn get_default_protected_store_insecure_fallback_directory() -> PathBuf { + // Get default configuration file location + let mut default_config_path; + + if let Some(my_proj_dirs) = ProjectDirs::from("org", "Veilid", "Veilid") { + default_config_path = PathBuf::from(my_proj_dirs.data_local_dir()); + } else { + default_config_path = PathBuf::from("./"); + } + default_config_path.push("protected_store"); default_config_path } @@ -684,6 +714,7 @@ impl Settings { Arc::new(move |key: String| { let inner = inner.read(); let out: Result, String> = match key.as_str() { + "program_name" => Ok(Box::new("veilid-server".to_owned())), "namespace" => Ok(Box::new(if inner.testing.subnode_index == 0 { "".to_owned() } else { @@ -696,10 +727,24 @@ impl Settings { "capabilities.protocol_accept_ws" => Ok(Box::new(true)), "capabilities.protocol_connect_wss" => Ok(Box::new(true)), "capabilities.protocol_accept_wss" => Ok(Box::new(true)), - "tablestore.directory" => Ok(Box::new( + "protected_store.allow_insecure_fallback" => { + Ok(Box::new(inner.core.protected_store.allow_insecure_fallback)) + } + "protected_store.always_use_insecure_storage" => Ok(Box::new( + inner.core.protected_store.always_use_insecure_storage, + )), + "protected_store.insecure_fallback_directory" => Ok(Box::new( inner .core - .tablestore + .protected_store + .insecure_fallback_directory + .to_string_lossy() + .to_string(), + )), + "table_store.directory" => Ok(Box::new( + inner + .core + .table_store .directory .to_string_lossy() .to_string(), @@ -1027,9 +1072,15 @@ mod tests { assert_eq!(s.logging.client.level, LogLevel::Info); assert_eq!(s.testing.subnode_index, 0); assert_eq!( - s.core.tablestore.directory, + s.core.table_store.directory, Settings::get_default_table_store_path() ); + assert_eq!(s.core.protected_store.allow_insecure_fallback, true); + assert_eq!(s.core.protected_store.always_use_insecure_storage, false); + assert_eq!( + s.protected_store.insecure_fallback_directory, + Settings::get_default_protected_store_insecure_fallback_directory() + ); assert_eq!(s.core.network.max_connections, 16); assert_eq!(s.core.network.connection_initial_timeout, 2_000_000u64); assert_eq!(s.core.network.node_id, veilid_core::DHTKey::default());