mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-04-27 11:56:05 +02:00
feat(stronghold): add an init function that uses argon2 (#449)
Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"stronghold": patch
|
||||
---
|
||||
|
||||
Added `Builder::with_argon2`.
|
||||
Generated
+982
-808
File diff suppressed because it is too large
Load Diff
@@ -20,6 +20,15 @@ iota-crypto = "0.23"
|
||||
hex = "0.4"
|
||||
zeroize = { version = "1", features = ["zeroize_derive"] }
|
||||
|
||||
# kdf dependencies
|
||||
rust-argon2 = { version = "1", optional = true }
|
||||
rand_chacha = { version = "0.3.1", optional = true }
|
||||
rand_core = { version = "0.6.4", features = ["getrandom"], optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.8"
|
||||
rusty-fork = "0.3"
|
||||
rusty-fork = "0.3"
|
||||
|
||||
[features]
|
||||
default = ["kdf"]
|
||||
kdf = ["dep:rust-argon2", "dep:rand_chacha", "dep:rand_core"]
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
use rand_chacha::ChaCha20Rng;
|
||||
use rand_core::{RngCore, SeedableRng};
|
||||
use std::path::Path;
|
||||
|
||||
/// NOTE: Hash supplied to Stronghold must be 32bits long.
|
||||
/// This is a current limitation of Stronghold.
|
||||
const HASH_LENGTH: usize = 32;
|
||||
|
||||
pub struct KeyDerivation {}
|
||||
|
||||
impl KeyDerivation {
|
||||
/// Will create a key from [`password`] and a generated salt.
|
||||
/// Salt will be generated to file [`salt_path`] or taken from it
|
||||
/// if file already exists
|
||||
pub fn argon2(password: &str, salt_path: &Path) -> Vec<u8> {
|
||||
let mut salt = [0u8; HASH_LENGTH];
|
||||
create_or_get_salt(&mut salt, salt_path);
|
||||
|
||||
argon2::hash_raw(password.as_bytes(), &salt, &Default::default())
|
||||
.expect("Failed to generate hash for password")
|
||||
}
|
||||
}
|
||||
|
||||
fn create_or_get_salt(salt: &mut [u8], salt_path: &Path) {
|
||||
if salt_path.is_file() {
|
||||
// Get existing salt
|
||||
let tmp = std::fs::read(salt_path).unwrap();
|
||||
salt.clone_from_slice(&tmp);
|
||||
} else {
|
||||
// Generate new salt
|
||||
let mut gen = ChaCha20Rng::from_entropy();
|
||||
gen.fill_bytes(salt);
|
||||
std::fs::write(salt_path, salt).expect("Failed to write salt for Stronghold")
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,9 @@ use tauri::{
|
||||
};
|
||||
use zeroize::Zeroize;
|
||||
|
||||
#[cfg(feature = "kdf")]
|
||||
pub mod kdf;
|
||||
|
||||
pub mod stronghold;
|
||||
|
||||
type PasswordHashFn = dyn Fn(&str) -> Vec<u8> + Send + Sync;
|
||||
@@ -394,26 +397,70 @@ fn get_client(
|
||||
}
|
||||
}
|
||||
|
||||
enum PasswordHashFunctionKind {
|
||||
#[cfg(feature = "kdf")]
|
||||
Argon2(PathBuf),
|
||||
Custom(Box<PasswordHashFn>),
|
||||
}
|
||||
|
||||
pub struct Builder {
|
||||
password_hash_function: Box<PasswordHashFn>,
|
||||
password_hash_function: PasswordHashFunctionKind,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
pub fn new<F: Fn(&str) -> Vec<u8> + Send + Sync + 'static>(password_hash_function: F) -> Self {
|
||||
Self {
|
||||
password_hash_function: Box::new(password_hash_function),
|
||||
password_hash_function: PasswordHashFunctionKind::Custom(Box::new(
|
||||
password_hash_function,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes [`Self`] with argon2 as password hash function.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// tauri::Builder::default()
|
||||
/// .setup(|app| {
|
||||
/// let salt_path = app
|
||||
/// .path_resolver()
|
||||
/// .app_local_data_dir()
|
||||
/// .expect("could not resolve app local data path")
|
||||
/// .join("salt.txt");
|
||||
/// app.handle().plugin(tauri_plugin_stronghold::Builder::with_argon2(&salt_path).build())?;
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// ```
|
||||
#[cfg(feature = "kdf")]
|
||||
pub fn with_argon2(salt_path: &std::path::Path) -> Self {
|
||||
Self {
|
||||
password_hash_function: PasswordHashFunctionKind::Argon2(salt_path.to_owned()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build<R: Runtime>(self) -> TauriPlugin<R> {
|
||||
let password_hash_function = self.password_hash_function;
|
||||
|
||||
PluginBuilder::new("stronghold")
|
||||
.setup(move |app| {
|
||||
app.manage(StrongholdCollection::default());
|
||||
app.manage(PasswordHashFunction(password_hash_function));
|
||||
Ok(())
|
||||
})
|
||||
let plugin_builder = PluginBuilder::new("stronghold").setup(move |app| {
|
||||
app.manage(StrongholdCollection::default());
|
||||
app.manage(PasswordHashFunction(match password_hash_function {
|
||||
#[cfg(feature = "kdf")]
|
||||
PasswordHashFunctionKind::Argon2(path) => {
|
||||
Box::new(move |p| kdf::KeyDerivation::argon2(p, &path))
|
||||
}
|
||||
PasswordHashFunctionKind::Custom(f) => f,
|
||||
}));
|
||||
Ok(())
|
||||
});
|
||||
|
||||
Builder::invoke_stronghold_handlers_and_build(plugin_builder)
|
||||
}
|
||||
|
||||
fn invoke_stronghold_handlers_and_build<R: Runtime>(
|
||||
builder: PluginBuilder<R>,
|
||||
) -> TauriPlugin<R> {
|
||||
builder
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
initialize,
|
||||
destroy,
|
||||
|
||||
Reference in New Issue
Block a user