chore: update documentation

This commit is contained in:
Lucas Nogueira committed 2026-09-22 11:30:47 -03:00
1 parent d869c162a7
commit a87a3c7d44
104 files changed
+4875 -222

No files matched your search

+16 -3
View File
@@ -2,6 +2,10 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
//! Key derivation used to turn the user password into the key that encrypts a snapshot.
//!
//! Only available when the **kdf** Cargo feature is enabled, which is the case by default.
use rand_chacha::ChaCha20Rng;
use rand_core::{RngCore, SeedableRng};
use std::path::Path;
@@ -10,12 +14,21 @@ use std::path::Path;
/// This is a current limitation of Stronghold.
const HASH_LENGTH: usize = 32;
/// Password hashing functions that can be used as the key derivation function of
/// [`Builder::new`](crate::Builder::new).
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
/// Hashes `password` with Argon2 using the salt stored in `salt_path`, returning the
/// 32 bytes key used to encrypt a snapshot.
///
/// The salt is read from `salt_path` when that file already exists, otherwise a new
/// random salt is generated and written to it.
///
/// # Panics
///
/// Panics when the salt file cannot be read or written, when its contents are not
/// 32 bytes long, or when hashing the password fails.
pub fn argon2(password: &str, salt_path: &Path) -> Vec<u8> {
let mut salt = [0u8; HASH_LENGTH];
create_or_get_salt(&mut salt, salt_path);
+37 -4
View File
@@ -108,8 +108,13 @@ impl From<Slip10DeriveInputDto> for Slip10DeriveInput {
}
}
/// The type of a key pair handled by the plugin procedures.
///
/// Deserialized from the strings `ed25519` and `x25519`, ignoring case.
pub enum KeyType {
/// The Ed25519 signature scheme.
Ed25519,
/// The X25519 key exchange scheme.
X25519,
}
@@ -423,11 +428,34 @@ enum PasswordHashFunctionKind {
Custom(Box<PasswordHashFn>),
}
/// Builder for the stronghold plugin.
///
/// It defines how the password sent by the frontend is hashed into the key that
/// encrypts the snapshot file.
pub struct Builder {
password_hash_function: PasswordHashFunctionKind,
}
impl Builder {
/// Initializes [`Self`] with a custom password hash function.
///
/// The function is called with the password sent by the frontend and must return the
/// key used to encrypt the snapshot, which must be 32 bytes long.
///
/// # Examples
///
/// ```rust
/// fn init<R: tauri::Runtime>(builder: tauri::Builder<R>) -> tauri::Builder<R> {
/// builder.plugin(
/// tauri_plugin_stronghold::Builder::new(|_password| {
/// // hash the password with a secure algorithm such as argon2 or blake2b
/// // and return the resulting 32 bytes hash
/// unimplemented!()
/// })
/// .build(),
/// )
/// }
/// ```
pub fn new<F: Fn(&str) -> Vec<u8> + Send + Sync + 'static>(password_hash_function: F) -> Self {
Self {
password_hash_function: PasswordHashFunctionKind::Custom(Box::new(
@@ -442,16 +470,19 @@ impl Builder {
///
/// ```rust
/// use tauri::Manager;
/// tauri::Builder::default()
/// .setup(|app| {
///
/// fn init<R: tauri::Runtime>(builder: tauri::Builder<R>) -> tauri::Builder<R> {
/// builder.setup(|app| {
/// let salt_path = app
/// .path()
/// .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())?;
/// 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 {
@@ -460,6 +491,8 @@ impl Builder {
}
}
/// Builds the plugin, registering the password hash function and the commands used
/// by the JavaScript guest bindings.
pub fn build<R: Runtime>(self) -> TauriPlugin<R> {
let password_hash_function = self.password_hash_function;
+32
View File
@@ -2,22 +2,35 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
//! Types to load, modify and persist an [IOTA Stronghold](https://github.com/iotaledger/stronghold.rs)
//! snapshot file.
use std::{convert::TryFrom, ops::Deref, path::Path};
use iota_stronghold::{KeyProvider, SnapshotPath};
use serde::{Serialize, Serializer};
use zeroize::Zeroizing;
/// Alias for a [`std::result::Result`] with the error type [`Error`].
pub type Result<T> = std::result::Result<T, Error>;
/// Errors returned by the stronghold plugin.
///
/// Serialized as the error message string when returned to the frontend.
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// No stronghold was initialized for the given snapshot path.
#[error("stronghold not initialized")]
StrongholdNotInitialized,
/// An error from the underlying Stronghold client, e.g. when loading a snapshot
/// with the wrong password or when addressing a client that does not exist.
#[error(transparent)]
Stronghold(#[from] iota_stronghold::ClientError),
/// An error from the Stronghold secure memory implementation, e.g. when the
/// password hash is not a key size Stronghold accepts.
#[error(transparent)]
Memory(#[from] iota_stronghold::MemoryError),
/// A Stronghold procedure (key generation, key derivation, signing, ...) failed.
#[error(transparent)]
Procedure(#[from] iota_stronghold::procedures::ProcedureError),
}
@@ -31,6 +44,10 @@ impl Serialize for Error {
}
}
/// A Stronghold instance bound to a snapshot file and to the key it is encrypted with.
///
/// Dereferences to the underlying [`iota_stronghold::Stronghold`], so all of its client
/// and vault operations are available on this type.
pub struct Stronghold {
inner: iota_stronghold::Stronghold,
path: SnapshotPath,
@@ -38,6 +55,18 @@ pub struct Stronghold {
}
impl Stronghold {
/// Creates a Stronghold instance for the snapshot file at `path`, encrypted with
/// `password` as the key.
///
/// When the file already exists its snapshot is loaded, which requires `password` to
/// be the key it was encrypted with. Otherwise an empty instance is created and
/// nothing is written to disk until [`Self::save`] is called.
///
/// # Errors
///
/// Returns [`Error::Memory`] when `password` is not a key size Stronghold accepts
/// (it must be 32 bytes long) and [`Error::Stronghold`] when an existing snapshot
/// cannot be loaded with it.
pub fn new<P: AsRef<Path>>(path: P, password: Vec<u8>) -> Result<Self> {
let path = SnapshotPath::from_path(path);
let stronghold = iota_stronghold::Stronghold::default();
@@ -52,12 +81,15 @@ impl Stronghold {
})
}
/// Writes the state of all clients to the snapshot file, encrypted with the key
/// this instance was created with.
pub fn save(&self) -> Result<()> {
self.inner
.commit_with_keyprovider(&self.path, &self.keyprovider)?;
Ok(())
}
/// Returns a reference to the underlying [`iota_stronghold::Stronghold`] instance.
pub fn inner(&self) -> &iota_stronghold::Stronghold {
&self.inner
}