91 lines
3.7 KiB
Rust
91 lines
3.7 KiB
Rust
// file: ks-wallet-demo-scenarios/tests/password_lifecycle.rs
|
|
// version: 1
|
|
|
|
//! External ordered password lifecycle scenario.
|
|
|
|
const PASSWORD_A: &str = "ks-wallet-demo-password-A";
|
|
const PASSWORD_B: &str = "ks-wallet-demo-password-B";
|
|
const CHALLENGE_B: &[u8] = b"ks-wallet-demo-scenarios password-B signing proof";
|
|
const CHALLENGE_A_RESTORED: &[u8] = b"ks-wallet-demo-scenarios restored password-A signing proof";
|
|
|
|
fn make_directory_private(path: &std::path::Path) {
|
|
#[cfg(unix)]
|
|
{
|
|
use std::os::unix::fs::PermissionsExt; // rust-rules: trait-import
|
|
std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o700)).unwrap_or_else(
|
|
|error| panic!("wallet scenario directory permissions must be set: {error}"),
|
|
);
|
|
}
|
|
}
|
|
|
|
fn password(value: &str) -> ks_wallet::WalletPassword {
|
|
return ks_wallet::WalletPassword::new(value.to_owned())
|
|
.unwrap_or_else(|error| panic!("scenario password must be accepted: {error}"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn password_rotation_runs_a_to_b_rejects_a_then_restores_a() {
|
|
let directory = tempfile::tempdir()
|
|
.unwrap_or_else(|error| panic!("temporary scenario directory must exist: {error}"));
|
|
make_directory_private(directory.path());
|
|
let manager = ks_wallet::WalletManager::new(directory.path())
|
|
.unwrap_or_else(|error| panic!("wallet manager must be created: {error}"));
|
|
let alias = ks_wallet::WalletAlias::parse("password-lifecycle")
|
|
.unwrap_or_else(|error| panic!("scenario alias must be valid: {error}"));
|
|
let created = manager
|
|
.create(alias.clone(), password(PASSWORD_A))
|
|
.await
|
|
.unwrap_or_else(|error| panic!("scenario wallet creation must succeed: {error}"));
|
|
let initial_public_key = created.public_key();
|
|
created.lock();
|
|
let rotated_to_b = ks_wallet_demo_scenarios::rotate_wallet_password(
|
|
&manager,
|
|
&alias,
|
|
password(PASSWORD_A),
|
|
password(PASSWORD_B),
|
|
)
|
|
.await
|
|
.unwrap_or_else(|error| panic!("password A to B rotation must succeed: {error}"));
|
|
assert_eq!(rotated_to_b.identity.public_key, initial_public_key);
|
|
let rejected_a = ks_wallet_demo_scenarios::expect_wallet_password_rejected(
|
|
&manager,
|
|
&alias,
|
|
password(PASSWORD_A),
|
|
)
|
|
.await
|
|
.unwrap_or_else(|error| panic!("old password A must be rejected: {error}"));
|
|
assert_eq!(rejected_a.identity.public_key, initial_public_key);
|
|
assert_eq!(rejected_a.rejection_code, "wallet_native_authentication_failed");
|
|
let signed_with_b = ks_wallet_demo_scenarios::unlock_and_sign_wallet(
|
|
&manager,
|
|
&alias,
|
|
password(PASSWORD_B),
|
|
CHALLENGE_B,
|
|
)
|
|
.await
|
|
.unwrap_or_else(|error| panic!("password B must unlock and sign: {error}"));
|
|
assert_eq!(signed_with_b.identity.public_key, initial_public_key);
|
|
assert_eq!(signed_with_b.message_length, CHALLENGE_B.len());
|
|
assert!(!signed_with_b.signature.is_empty());
|
|
let restored_to_a = ks_wallet_demo_scenarios::rotate_wallet_password(
|
|
&manager,
|
|
&alias,
|
|
password(PASSWORD_B),
|
|
password(PASSWORD_A),
|
|
)
|
|
.await
|
|
.unwrap_or_else(|error| panic!("password B to A restoration must succeed: {error}"));
|
|
assert_eq!(restored_to_a.identity.public_key, initial_public_key);
|
|
let signed_with_restored_a = ks_wallet_demo_scenarios::unlock_and_sign_wallet(
|
|
&manager,
|
|
&alias,
|
|
password(PASSWORD_A),
|
|
CHALLENGE_A_RESTORED,
|
|
)
|
|
.await
|
|
.unwrap_or_else(|error| panic!("restored password A must unlock and sign: {error}"));
|
|
assert_eq!(signed_with_restored_a.identity.public_key, initial_public_key);
|
|
assert_eq!(signed_with_restored_a.message_length, CHALLENGE_A_RESTORED.len());
|
|
assert!(!signed_with_restored_a.signature.is_empty());
|
|
}
|