v0.5.2-pre.004
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
// file: ks-wallet/src/manager.rs
|
||||
// version: 4
|
||||
// version: 6
|
||||
|
||||
//! Multi-wallet discovery and native wallet file references.
|
||||
//! Multi-wallet discovery, authenticated opening and password rotation.
|
||||
|
||||
use solana_signer::Signer; // rust-rules: trait-import
|
||||
|
||||
/// Opaque reference to one validated native wallet file.
|
||||
///
|
||||
@@ -182,6 +184,204 @@ impl crate::WalletManager {
|
||||
) -> ks_core::Result<crate::WalletFileHandle> {
|
||||
return inspect_native_wallet_file(path.as_ref().to_path_buf()).await;
|
||||
}
|
||||
|
||||
/// Creates and atomically persists one new password-protected native wallet.
|
||||
pub async fn create(
|
||||
&self,
|
||||
alias: crate::WalletAlias,
|
||||
password: crate::WalletPassword,
|
||||
) -> ks_core::Result<crate::UnlockedWallet> {
|
||||
let path = self.wallet_path(&alias);
|
||||
let exists = match tokio::fs::try_exists(&path).await {
|
||||
std::result::Result::Ok(exists) => exists,
|
||||
std::result::Result::Err(error) => {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"wallet_file_exists_check_failed",
|
||||
error.to_string(),
|
||||
));
|
||||
},
|
||||
};
|
||||
if exists {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"wallet_native_already_exists",
|
||||
"native wallet already exists for this alias",
|
||||
));
|
||||
}
|
||||
let keypair = solana_keypair::Keypair::new();
|
||||
let public_key = keypair.pubkey();
|
||||
let keypair_bytes = zeroize::Zeroizing::new(keypair.to_bytes());
|
||||
let container = match crate::protect_native_wallet_keypair(
|
||||
alias.clone(),
|
||||
public_key,
|
||||
keypair_bytes,
|
||||
password,
|
||||
)
|
||||
.await
|
||||
{
|
||||
std::result::Result::Ok(container) => container,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
match crate::write_native_wallet_file_atomic(path, &container).await {
|
||||
std::result::Result::Ok(()) => {},
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
tracing::info!(
|
||||
target: crate::TRACING_TARGET,
|
||||
action = "create_native_wallet",
|
||||
wallet_alias = alias.as_str(),
|
||||
public_key = %public_key,
|
||||
"created password-protected native wallet"
|
||||
);
|
||||
return std::result::Result::Ok(crate::UnlockedWallet::new(alias, keypair));
|
||||
}
|
||||
|
||||
/// Authenticates and unlocks one native wallet from the configured directory.
|
||||
pub async fn unlock(
|
||||
&self,
|
||||
alias: &crate::WalletAlias,
|
||||
password: crate::WalletPassword,
|
||||
) -> ks_core::Result<crate::UnlockedWallet> {
|
||||
let path = self.wallet_path(alias);
|
||||
let exists = match tokio::fs::try_exists(&path).await {
|
||||
std::result::Result::Ok(exists) => exists,
|
||||
std::result::Result::Err(error) => {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"wallet_file_exists_check_failed",
|
||||
error.to_string(),
|
||||
));
|
||||
},
|
||||
};
|
||||
if !exists {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"wallet_native_not_found",
|
||||
"native wallet was not found for this alias",
|
||||
));
|
||||
}
|
||||
return unlock_native_wallet_at_path(
|
||||
path,
|
||||
std::option::Option::Some(alias),
|
||||
std::option::Option::None,
|
||||
password,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Authenticates an explicitly selected native wallet file outside or inside the store.
|
||||
///
|
||||
/// The opaque handle is revalidated against the file before any signing
|
||||
/// capability is returned. The file is never registered in the configured store.
|
||||
pub async fn unlock_file(
|
||||
&self,
|
||||
handle: &crate::WalletFileHandle,
|
||||
password: crate::WalletPassword,
|
||||
) -> ks_core::Result<crate::UnlockedWallet> {
|
||||
return unlock_native_wallet_at_path(
|
||||
handle.path.clone(),
|
||||
std::option::Option::Some(&handle.alias),
|
||||
std::option::Option::Some(handle),
|
||||
password,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Changes the protection password while preserving the exact keypair and public key.
|
||||
pub async fn change_password(
|
||||
&self,
|
||||
alias: &crate::WalletAlias,
|
||||
current_password: crate::WalletPassword,
|
||||
new_password: crate::WalletPassword,
|
||||
) -> ks_core::Result<crate::WalletFileHandle> {
|
||||
let path = self.wallet_path(alias);
|
||||
let container = match crate::read_native_wallet_container(&path).await {
|
||||
std::result::Result::Ok(container) => container,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
if container.alias() != alias {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"wallet_native_alias_mismatch",
|
||||
"native wallet filename alias does not match the container alias",
|
||||
));
|
||||
}
|
||||
let public_key = *container.public_key();
|
||||
let keypair = match crate::unlock_native_wallet_keypair(container, current_password).await {
|
||||
std::result::Result::Ok(keypair) => keypair,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let keypair_bytes = zeroize::Zeroizing::new(keypair.to_bytes());
|
||||
let replacement = match crate::protect_native_wallet_keypair(
|
||||
alias.clone(),
|
||||
public_key,
|
||||
keypair_bytes,
|
||||
new_password,
|
||||
)
|
||||
.await
|
||||
{
|
||||
std::result::Result::Ok(container) => container,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
match crate::replace_native_wallet_file_atomic(path.clone(), &replacement).await {
|
||||
std::result::Result::Ok(()) => {},
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
tracing::info!(
|
||||
target: crate::TRACING_TARGET,
|
||||
action = "change_native_wallet_password",
|
||||
wallet_alias = alias.as_str(),
|
||||
public_key = %public_key,
|
||||
"changed native wallet protection password"
|
||||
);
|
||||
return std::result::Result::Ok(crate::WalletFileHandle {
|
||||
alias: alias.clone(),
|
||||
public_key: public_key.to_string(),
|
||||
format_version: crate::KSWALLET_FORMAT_VERSION,
|
||||
path,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async fn unlock_native_wallet_at_path(
|
||||
path: std::path::PathBuf,
|
||||
expected_alias: std::option::Option<&crate::WalletAlias>,
|
||||
expected_handle: std::option::Option<&crate::WalletFileHandle>,
|
||||
password: crate::WalletPassword,
|
||||
) -> ks_core::Result<crate::UnlockedWallet> {
|
||||
let container = match crate::read_native_wallet_container(&path).await {
|
||||
std::result::Result::Ok(container) => container,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
if let std::option::Option::Some(alias) = expected_alias {
|
||||
if container.alias() != alias {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"wallet_native_alias_mismatch",
|
||||
"native wallet alias does not match the requested identity",
|
||||
));
|
||||
}
|
||||
}
|
||||
if let std::option::Option::Some(handle) = expected_handle {
|
||||
if handle.format_version != crate::KSWALLET_FORMAT_VERSION
|
||||
|| container.alias() != &handle.alias
|
||||
|| container.public_key().to_string() != handle.public_key
|
||||
{
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"wallet_native_handle_stale",
|
||||
"native wallet file no longer matches the inspected handle",
|
||||
));
|
||||
}
|
||||
}
|
||||
let alias = container.alias().clone();
|
||||
let public_key = *container.public_key();
|
||||
let keypair = match crate::unlock_native_wallet_keypair(container, password).await {
|
||||
std::result::Result::Ok(keypair) => keypair,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
tracing::debug!(
|
||||
target: crate::TRACING_TARGET,
|
||||
action = "unlock_native_wallet",
|
||||
wallet_alias = alias.as_str(),
|
||||
public_key = %public_key,
|
||||
"authenticated native wallet"
|
||||
);
|
||||
return std::result::Result::Ok(crate::UnlockedWallet::new(alias, keypair));
|
||||
}
|
||||
|
||||
fn has_native_wallet_extension(path: &std::path::Path) -> bool {
|
||||
|
||||
Reference in New Issue
Block a user