checkpoint
This commit is contained in:
parent
cefbeed09a
commit
5b0bfcef48
@ -88,11 +88,7 @@ impl ServicesContext {
|
||||
|
||||
// Set up crypto
|
||||
trace!("init crypto");
|
||||
let crypto = Crypto::new(
|
||||
self.config.clone(),
|
||||
table_store.clone(),
|
||||
protected_store.clone(),
|
||||
);
|
||||
let crypto = Crypto::new(self.config.clone(), table_store.clone());
|
||||
if let Err(e) = crypto.init().await {
|
||||
error!("failed to init crypto: {}", e);
|
||||
self.shutdown().await;
|
||||
|
@ -82,7 +82,6 @@ struct CryptoInner {
|
||||
struct CryptoUnlockedInner {
|
||||
config: VeilidConfig,
|
||||
table_store: TableStore,
|
||||
protected_store: ProtectedStore,
|
||||
}
|
||||
|
||||
/// Crypto factory implementation
|
||||
@ -104,16 +103,11 @@ impl Crypto {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
config: VeilidConfig,
|
||||
table_store: TableStore,
|
||||
protected_store: ProtectedStore,
|
||||
) -> Self {
|
||||
pub fn new(config: VeilidConfig, table_store: TableStore) -> Self {
|
||||
let out = Self {
|
||||
unlocked_inner: Arc::new(CryptoUnlockedInner {
|
||||
config,
|
||||
table_store,
|
||||
protected_store,
|
||||
}),
|
||||
inner: Arc::new(Mutex::new(Self::new_inner())),
|
||||
};
|
||||
|
@ -163,19 +163,87 @@ 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);
|
||||
};
|
||||
|
||||
// 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");
|
||||
}
|
||||
|
||||
// Get cryptosystem
|
||||
let kind = FourCC::try_from(&dek_bytes[0..4]).unwrap();
|
||||
let crypto = self.inner.lock().crypto.as_ref().unwrap().clone();
|
||||
let Some(vcrypto) = crypto.get(kind) else {
|
||||
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)
|
||||
{
|
||||
bail!("password protected device encryption key is not valid");
|
||||
}
|
||||
let protected_key = &dek_bytes[4..(4 + SHARED_SECRET_LENGTH + vcrypto.aead_overhead())];
|
||||
let nonce = &dek_bytes[(4 + SHARED_SECRET_LENGTH + vcrypto.aead_overhead())..];
|
||||
|
||||
let shared_secret = vcrypto
|
||||
.derive_shared_secret(device_encryption_key_password.as_bytes(), &nonce)
|
||||
.wrap_err("failed to derive shared secret")?;
|
||||
let unprotected_key = vcrypto
|
||||
.decrypt_aead(
|
||||
&protected_key,
|
||||
&Nonce::try_from(nonce).wrap_err("invalid nonce")?,
|
||||
&shared_secret,
|
||||
None,
|
||||
)
|
||||
.wrap_err("failed to decrypt device encryption key")?;
|
||||
return Ok(Some(TypedSharedSecret::new(
|
||||
kind,
|
||||
SharedSecret::try_from(unprotected_key.as_slice())
|
||||
.wrap_err("invalid shared secret")?,
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(Some(TypedSharedSecret::new(
|
||||
kind,
|
||||
SharedSecret::try_from(&dek_bytes[4..])?,
|
||||
)))
|
||||
}
|
||||
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?;
|
||||
|
||||
xxxx
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn init(&self) -> EyreResult<()> {
|
||||
let _async_guard = self.async_lock.lock().await;
|
||||
|
||||
// Get device encryption key from protected store
|
||||
let mut encryption_key: Option<TypedSharedSecret> = self
|
||||
.protected_store
|
||||
.load_user_secret_json("device_encryption_key")
|
||||
.await?;
|
||||
|
||||
if let Some(encryption_key) = encryption_key {
|
||||
let mut device_encryption_key = self.load_device_encryption_key().await?;
|
||||
let mut device_encryption_key_changed = false;
|
||||
if let Some(device_encryption_key) = device_encryption_key {
|
||||
// If encryption in current use is not the best encryption, then run table migration
|
||||
let best_kind = best_crypto_kind();
|
||||
if encryption_key.kind != best_kind {
|
||||
if device_encryption_key.kind != best_kind {
|
||||
// XXX: Run migration. See issue #209
|
||||
}
|
||||
} else {
|
||||
@ -183,14 +251,14 @@ impl TableStore {
|
||||
let best_kind = best_crypto_kind();
|
||||
let mut shared_secret = SharedSecret::default();
|
||||
random_bytes(&mut shared_secret.bytes);
|
||||
let device_encryption_key = TypedSharedSecret::new(best_kind, shared_secret);
|
||||
|
||||
// Save the new device encryption key
|
||||
self.protected_store
|
||||
.save_user_secret_json("device_encryption_key", &device_encryption_key)
|
||||
device_encryption_key = Some(TypedSharedSecret::new(best_kind, shared_secret));
|
||||
device_encryption_key_changed = true;
|
||||
}
|
||||
|
||||
if device_encryption_key_changed {
|
||||
self.save_device_encryption_key(device_encryption_key)
|
||||
.await?;
|
||||
|
||||
encryption_key = Some(device_encryption_key);
|
||||
}
|
||||
|
||||
// Deserialize all table names
|
||||
@ -220,7 +288,7 @@ impl TableStore {
|
||||
|
||||
{
|
||||
let mut inner = self.inner.lock();
|
||||
inner.encryption_key = encryption_key;
|
||||
inner.encryption_key = device_encryption_key;
|
||||
inner.all_tables_db = Some(all_tables_db);
|
||||
}
|
||||
|
||||
|
@ -431,6 +431,8 @@ pub struct VeilidConfigProtectedStore {
|
||||
pub always_use_insecure_storage: bool,
|
||||
pub directory: String,
|
||||
pub delete: bool,
|
||||
pub device_encryption_key_password: String,
|
||||
pub new_device_encryption_key_password: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
@ -632,6 +634,8 @@ impl VeilidConfig {
|
||||
get_config!(inner.protected_store.always_use_insecure_storage);
|
||||
get_config!(inner.protected_store.directory);
|
||||
get_config!(inner.protected_store.delete);
|
||||
get_config!(inner.protected_store.device_encryption_key_password);
|
||||
get_config!(inner.protected_store.new_device_encryption_key_password);
|
||||
get_config!(inner.network.connection_initial_timeout_ms);
|
||||
get_config!(inner.network.connection_inactivity_timeout_ms);
|
||||
get_config!(inner.network.max_connections_per_ip4);
|
||||
@ -925,7 +929,7 @@ impl VeilidConfig {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
//xxx#[cfg(not(test))]
|
||||
#[cfg(not(test))]
|
||||
async fn init_node_id(
|
||||
&self,
|
||||
vcrypto: CryptoSystemVersion,
|
||||
|
@ -66,6 +66,8 @@ Future<VeilidConfig> getDefaultVeilidConfig(String programName) async {
|
||||
alwaysUseInsecureStorage: false,
|
||||
directory: "",
|
||||
delete: false,
|
||||
deviceEncryptionKey: "",
|
||||
newDeviceEncryptionKey: null,
|
||||
),
|
||||
tableStore: VeilidConfigTableStore(
|
||||
directory: kIsWeb
|
||||
|
@ -828,13 +828,16 @@ class VeilidConfigProtectedStore {
|
||||
bool alwaysUseInsecureStorage;
|
||||
String directory;
|
||||
bool delete;
|
||||
String deviceEncryptionKey;
|
||||
String? newDeviceEncryptionKey;
|
||||
|
||||
VeilidConfigProtectedStore({
|
||||
required this.allowInsecureFallback,
|
||||
VeilidConfigProtectedStore(
|
||||
{required this.allowInsecureFallback,
|
||||
required this.alwaysUseInsecureStorage,
|
||||
required this.directory,
|
||||
required this.delete,
|
||||
});
|
||||
required this.deviceEncryptionKey,
|
||||
String? newDeviceEncryptionKey});
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
@ -842,6 +845,8 @@ class VeilidConfigProtectedStore {
|
||||
'always_use_insecure_storage': alwaysUseInsecureStorage,
|
||||
'directory': directory,
|
||||
'delete': delete,
|
||||
'device_encryption_key': deviceEncryptionKey,
|
||||
'new_device_encryption_key': newDeviceEncryptionKey,
|
||||
};
|
||||
}
|
||||
|
||||
@ -849,7 +854,9 @@ class VeilidConfigProtectedStore {
|
||||
: allowInsecureFallback = json['allow_insecure_fallback'],
|
||||
alwaysUseInsecureStorage = json['always_use_insecure_storage'],
|
||||
directory = json['directory'],
|
||||
delete = json['delete'];
|
||||
delete = json['delete'],
|
||||
deviceEncryptionKey = json['device_encryption_key'],
|
||||
newDeviceEncryptionKey = json['new_device_encryption_key'];
|
||||
}
|
||||
|
||||
////////////
|
||||
|
@ -42,6 +42,19 @@ fn do_clap_matches(default_config_path: &OsStr) -> Result<clap::ArgMatches, clap
|
||||
.multiple_occurrences(true)
|
||||
.help("Specify configuration value to set (key in dot format, value in json format), eg: logging.api.enabled=true")
|
||||
)
|
||||
.arg(
|
||||
Arg::new("password")
|
||||
.short('p')
|
||||
.long("password")
|
||||
.takes_value(true)
|
||||
.help("Specify password to use to protect the device encryption key")
|
||||
)
|
||||
.arg(
|
||||
Arg::new("new-password")
|
||||
.long("new-password")
|
||||
.takes_value(true)
|
||||
.help("Change password used to protect the device encryption key. Device storage will be migrated.")
|
||||
)
|
||||
.arg(
|
||||
Arg::new("attach")
|
||||
.long("attach")
|
||||
|
@ -6,13 +6,14 @@ use serde_derive::*;
|
||||
use std::ffi::OsStr;
|
||||
use std::net::SocketAddr;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use sysinfo::{DiskExt, SystemExt};
|
||||
use url::Url;
|
||||
use veilid_core::tools::*;
|
||||
use veilid_core::*;
|
||||
|
||||
pub fn load_default_config() -> EyreResult<config::Config> {
|
||||
let default_config = String::from(
|
||||
let mut default_config = String::from(
|
||||
r#"---
|
||||
daemon:
|
||||
enabled: false
|
||||
@ -49,6 +50,8 @@ core:
|
||||
always_use_insecure_storage: true
|
||||
directory: '%DIRECTORY%'
|
||||
delete: false
|
||||
device_encryption_key_password: '%DEVICE_ENCRYPTION_KEY_PASSWORD%'
|
||||
new_device_encryption_key_password: %NEW_DEVICE_ENCRYPTION_KEY_PASSWORD%
|
||||
table_store:
|
||||
directory: '%TABLE_STORE_DIRECTORY%'
|
||||
delete: false
|
||||
@ -176,6 +179,30 @@ core:
|
||||
"%REMOTE_MAX_SUBKEY_CACHE_MEMORY_MB%",
|
||||
&Settings::get_default_remote_max_subkey_cache_memory_mb().to_string(),
|
||||
);
|
||||
|
||||
let dek_password = if let Some(dek_password) = std::env::var_os("DEK_PASSWORD") {
|
||||
dek_password
|
||||
.to_str()
|
||||
.ok_or_else(|| eyre!("DEK_PASSWORD is not valid unicode"))?
|
||||
.to_owned()
|
||||
} else {
|
||||
"".to_owned()
|
||||
};
|
||||
default_config = default_config.replace("%DEVICE_ENCRYPTION_KEY_PASSWORD%", &dek_password);
|
||||
|
||||
let new_dek_password = if let Some(new_dek_password) = std::env::var_os("NEW_DEK_PASSWORD") {
|
||||
format!(
|
||||
"'{}'",
|
||||
new_dek_password
|
||||
.to_str()
|
||||
.ok_or_else(|| eyre!("NEW_DEK_PASSWORD is not valid unicode"))?
|
||||
)
|
||||
} else {
|
||||
"null".to_owned()
|
||||
};
|
||||
default_config =
|
||||
default_config.replace("%NEW_DEVICE_ENCRYPTION_KEY_PASSWORD%", &new_dek_password);
|
||||
|
||||
config::Config::builder()
|
||||
.add_source(config::File::from_str(
|
||||
&default_config,
|
||||
@ -588,6 +615,8 @@ pub struct ProtectedStore {
|
||||
pub always_use_insecure_storage: bool,
|
||||
pub directory: PathBuf,
|
||||
pub delete: bool,
|
||||
pub device_encryption_key_password: String,
|
||||
pub new_device_encryption_key_password: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
@ -937,6 +966,17 @@ impl Settings {
|
||||
);
|
||||
set_config_value!(inner.core.protected_store.directory, value);
|
||||
set_config_value!(inner.core.protected_store.delete, value);
|
||||
set_config_value!(
|
||||
inner.core.protected_store.device_encryption_key_password,
|
||||
value
|
||||
);
|
||||
set_config_value!(
|
||||
inner
|
||||
.core
|
||||
.protected_store
|
||||
.new_device_encryption_key_password,
|
||||
value
|
||||
);
|
||||
set_config_value!(inner.core.table_store.directory, value);
|
||||
set_config_value!(inner.core.table_store.delete, value);
|
||||
set_config_value!(inner.core.block_store.directory, value);
|
||||
@ -1071,6 +1111,20 @@ impl Settings {
|
||||
.to_string(),
|
||||
)),
|
||||
"protected_store.delete" => Ok(Box::new(inner.core.protected_store.delete)),
|
||||
"protected_store.device_encryption_key_password" => Ok(Box::new(
|
||||
inner
|
||||
.core
|
||||
.protected_store
|
||||
.device_encryption_key_password
|
||||
.clone(),
|
||||
)),
|
||||
"protected_store.new_device_encryption_key_password" => Ok(Box::new(
|
||||
inner
|
||||
.core
|
||||
.protected_store
|
||||
.new_device_encryption_key_password
|
||||
.clone(),
|
||||
)),
|
||||
|
||||
"table_store.directory" => Ok(Box::new(
|
||||
inner
|
||||
@ -1505,6 +1559,11 @@ mod tests {
|
||||
Settings::get_default_protected_store_directory()
|
||||
);
|
||||
assert_eq!(s.core.protected_store.delete, false);
|
||||
assert_eq!(s.core.protected_store.device_encryption_key_password, "");
|
||||
assert_eq!(
|
||||
s.core.protected_store.new_device_encryption_key_password,
|
||||
None
|
||||
);
|
||||
|
||||
assert_eq!(s.core.network.connection_initial_timeout_ms, 2_000u32);
|
||||
assert_eq!(s.core.network.connection_inactivity_timeout_ms, 60_000u32);
|
||||
|
Loading…
Reference in New Issue
Block a user