v0.5.2-pre.006-fix-010
This commit is contained in:
23
ks-wallet-demo-scenarios/src/lib.rs
Normal file
23
ks-wallet-demo-scenarios/src/lib.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
// file: ks-wallet-demo-scenarios/src/lib.rs
|
||||
// version: 1
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(unreachable_pub)]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
//! Reusable integration scenarios for the public `ks-wallet` boundary.
|
||||
|
||||
mod password_lifecycle;
|
||||
|
||||
/// Safe summary returned after a password is rejected as expected.
|
||||
pub use self::password_lifecycle::WalletPasswordRejectionSummary;
|
||||
/// Safe summary returned after a password rotation preserving wallet identity.
|
||||
pub use self::password_lifecycle::WalletPasswordRotationSummary;
|
||||
/// Safe summary returned after authenticated message signing.
|
||||
pub use self::password_lifecycle::WalletSigningScenarioSummary;
|
||||
/// Requires one supplied password to fail native wallet authentication.
|
||||
pub use self::password_lifecycle::expect_wallet_password_rejected;
|
||||
/// Changes one native wallet password and verifies that its public identity is preserved.
|
||||
pub use self::password_lifecycle::rotate_wallet_password;
|
||||
/// Authenticates one native wallet and signs an explicit non-empty challenge.
|
||||
pub use self::password_lifecycle::unlock_and_sign_wallet;
|
||||
161
ks-wallet-demo-scenarios/src/password_lifecycle.rs
Normal file
161
ks-wallet-demo-scenarios/src/password_lifecycle.rs
Normal file
@@ -0,0 +1,161 @@
|
||||
// file: ks-wallet-demo-scenarios/src/password_lifecycle.rs
|
||||
// version: 1
|
||||
|
||||
//! Password lifecycle scenarios driven only through the public wallet API.
|
||||
|
||||
/// Safe summary returned after a password rotation preserving wallet identity.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct WalletPasswordRotationSummary {
|
||||
/// Persistent wallet identity retained across the password rotation.
|
||||
pub identity: ks_wallet::WalletIdentity,
|
||||
}
|
||||
|
||||
/// Safe summary returned after a password is rejected as expected.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct WalletPasswordRejectionSummary {
|
||||
/// Persistent wallet identity whose old password was rejected.
|
||||
pub identity: ks_wallet::WalletIdentity,
|
||||
/// Stable `ks-wallet` error code that proved the authentication rejection.
|
||||
pub rejection_code: std::string::String,
|
||||
}
|
||||
|
||||
/// Safe summary returned after authenticated message signing.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct WalletSigningScenarioSummary {
|
||||
/// Persistent wallet identity used by the signing capability.
|
||||
pub identity: ks_wallet::WalletIdentity,
|
||||
/// Base58 signature produced for the supplied challenge.
|
||||
pub signature: std::string::String,
|
||||
/// Number of challenge bytes signed by the wallet.
|
||||
pub message_length: usize,
|
||||
}
|
||||
|
||||
/// Changes one native wallet password and verifies that its public identity is preserved.
|
||||
pub async fn rotate_wallet_password(
|
||||
manager: &ks_wallet::WalletManager,
|
||||
alias: &ks_wallet::WalletAlias,
|
||||
current_password: ks_wallet::WalletPassword,
|
||||
replacement_password: ks_wallet::WalletPassword,
|
||||
) -> ks_core::Result<crate::WalletPasswordRotationSummary> {
|
||||
let before = match manager.lookup(alias).await {
|
||||
std::result::Result::Ok(std::option::Option::Some(handle)) => handle,
|
||||
std::result::Result::Ok(std::option::Option::None) => {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"wallet_demo_native_not_found",
|
||||
"password lifecycle scenario requires an existing native wallet",
|
||||
));
|
||||
},
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let changed = match manager.change_password(alias, current_password, replacement_password).await
|
||||
{
|
||||
std::result::Result::Ok(handle) => handle,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
if before.alias() != changed.alias() || before.public_key() != changed.public_key() {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"wallet_demo_password_rotation_identity_changed",
|
||||
"password rotation changed the native wallet public identity",
|
||||
));
|
||||
}
|
||||
return std::result::Result::Ok(crate::WalletPasswordRotationSummary {
|
||||
identity: changed.identity(),
|
||||
});
|
||||
}
|
||||
|
||||
/// Requires one supplied password to fail native wallet authentication.
|
||||
pub async fn expect_wallet_password_rejected(
|
||||
manager: &ks_wallet::WalletManager,
|
||||
alias: &ks_wallet::WalletAlias,
|
||||
rejected_password: ks_wallet::WalletPassword,
|
||||
) -> ks_core::Result<crate::WalletPasswordRejectionSummary> {
|
||||
let handle = match manager.lookup(alias).await {
|
||||
std::result::Result::Ok(std::option::Option::Some(handle)) => handle,
|
||||
std::result::Result::Ok(std::option::Option::None) => {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"wallet_demo_native_not_found",
|
||||
"password rejection scenario requires an existing native wallet",
|
||||
));
|
||||
},
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let rejection = match manager.unlock(alias, rejected_password).await {
|
||||
std::result::Result::Ok(wallet) => {
|
||||
let accepted_identity = wallet.identity();
|
||||
wallet.lock();
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"wallet_demo_rejected_password_accepted",
|
||||
format!(
|
||||
"password expected to be rejected authenticated wallet alias {} with public key {}",
|
||||
accepted_identity.alias.as_str(),
|
||||
accepted_identity.public_key
|
||||
),
|
||||
));
|
||||
},
|
||||
std::result::Result::Err(error) => error,
|
||||
};
|
||||
if rejection.code() != "wallet_native_authentication_failed" {
|
||||
return std::result::Result::Err(rejection);
|
||||
}
|
||||
return std::result::Result::Ok(crate::WalletPasswordRejectionSummary {
|
||||
identity: handle.identity(),
|
||||
rejection_code: rejection.code().to_owned(),
|
||||
});
|
||||
}
|
||||
|
||||
/// Authenticates one native wallet and signs an explicit non-empty challenge.
|
||||
pub async fn unlock_and_sign_wallet(
|
||||
manager: &ks_wallet::WalletManager,
|
||||
alias: &ks_wallet::WalletAlias,
|
||||
password: ks_wallet::WalletPassword,
|
||||
challenge: &[u8],
|
||||
) -> ks_core::Result<crate::WalletSigningScenarioSummary> {
|
||||
if challenge.is_empty() {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"wallet_demo_signing_challenge_empty",
|
||||
"wallet signing scenario requires a non-empty challenge",
|
||||
));
|
||||
}
|
||||
let expected = match manager.lookup(alias).await {
|
||||
std::result::Result::Ok(std::option::Option::Some(handle)) => handle,
|
||||
std::result::Result::Ok(std::option::Option::None) => {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"wallet_demo_native_not_found",
|
||||
"wallet signing scenario requires an existing native wallet",
|
||||
));
|
||||
},
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let wallet = match manager.unlock(alias, password).await {
|
||||
std::result::Result::Ok(wallet) => wallet,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let identity = wallet.identity();
|
||||
if &identity.alias != expected.alias() || identity.public_key.as_str() != expected.public_key()
|
||||
{
|
||||
wallet.lock();
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"wallet_demo_unlocked_identity_mismatch",
|
||||
"authenticated wallet identity differs from the inspected native wallet",
|
||||
));
|
||||
}
|
||||
let signature = match wallet.sign_message(challenge) {
|
||||
std::result::Result::Ok(signature) => signature,
|
||||
std::result::Result::Err(error) => {
|
||||
wallet.lock();
|
||||
return std::result::Result::Err(error);
|
||||
},
|
||||
};
|
||||
wallet.lock();
|
||||
if signature.is_empty() {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"wallet_demo_signature_empty",
|
||||
"authenticated wallet produced an empty signature",
|
||||
));
|
||||
}
|
||||
return std::result::Result::Ok(crate::WalletSigningScenarioSummary {
|
||||
identity,
|
||||
signature,
|
||||
message_length: challenge.len(),
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user