v0.2.5-pre.010.fix.001

This commit is contained in:
2026-08-20 11:17:12 +02:00
parent 5bf9651038
commit c0b131bf6f
97 changed files with 2277 additions and 655 deletions

View File

@@ -1,11 +1,12 @@
// file: crates/ksp-wallet-lib/src/wallet.rs
// version: 7
// version: 8
//! In-memory native Wallet V1 create/open orchestration.
use ed25519_dalek::Signer; // rust-rules: derive-import
use zeroize::Zeroize; // rust-rules: derive-import
use ed25519_dalek::Signer; // rust-rules: trait-import
use zeroize::Zeroize; // rust-rules: trait-import
/// Crate-internal `OwnerStateV1` state shared across the owning crate.
pub(crate) struct OwnerStateV1 {
envelope: crate::KspWalletEnvelopeV1,
owner_root: std::option::Option<crate::SecretKeyV1>,
@@ -16,6 +17,7 @@ pub(crate) struct OwnerStateV1 {
}
impl OwnerStateV1 {
/// Creates a new `OwnerStateV1` value.
pub(crate) fn new(
envelope: crate::KspWalletEnvelopeV1,
owner_root: crate::SecretKeyV1,
@@ -34,15 +36,18 @@ impl OwnerStateV1 {
};
}
/// Returns the current envelope.
pub(crate) const fn envelope(&self) -> &crate::KspWalletEnvelopeV1 {
return &self.envelope;
}
/// Applies envelope.
pub(crate) fn apply_envelope(&mut self, envelope: crate::KspWalletEnvelopeV1) {
self.envelope = envelope;
return;
}
/// Applies strong view state.
pub(crate) fn apply_strong_view_state(&mut self, envelope: crate::KspWalletEnvelopeV1, metadata_key: crate::SecretKeyV1) {
self.envelope = envelope;
let previous = self.metadata_key.replace(metadata_key);
@@ -50,6 +55,7 @@ impl OwnerStateV1 {
return;
}
/// Executes the crate-internal export transfer operation for `OwnerStateV1`.
pub(crate) fn export_transfer(&self, format: crate::WalletTransferFormat) -> ksp_core_lib::Result<std::vec::Vec<u8>> {
let keypair = match self.solana_keypair.as_ref() {
std::option::Option::Some(value) => value,
@@ -74,6 +80,7 @@ impl OwnerStateV1 {
};
}
/// Executes the crate-internal sign message operation for `OwnerStateV1`.
pub(crate) fn sign_message(&self, message: &[u8]) -> ksp_core_lib::Result<[u8; crate::KSPWALLET_SOLANA_SIGNATURE_BYTES]> {
let keypair = match self.solana_keypair.as_ref() {
std::option::Option::Some(value) => value,
@@ -89,6 +96,7 @@ impl OwnerStateV1 {
return std::result::Result::Ok(signature);
}
/// Executes the crate-internal stage metadata payload operation for `OwnerStateV1`.
pub(crate) fn stage_metadata_payload(&self, payload: crate::MetadataPayloadV1) -> ksp_core_lib::Result<(crate::KspWalletEnvelopeV1, crate::WalletInfo)> {
let metadata_key = match self.metadata_key.as_ref() {
std::option::Option::Some(value) => value,
@@ -138,6 +146,7 @@ impl OwnerStateV1 {
return std::result::Result::Ok((envelope, payload.into_info(crate::WalletCapability::Owner)));
}
/// Executes the crate-internal stage owner password rotation operation for `OwnerStateV1`.
pub(crate) async fn stage_owner_password_rotation(&self, new_password: crate::OwnerPassword) -> ksp_core_lib::Result<crate::KspWalletEnvelopeV1> {
let owner_root = match self.owner_root.as_ref() {
std::option::Option::Some(value) => value,
@@ -172,6 +181,7 @@ impl OwnerStateV1 {
return verify_and_return(envelope);
}
/// Executes the crate-internal stage view password rotation operation for `OwnerStateV1`.
pub(crate) async fn stage_view_password_rotation(&self, new_password: crate::ViewPassword) -> ksp_core_lib::Result<crate::KspWalletEnvelopeV1> {
let metadata_key = match self.metadata_key.as_ref() {
std::option::Option::Some(value) => value,
@@ -201,6 +211,7 @@ impl OwnerStateV1 {
return verify_and_return(envelope);
}
/// Executes the crate-internal stage disable view operation for `OwnerStateV1`.
pub(crate) fn stage_disable_view(&self) -> ksp_core_lib::Result<(crate::KspWalletEnvelopeV1, crate::SecretKeyV1)> {
if !self.envelope.view_descriptor().enabled() {
return std::result::Result::Err(capability_error("Wallet VIEW capability is already disabled"));
@@ -208,6 +219,7 @@ impl OwnerStateV1 {
return self.stage_strong_view_change(std::option::Option::None);
}
/// Executes the crate-internal stage recreate view operation for `OwnerStateV1`.
pub(crate) async fn stage_recreate_view(
&self,
new_password: crate::ViewPassword,
@@ -260,7 +272,6 @@ impl OwnerStateV1 {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::result::Result::Err(crypto_operation_error()),
};
let current_plaintext_result = crate::decrypt_bytes(
current_metadata_key,
self.envelope.metadata().nonce(),
@@ -290,9 +301,8 @@ impl OwnerStateV1 {
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let metadata = crate::WalletEncryptedCompartmentV1::new(crate::WalletCompartmentKindV1::Metadata, metadata_nonce, metadata_ciphertext);
let mut admin_secret = admin_signing_key.to_bytes();
let mut owner_control_plaintext = crate::payload::encode_owner_control_payload(&admin_secret, &new_metadata_key, secret_key);
let mut owner_control_plaintext = crate::encode_owner_control_payload(&admin_secret, &new_metadata_key, secret_key);
admin_secret.zeroize();
let owner_control_nonce = match crate::random_nonce() {
std::result::Result::Ok(value) => value,
@@ -314,7 +324,6 @@ impl OwnerStateV1 {
};
let owner_control =
crate::WalletEncryptedCompartmentV1::new(crate::WalletCompartmentKindV1::OwnerControl, owner_control_nonce, owner_control_ciphertext);
let view_descriptor = match view_slot.as_ref() {
std::option::Option::Some(slot) => crate::WalletViewDescriptorV1::enabled_for_slot(*slot.slot_id()),
std::option::Option::None => crate::WalletViewDescriptorV1::disabled(),
@@ -352,25 +361,30 @@ impl std::ops::Drop for OwnerStateV1 {
}
}
/// Crate-internal `ViewStateV1` state shared across the owning crate.
pub(crate) struct ViewStateV1 {
envelope: crate::KspWalletEnvelopeV1,
metadata_key: std::option::Option<crate::SecretKeyV1>,
}
impl ViewStateV1 {
/// Creates a new `ViewStateV1` value.
pub(crate) fn new(envelope: crate::KspWalletEnvelopeV1, metadata_key: crate::SecretKeyV1) -> Self {
return Self { envelope, metadata_key: std::option::Option::Some(metadata_key) };
}
/// Returns the current envelope.
pub(crate) const fn envelope(&self) -> &crate::KspWalletEnvelopeV1 {
return &self.envelope;
}
/// Applies envelope.
pub(crate) fn apply_envelope(&mut self, envelope: crate::KspWalletEnvelopeV1) {
self.envelope = envelope;
return;
}
/// Executes the crate-internal stage view password rotation operation for `ViewStateV1`.
pub(crate) async fn stage_view_password_rotation(&self, new_password: crate::ViewPassword) -> ksp_core_lib::Result<crate::KspWalletEnvelopeV1> {
let metadata_key = match self.metadata_key.as_ref() {
std::option::Option::Some(value) => value,
@@ -526,7 +540,7 @@ pub async fn open_wallet_owner_v1(source: &[u8], password: crate::OwnerPassword)
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::result::Result::Err(owner_unlock_error()),
};
let control_result = crate::payload::decode_owner_control_payload(owner_control_plaintext.as_slice());
let control_result = crate::decode_owner_control_payload(owner_control_plaintext.as_slice());
owner_control_plaintext.zeroize();
let control = match control_result {
std::result::Result::Ok(value) => value,
@@ -538,7 +552,6 @@ pub async fn open_wallet_owner_v1(source: &[u8], password: crate::OwnerPassword)
if admin_signing_key.verifying_key().to_bytes() != *envelope.owner_auth_public_key() {
return std::result::Result::Err(authentication_error());
}
let metadata_plaintext_result = crate::decrypt_bytes(
&metadata_key,
envelope.metadata().nonce(),
@@ -555,7 +568,6 @@ pub async fn open_wallet_owner_v1(source: &[u8], password: crate::OwnerPassword)
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let secret_plaintext_result = crate::decrypt_bytes(
&secret_key,
envelope.secret().nonce(),
@@ -590,7 +602,6 @@ pub async fn open_wallet_owner_v1(source: &[u8], password: crate::OwnerPassword)
if &secret_pubkey != metadata_payload.pubkey() {
return std::result::Result::Err(key_material_error());
}
let info = metadata_payload.into_info(crate::WalletCapability::Owner);
let state = OwnerStateV1::new(envelope, owner_root, metadata_key, secret_key, admin_signing_key, solana_keypair);
ksp_logging_lib::debug!(
@@ -616,6 +627,7 @@ pub fn inspect_locked_wallet_v1(source: &[u8]) -> ksp_core_lib::Result<crate::Lo
return std::result::Result::Ok(crate::LockedWalletInfo::new(envelope.view_descriptor().enabled()));
}
/// Executes the crate-internal create wallet v1 from keypair operation for the owning module.
pub(crate) async fn create_wallet_v1_from_keypair(
owner_password: crate::OwnerPassword,
view_password: std::option::Option<crate::ViewPassword>,
@@ -634,14 +646,12 @@ pub(crate) async fn create_wallet_v1_from_keypair(
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let mut admin_secret = match crate::random_bytes::<{ crate::SECRET_KEY_BYTES }>() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let admin_signing_key = ed25519_dalek::SigningKey::from_bytes(&admin_secret);
let owner_auth_public_key = admin_signing_key.verifying_key().to_bytes();
let mut solana_keypair_bytes = solana_keypair.to_bytes();
let pubkey_result = pubkey_from_keypair_bytes(&solana_keypair_bytes);
let pubkey = match pubkey_result {
@@ -652,8 +662,7 @@ pub(crate) async fn create_wallet_v1_from_keypair(
return std::result::Result::Err(error);
},
};
let metadata_encoded_result = crate::payload::encode_initial_metadata_payload(pubkey, metadata);
let metadata_encoded_result = crate::encode_initial_metadata_payload(pubkey, metadata);
let (mut metadata_plaintext, metadata_payload) = match metadata_encoded_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
@@ -662,9 +671,8 @@ pub(crate) async fn create_wallet_v1_from_keypair(
return std::result::Result::Err(error);
},
};
let mut owner_control_plaintext = crate::payload::encode_owner_control_payload(&admin_secret, &metadata_key, &secret_key);
let mut owner_control_plaintext = crate::encode_owner_control_payload(&admin_secret, &metadata_key, &secret_key);
admin_secret.zeroize();
let owner_slot_id = match crate::random_bytes::<{ crate::KSPWALLET_V1_SLOT_ID_BYTES }>() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
@@ -684,7 +692,6 @@ pub(crate) async fn create_wallet_v1_from_keypair(
return cleanup_create_error(error, &mut owner_control_plaintext, &mut metadata_plaintext, &mut solana_keypair_bytes);
},
};
let view_material_result = prepare_view_creation(view_password);
let view_material = match view_material_result {
std::result::Result::Ok(value) => value,
@@ -692,7 +699,6 @@ pub(crate) async fn create_wallet_v1_from_keypair(
return cleanup_create_error(error, &mut owner_control_plaintext, &mut metadata_plaintext, &mut solana_keypair_bytes);
},
};
let owner_control_nonce = match crate::random_nonce() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
@@ -711,7 +717,6 @@ pub(crate) async fn create_wallet_v1_from_keypair(
return cleanup_create_error(error, &mut owner_control_plaintext, &mut metadata_plaintext, &mut solana_keypair_bytes);
},
};
let provisional = provisional_envelope(
owner_auth_public_key,
owner_slot_id,
@@ -722,7 +727,6 @@ pub(crate) async fn create_wallet_v1_from_keypair(
metadata_nonce,
secret_nonce,
);
let owner_derived_result = derive_owner_password_key_async(owner_password, owner_kdf.clone()).await;
let owner_derived = match owner_derived_result {
std::result::Result::Ok(value) => value,
@@ -737,7 +741,6 @@ pub(crate) async fn create_wallet_v1_from_keypair(
return cleanup_create_error(error, &mut owner_control_plaintext, &mut metadata_plaintext, &mut solana_keypair_bytes);
},
};
let view_slot_result = seal_view_slot(&provisional, view_material, &metadata_key).await;
let view_slot = match view_slot_result {
std::result::Result::Ok(value) => value,
@@ -745,7 +748,6 @@ pub(crate) async fn create_wallet_v1_from_keypair(
return cleanup_create_error(error, &mut owner_control_plaintext, &mut metadata_plaintext, &mut solana_keypair_bytes);
},
};
let owner_control_ciphertext_result = crate::encrypt_bytes(
&owner_root,
&owner_control_nonce,
@@ -759,7 +761,6 @@ pub(crate) async fn create_wallet_v1_from_keypair(
},
};
owner_control_plaintext.zeroize();
let metadata_ciphertext_result = crate::encrypt_bytes(
&metadata_key,
&metadata_nonce,
@@ -773,7 +774,6 @@ pub(crate) async fn create_wallet_v1_from_keypair(
},
};
metadata_plaintext.zeroize();
let secret_ciphertext_result = crate::encrypt_bytes(
&secret_key,
&secret_nonce,
@@ -787,7 +787,6 @@ pub(crate) async fn create_wallet_v1_from_keypair(
},
};
solana_keypair_bytes.zeroize();
let owner_slot =
crate::WalletKeySlotV1::new(owner_slot_id, crate::WalletKeySlotRoleV1::Owner, owner_kdf, crate::WalletKeyWrapV1::new(owner_wrap_nonce, owner_wrapped));
let view_descriptor = match view_slot.as_ref() {
@@ -822,7 +821,6 @@ pub(crate) async fn create_wallet_v1_from_keypair(
if let std::result::Result::Err(error) = verify_result {
return std::result::Result::Err(error);
}
let info = metadata_payload.into_info(crate::WalletCapability::Owner);
let state = OwnerStateV1::new(envelope, owner_root, metadata_key, secret_key, admin_signing_key, solana_keypair);
ksp_logging_lib::debug!(
@@ -835,6 +833,7 @@ pub(crate) async fn create_wallet_v1_from_keypair(
return std::result::Result::Ok(crate::WalletOwner::from_unlocked(info, state));
}
/// Verifies state signature.
pub(crate) fn verify_state_signature(envelope: &crate::KspWalletEnvelopeV1) -> ksp_core_lib::Result<()> {
let verifying_key_result = ed25519_dalek::VerifyingKey::from_bytes(envelope.owner_auth_public_key());
let verifying_key = match verifying_key_result {