v0.2.5-pre.002
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
# file: Cargo.toml
|
||||
# version: 142
|
||||
# version: 143
|
||||
|
||||
[workspace]
|
||||
resolver = "3"
|
||||
members = ["crates/ksp-app-config-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-logging-lib", "crates/ksp-onchain-transport-lib"]
|
||||
members = ["crates/ksp-app-config-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-logging-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-wallet-lib"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.2.5-pre.1"
|
||||
version = "0.2.5-pre.2"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"
|
||||
@@ -29,6 +29,7 @@ tauri = { version = "^2.11" }
|
||||
tauri-build = { version = "^2.6" }
|
||||
tauri-plugin-tracing = { version = "^0.3" }
|
||||
ts-rs = { version = "^12.0" }
|
||||
zeroize = { version = "^1.9" }
|
||||
|
||||
[workspace.lints.rust]
|
||||
missing_docs = "warn"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: ROADMAP.md -->
|
||||
<!-- version: 46 -->
|
||||
<!-- version: 47 -->
|
||||
|
||||
# Roadmap KSP
|
||||
|
||||
@@ -49,7 +49,7 @@ Le roadmap décrit les objectifs à atteindre et les grandes étapes prévues. U
|
||||
- [X] `0.2.2` — HTTP Accounts + Tokens + Cluster : 22 wrappers typés (5 Accounts + 5 Tokens + 12 Cluster), canaries de complétude 52+14, smoke Devnet Transport pur et smoke historique Config -> Transport validés, documentation durable et prompt `0.2.3` publiés stables.
|
||||
- [X] `0.2.3` — HTTP Transactions stable : 11/11 wrappers typés publiés, classification `8 Read / 2 WriteSubmission / 1 Simulation`, no-resend ambigu prouvé pour les write submissions, `KSP-TRANSPORT-007` réaudité conforme sur les 37 wrappers HTTP courants, graphes Cargo et deux smokes Devnet validés ; `0.2.4` reprend les 15 Blocks/Economics restants.
|
||||
- [X] `0.2.4` — HTTP Blocks + Economics stable : 15/15 wrappers `V0_2_4` publiés, surface typed complète à 52/52 méthodes courantes, 14/14 historiques conservées, réaudit SIMD/inventaire final et `KSP-TRANSPORT-007` global validés ; deux smokes Devnet passés avant publication.
|
||||
- [/] `0.2.5` — Wallet foundation ouverte par `pre.001` : threat model offline et héritage bot2/bot3 réaudités, `.kspwallet` V1 entièrement autonome sans facteur externe, design content-keys/key-slots VIEW/OWNER indépendant, metadata VIEW read-only niveau B via autorité Ed25519 de format distincte, slot VIEW auto-rotatable uniquement pour son propre password, OWNER capable de rotation OWNER/VIEW et d’administration complète, keypair V1 immuable, spec multi-langages séparée + vecteurs publics exigés, paramètres KDF à benchmarker avant freeze, persistence/signature/rotations/import-export répartis jusqu’à `pre.010` ; `WalletPolicy` reste exclu.
|
||||
- [/] `0.2.5` — Wallet foundation : `pre.001` fixe le threat model et le format V1 autonome ; `pre.002` crée `ksp-wallet-lib` avec capabilities VIEW/OWNER, projections metadata protégées, wrappers password redacted/zeroized, erreurs Wallet, target `ksp-wallet-lib` via `ksp-logging-lib` et canaries de frontières. `Pubkey` est consommée uniquement via `ksp-core-lib`, sans dépendance directe `solana-pubkey`; Config/Transport/ExecutionPolicy/Store/Tauri restent hors Wallet. Le wire/crypto/persistence/signature/rotations/import-export restent répartis jusqu’à `pre.010` ; `WalletPolicy` reste exclu.
|
||||
- [ ] `0.2.6` — Introduire `ksp-app-wallet-desk` utilisant Config composite + Wallet + transport HTTP, notamment pour afficher l'identité et le solde d'un wallet.
|
||||
- [ ] `0.2.7` — Étendre `ksp-onchain-transport-lib` au WebSocket Solana standard complet ; permettre plusieurs sessions sur une même URL sans imposer encore un pool automatique complexe.
|
||||
- [ ] `0.2.8` — Ajouter Helius LaserStream WebSocket comme extension du moteur WebSocket standard, sans duplication de client.
|
||||
|
||||
16
crates/ksp-wallet-lib/Cargo.toml
Normal file
16
crates/ksp-wallet-lib/Cargo.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
# file: crates/ksp-wallet-lib/Cargo.toml
|
||||
# version: 1
|
||||
|
||||
[package]
|
||||
name = "ksp-wallet-lib"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
ksp-core-lib = { path = "../ksp-core-lib" }
|
||||
ksp-logging-lib = { path = "../ksp-logging-lib" }
|
||||
zeroize.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
15
crates/ksp-wallet-lib/src/capability.rs
Normal file
15
crates/ksp-wallet-lib/src/capability.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
// file: crates/ksp-wallet-lib/src/capability.rs
|
||||
// version: 1
|
||||
|
||||
/// Authorized capability represented by an unlocked Wallet handle.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum WalletCapability {
|
||||
/// Metadata access plus rotation of the current VIEW password only.
|
||||
View,
|
||||
/// Full Wallet ownership including signing and administration.
|
||||
Owner,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "../unit_tests/capability.rs"]
|
||||
mod tests;
|
||||
7
crates/ksp-wallet-lib/src/constants.rs
Normal file
7
crates/ksp-wallet-lib/src/constants.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
// file: crates/ksp-wallet-lib/src/constants.rs
|
||||
// version: 1
|
||||
|
||||
//! Wallet-owned constants.
|
||||
|
||||
/// Owning tracing target for events emitted by the Wallet crate.
|
||||
pub(crate) const TRACING_TARGET: &str = "ksp-wallet-lib";
|
||||
29
crates/ksp-wallet-lib/src/error.rs
Normal file
29
crates/ksp-wallet-lib/src/error.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
// file: crates/ksp-wallet-lib/src/error.rs
|
||||
// version: 1
|
||||
|
||||
/// Error code used when a native Wallet structure is invalid.
|
||||
pub const ERROR_CODE_FORMAT_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("wallet", "format_invalid");
|
||||
/// Error code used when a native Wallet format version is unsupported.
|
||||
pub const ERROR_CODE_FORMAT_VERSION_UNSUPPORTED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("wallet", "format_version_unsupported");
|
||||
/// Error code used when serialized cryptographic parameters are invalid or unsupported.
|
||||
pub const ERROR_CODE_CRYPTO_PARAMETERS_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("wallet", "crypto_parameters_invalid");
|
||||
/// Error code used when an authenticated Wallet structure cannot be verified.
|
||||
pub const ERROR_CODE_AUTHENTICATION_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("wallet", "authentication_failed");
|
||||
/// Error code used when a VIEW unlock attempt fails without exposing a finer cryptographic oracle.
|
||||
pub const ERROR_CODE_VIEW_UNLOCK_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("wallet", "view_unlock_failed");
|
||||
/// Error code used when an OWNER unlock attempt fails without exposing a finer cryptographic oracle.
|
||||
pub const ERROR_CODE_OWNER_UNLOCK_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("wallet", "owner_unlock_failed");
|
||||
/// Error code used when an operation requires a capability that the caller does not own.
|
||||
pub const ERROR_CODE_CAPABILITY_INSUFFICIENT: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("wallet", "capability_insufficient");
|
||||
/// Error code used when Wallet filesystem I/O fails.
|
||||
pub const ERROR_CODE_IO_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("wallet", "io_failed");
|
||||
/// Error code used when a no-clobber create or import destination already exists.
|
||||
pub const ERROR_CODE_DESTINATION_EXISTS: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("wallet", "destination_exists");
|
||||
/// Error code used when an atomic Wallet persistence operation cannot publish a valid replacement.
|
||||
pub const ERROR_CODE_ATOMIC_PERSISTENCE_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("wallet", "atomic_persistence_failed");
|
||||
/// Error code used when an import/export transfer format is unsupported.
|
||||
pub const ERROR_CODE_TRANSFER_FORMAT_UNSUPPORTED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("wallet", "transfer_format_unsupported");
|
||||
/// Error code used when imported or decoded key material is invalid.
|
||||
pub const ERROR_CODE_KEY_MATERIAL_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("wallet", "key_material_invalid");
|
||||
/// Error code used when a Wallet signing operation fails.
|
||||
pub const ERROR_CODE_SIGNATURE_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("wallet", "signature_failed");
|
||||
66
crates/ksp-wallet-lib/src/lib.rs
Normal file
66
crates/ksp-wallet-lib/src/lib.rs
Normal file
@@ -0,0 +1,66 @@
|
||||
// file: crates/ksp-wallet-lib/src/lib.rs
|
||||
// version: 1
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
//! Autonomous KSP Wallet foundation.
|
||||
//!
|
||||
//! `ksp-wallet-lib` owns the native `.kspwallet` domain, VIEW/OWNER capability model, protected metadata projection, password-secret wrappers and Wallet
|
||||
//! error contract. The `0.2.5-pre.002` foundation deliberately contains no file codec, KDF/AEAD implementation, Solana secret material, persistence,
|
||||
//! network access, Config integration or execution policy. Public keys are consumed exclusively through the [`ksp_core_lib::Pubkey`] re-export owned by
|
||||
//! KSP Core, and behavioral observability uses only `ksp-logging-lib` with the explicit crate target defined in `src/constants.rs`.
|
||||
|
||||
mod capability;
|
||||
mod constants;
|
||||
mod error;
|
||||
mod metadata;
|
||||
mod owner;
|
||||
mod password;
|
||||
mod view;
|
||||
|
||||
/// Authorized capability represented by an unlocked Wallet handle.
|
||||
pub use self::capability::WalletCapability;
|
||||
/// Error code used when an atomic Wallet persistence operation cannot publish a valid replacement.
|
||||
pub use self::error::ERROR_CODE_ATOMIC_PERSISTENCE_FAILED;
|
||||
/// Error code used when an authenticated Wallet structure cannot be verified.
|
||||
pub use self::error::ERROR_CODE_AUTHENTICATION_FAILED;
|
||||
/// Error code used when an operation requires a capability that the caller does not own.
|
||||
pub use self::error::ERROR_CODE_CAPABILITY_INSUFFICIENT;
|
||||
/// Error code used when serialized cryptographic parameters are invalid or unsupported.
|
||||
pub use self::error::ERROR_CODE_CRYPTO_PARAMETERS_INVALID;
|
||||
/// Error code used when a no-clobber create or import destination already exists.
|
||||
pub use self::error::ERROR_CODE_DESTINATION_EXISTS;
|
||||
/// Error code used when a native Wallet structure is invalid.
|
||||
pub use self::error::ERROR_CODE_FORMAT_INVALID;
|
||||
/// Error code used when a native Wallet format version is unsupported.
|
||||
pub use self::error::ERROR_CODE_FORMAT_VERSION_UNSUPPORTED;
|
||||
/// Error code used when Wallet filesystem I/O fails.
|
||||
pub use self::error::ERROR_CODE_IO_FAILED;
|
||||
/// Error code used when imported or decoded key material is invalid.
|
||||
pub use self::error::ERROR_CODE_KEY_MATERIAL_INVALID;
|
||||
/// Error code used when an OWNER unlock attempt fails without exposing a finer cryptographic oracle.
|
||||
pub use self::error::ERROR_CODE_OWNER_UNLOCK_FAILED;
|
||||
/// Error code used when a Wallet signing operation fails.
|
||||
pub use self::error::ERROR_CODE_SIGNATURE_FAILED;
|
||||
/// Error code used when an import/export transfer format is unsupported.
|
||||
pub use self::error::ERROR_CODE_TRANSFER_FORMAT_UNSUPPORTED;
|
||||
/// Error code used when a VIEW unlock attempt fails without exposing a finer cryptographic oracle.
|
||||
pub use self::error::ERROR_CODE_VIEW_UNLOCK_FAILED;
|
||||
/// Minimal non-secret information available while a native Wallet remains locked.
|
||||
pub use self::metadata::LockedWalletInfo;
|
||||
/// Safe metadata projection produced after VIEW or OWNER authorization.
|
||||
pub use self::metadata::WalletInfo;
|
||||
/// One protected Wallet note exposed only after authorization.
|
||||
pub use self::metadata::WalletNote;
|
||||
/// Authorized OWNER capability handle.
|
||||
pub use self::owner::WalletOwner;
|
||||
/// Owned OWNER password material with redacted diagnostics and drop-time zeroization.
|
||||
pub use self::password::OwnerPassword;
|
||||
/// Owned VIEW password material with redacted diagnostics and drop-time zeroization.
|
||||
pub use self::password::ViewPassword;
|
||||
/// Authorized VIEW capability handle.
|
||||
pub use self::view::WalletView;
|
||||
|
||||
/// Wallet-owned tracing target used by the KSP logging facade.
|
||||
pub(crate) use self::constants::TRACING_TARGET;
|
||||
109
crates/ksp-wallet-lib/src/metadata.rs
Normal file
109
crates/ksp-wallet-lib/src/metadata.rs
Normal file
@@ -0,0 +1,109 @@
|
||||
// file: crates/ksp-wallet-lib/src/metadata.rs
|
||||
// version: 1
|
||||
|
||||
/// One protected Wallet note exposed only after VIEW or OWNER authorization.
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct WalletNote {
|
||||
id: std::string::String,
|
||||
text: std::string::String,
|
||||
}
|
||||
|
||||
impl WalletNote {
|
||||
/// Returns the stable note identifier used by Wallet administration operations.
|
||||
#[must_use]
|
||||
pub fn id(&self) -> &str {
|
||||
return self.id.as_str();
|
||||
}
|
||||
|
||||
/// Returns the protected note text after authorization.
|
||||
#[must_use]
|
||||
pub fn text(&self) -> &str {
|
||||
return self.text.as_str();
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for WalletNote {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter.debug_struct("WalletNote").field("id", &"<redacted>").field("text", &"<redacted>").finish();
|
||||
}
|
||||
}
|
||||
|
||||
/// Safe metadata projection produced after VIEW or OWNER authorization.
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct WalletInfo {
|
||||
format_version: u32,
|
||||
capability: crate::WalletCapability,
|
||||
pubkey: ksp_core_lib::Pubkey,
|
||||
alias: std::option::Option<std::string::String>,
|
||||
notes: std::vec::Vec<crate::WalletNote>,
|
||||
}
|
||||
|
||||
impl WalletInfo {
|
||||
/// Returns the native Wallet format version parsed for this projection.
|
||||
#[must_use]
|
||||
pub const fn format_version(&self) -> u32 {
|
||||
return self.format_version;
|
||||
}
|
||||
|
||||
/// Returns the authorization capability that produced this projection.
|
||||
#[must_use]
|
||||
pub const fn capability(&self) -> crate::WalletCapability {
|
||||
return self.capability;
|
||||
}
|
||||
|
||||
/// Returns the authorized Solana public key using the KSP Core re-export.
|
||||
#[must_use]
|
||||
pub const fn pubkey(&self) -> &ksp_core_lib::Pubkey {
|
||||
return &self.pubkey;
|
||||
}
|
||||
|
||||
/// Returns the protected internal alias after authorization, when present.
|
||||
#[must_use]
|
||||
pub fn alias(&self) -> std::option::Option<&str> {
|
||||
return self.alias.as_deref();
|
||||
}
|
||||
|
||||
/// Returns the protected Wallet notes after authorization.
|
||||
#[must_use]
|
||||
pub fn notes(&self) -> &[crate::WalletNote] {
|
||||
return self.notes.as_slice();
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for WalletInfo {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter
|
||||
.debug_struct("WalletInfo")
|
||||
.field("format_version", &self.format_version)
|
||||
.field("capability", &self.capability)
|
||||
.field("pubkey", &self.pubkey)
|
||||
.field("alias", &self.alias.as_ref().map(|_| "<redacted>"))
|
||||
.field("note_count", &self.notes.len())
|
||||
.finish();
|
||||
}
|
||||
}
|
||||
|
||||
/// Minimal non-secret information available while a native Wallet remains locked.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub struct LockedWalletInfo {
|
||||
format_version: u32,
|
||||
view_enabled: bool,
|
||||
}
|
||||
|
||||
impl LockedWalletInfo {
|
||||
/// Returns the native Wallet format version.
|
||||
#[must_use]
|
||||
pub const fn format_version(&self) -> u32 {
|
||||
return self.format_version;
|
||||
}
|
||||
|
||||
/// Reports whether a VIEW capability slot exists without exposing its protected contents.
|
||||
#[must_use]
|
||||
pub const fn view_enabled(&self) -> bool {
|
||||
return self.view_enabled;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "../unit_tests/metadata.rs"]
|
||||
mod tests;
|
||||
54
crates/ksp-wallet-lib/src/owner.rs
Normal file
54
crates/ksp-wallet-lib/src/owner.rs
Normal file
@@ -0,0 +1,54 @@
|
||||
// file: crates/ksp-wallet-lib/src/owner.rs
|
||||
// version: 1
|
||||
|
||||
/// Authorized OWNER capability handle.
|
||||
///
|
||||
/// OWNER exposes all authorized metadata and will receive signing plus Wallet administration operations in later `0.2.5` tranches without exposing a
|
||||
/// general-purpose secret-key getter.
|
||||
pub struct WalletOwner {
|
||||
info: crate::WalletInfo,
|
||||
}
|
||||
|
||||
impl WalletOwner {
|
||||
/// Returns the authorization capability represented by this handle.
|
||||
#[must_use]
|
||||
pub const fn capability(&self) -> crate::WalletCapability {
|
||||
return crate::WalletCapability::Owner;
|
||||
}
|
||||
|
||||
/// Returns the authorized Solana public key.
|
||||
#[must_use]
|
||||
pub const fn pubkey(&self) -> &ksp_core_lib::Pubkey {
|
||||
return self.info.pubkey();
|
||||
}
|
||||
|
||||
/// Returns the protected internal alias, when present.
|
||||
#[must_use]
|
||||
pub fn alias(&self) -> std::option::Option<&str> {
|
||||
return self.info.alias();
|
||||
}
|
||||
|
||||
/// Returns the protected notes.
|
||||
#[must_use]
|
||||
pub fn notes(&self) -> &[crate::WalletNote] {
|
||||
return self.info.notes();
|
||||
}
|
||||
|
||||
/// Returns the complete safe metadata projection for this OWNER capability.
|
||||
#[must_use]
|
||||
pub fn info(&self) -> &crate::WalletInfo {
|
||||
ksp_logging_lib::trace!(
|
||||
target: crate::TRACING_TARGET,
|
||||
operation = "wallet_info_projection",
|
||||
capability = "owner",
|
||||
"wallet metadata projection requested"
|
||||
);
|
||||
return &self.info;
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for WalletOwner {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter.debug_struct("WalletOwner").field("info", &self.info).finish();
|
||||
}
|
||||
}
|
||||
74
crates/ksp-wallet-lib/src/password.rs
Normal file
74
crates/ksp-wallet-lib/src/password.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
// file: crates/ksp-wallet-lib/src/password.rs
|
||||
// version: 1
|
||||
|
||||
/// Owned VIEW password material.
|
||||
///
|
||||
/// The value is never exposed through `Debug` or `Display`, is intentionally non-`Clone`, and is zeroized on drop. Consumers should move an existing
|
||||
/// `String` into this type instead of keeping unnecessary clear-text copies.
|
||||
///
|
||||
/// ```compile_fail
|
||||
/// let password = ksp_wallet_lib::ViewPassword::new(std::string::String::from("test-only"));
|
||||
/// let duplicated = password.clone();
|
||||
/// let _ = duplicated;
|
||||
/// ```
|
||||
pub struct ViewPassword {
|
||||
value: std::string::String,
|
||||
}
|
||||
|
||||
impl ViewPassword {
|
||||
/// Takes ownership of VIEW password material.
|
||||
#[must_use]
|
||||
pub fn new(value: std::string::String) -> Self {
|
||||
return Self { value };
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for ViewPassword {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter.write_str("ViewPassword(<redacted>)");
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Drop for ViewPassword {
|
||||
fn drop(&mut self) {
|
||||
zeroize::Zeroize::zeroize(&mut self.value);
|
||||
}
|
||||
}
|
||||
|
||||
/// Owned OWNER password material.
|
||||
///
|
||||
/// The value is never exposed through `Debug` or `Display`, is intentionally non-`Clone`, and is zeroized on drop. Consumers should move an existing
|
||||
/// `String` into this type instead of keeping unnecessary clear-text copies.
|
||||
///
|
||||
/// ```compile_fail
|
||||
/// let password = ksp_wallet_lib::OwnerPassword::new(std::string::String::from("test-only"));
|
||||
/// let duplicated = password.clone();
|
||||
/// let _ = duplicated;
|
||||
/// ```
|
||||
pub struct OwnerPassword {
|
||||
value: std::string::String,
|
||||
}
|
||||
|
||||
impl OwnerPassword {
|
||||
/// Takes ownership of OWNER password material.
|
||||
#[must_use]
|
||||
pub fn new(value: std::string::String) -> Self {
|
||||
return Self { value };
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for OwnerPassword {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter.write_str("OwnerPassword(<redacted>)");
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Drop for OwnerPassword {
|
||||
fn drop(&mut self) {
|
||||
zeroize::Zeroize::zeroize(&mut self.value);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "../unit_tests/password.rs"]
|
||||
mod tests;
|
||||
54
crates/ksp-wallet-lib/src/view.rs
Normal file
54
crates/ksp-wallet-lib/src/view.rs
Normal file
@@ -0,0 +1,54 @@
|
||||
// file: crates/ksp-wallet-lib/src/view.rs
|
||||
// version: 1
|
||||
|
||||
/// Authorized VIEW capability handle.
|
||||
///
|
||||
/// VIEW exposes protected metadata and, in later `0.2.5` tranches, will expose only self-rotation of its VIEW password. It never owns the Solana secret or
|
||||
/// OWNER administration material.
|
||||
pub struct WalletView {
|
||||
info: crate::WalletInfo,
|
||||
}
|
||||
|
||||
impl WalletView {
|
||||
/// Returns the authorization capability represented by this handle.
|
||||
#[must_use]
|
||||
pub const fn capability(&self) -> crate::WalletCapability {
|
||||
return crate::WalletCapability::View;
|
||||
}
|
||||
|
||||
/// Returns the authorized Solana public key.
|
||||
#[must_use]
|
||||
pub const fn pubkey(&self) -> &ksp_core_lib::Pubkey {
|
||||
return self.info.pubkey();
|
||||
}
|
||||
|
||||
/// Returns the protected internal alias, when present.
|
||||
#[must_use]
|
||||
pub fn alias(&self) -> std::option::Option<&str> {
|
||||
return self.info.alias();
|
||||
}
|
||||
|
||||
/// Returns the protected notes.
|
||||
#[must_use]
|
||||
pub fn notes(&self) -> &[crate::WalletNote] {
|
||||
return self.info.notes();
|
||||
}
|
||||
|
||||
/// Returns the complete safe metadata projection for this VIEW capability.
|
||||
#[must_use]
|
||||
pub fn info(&self) -> &crate::WalletInfo {
|
||||
ksp_logging_lib::trace!(
|
||||
target: crate::TRACING_TARGET,
|
||||
operation = "wallet_info_projection",
|
||||
capability = "view",
|
||||
"wallet metadata projection requested"
|
||||
);
|
||||
return &self.info;
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for WalletView {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter.debug_struct("WalletView").field("info", &self.info).finish();
|
||||
}
|
||||
}
|
||||
59
crates/ksp-wallet-lib/tests/dependency_boundary.rs
Normal file
59
crates/ksp-wallet-lib/tests/dependency_boundary.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
// file: crates/ksp-wallet-lib/tests/dependency_boundary.rs
|
||||
// version: 1
|
||||
|
||||
//! Wallet-specific dependency and ownership canaries.
|
||||
|
||||
fn crate_root() -> std::path::PathBuf {
|
||||
return std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
}
|
||||
|
||||
fn rust_source_files(directory: &std::path::Path) -> std::vec::Vec<std::path::PathBuf> {
|
||||
let entries = std::fs::read_dir(directory).expect("Wallet source directory must be readable during integration tests");
|
||||
let mut files = std::vec::Vec::new();
|
||||
for entry in entries {
|
||||
let entry = entry.expect("Wallet source directory entry must be readable");
|
||||
let path = entry.path();
|
||||
if path.is_dir() {
|
||||
files.extend(rust_source_files(path.as_path()));
|
||||
} else if path.extension() == std::option::Option::Some(std::ffi::OsStr::new("rs")) {
|
||||
files.push(path);
|
||||
}
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wallet_manifest_preserves_dependency_firewall() {
|
||||
let manifest = std::fs::read_to_string(crate_root().join("Cargo.toml")).expect("Wallet manifest must be readable during integration tests");
|
||||
assert!(manifest.contains("ksp-core-lib"));
|
||||
assert!(manifest.contains("ksp-logging-lib"));
|
||||
assert!(manifest.contains("zeroize.workspace = true"));
|
||||
for forbidden in [
|
||||
"ksp-config-lib",
|
||||
"ksp-onchain-transport-lib",
|
||||
"ksp-execution-policy-api",
|
||||
"ksp-store-api",
|
||||
"ksp-store-lib",
|
||||
"tauri",
|
||||
"tracing =",
|
||||
"solana-pubkey",
|
||||
] {
|
||||
assert!(!manifest.contains(forbidden), "forbidden direct Wallet dependency detected: {forbidden}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wallet_sources_use_core_pubkey_logging_facade_and_no_environment() {
|
||||
let source_files = rust_source_files(crate_root().join("src").as_path());
|
||||
let mut all_source = std::string::String::new();
|
||||
for source_file in source_files {
|
||||
let source = std::fs::read_to_string(source_file.as_path()).expect("Wallet Rust source must be readable during integration tests");
|
||||
all_source.push_str(source.as_str());
|
||||
}
|
||||
assert!(all_source.contains("ksp_core_lib::Pubkey"));
|
||||
assert!(all_source.contains("ksp_logging_lib::trace!"));
|
||||
assert!(all_source.contains("pub(crate) const TRACING_TARGET: &str = \"ksp-wallet-lib\";"));
|
||||
assert!(!all_source.contains("solana_pubkey::"));
|
||||
assert!(!all_source.contains("std::env::"));
|
||||
assert!(!all_source.contains("tracing::"));
|
||||
}
|
||||
61
crates/ksp-wallet-lib/tests/public_api.rs
Normal file
61
crates/ksp-wallet-lib/tests/public_api.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
// file: crates/ksp-wallet-lib/tests/public_api.rs
|
||||
// version: 1
|
||||
|
||||
//! Public API canaries for the Wallet foundation.
|
||||
|
||||
fn accepts_view_handle(_: std::option::Option<&ksp_wallet_lib::WalletView>) {}
|
||||
|
||||
fn accepts_owner_handle(_: std::option::Option<&ksp_wallet_lib::WalletOwner>) {}
|
||||
|
||||
#[test]
|
||||
fn capability_and_handle_types_are_available_from_crate_root() {
|
||||
assert_eq!(ksp_wallet_lib::WalletCapability::View, ksp_wallet_lib::WalletCapability::View);
|
||||
assert_ne!(ksp_wallet_lib::WalletCapability::View, ksp_wallet_lib::WalletCapability::Owner);
|
||||
accepts_view_handle(std::option::Option::None);
|
||||
accepts_owner_handle(std::option::Option::None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn password_types_redact_public_debug_output() {
|
||||
let view = ksp_wallet_lib::ViewPassword::new(std::string::String::from("PUBLIC-VIEW-CANARY"));
|
||||
let owner = ksp_wallet_lib::OwnerPassword::new(std::string::String::from("PUBLIC-OWNER-CANARY"));
|
||||
let view_debug = format!("{view:?}");
|
||||
let owner_debug = format!("{owner:?}");
|
||||
assert!(!view_debug.contains("PUBLIC-VIEW-CANARY"));
|
||||
assert!(!owner_debug.contains("PUBLIC-OWNER-CANARY"));
|
||||
assert!(std::mem::needs_drop::<ksp_wallet_lib::ViewPassword>());
|
||||
assert!(std::mem::needs_drop::<ksp_wallet_lib::OwnerPassword>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn metadata_projection_methods_use_core_pubkey_contract() {
|
||||
let pubkey_method: fn(&ksp_wallet_lib::WalletInfo) -> &ksp_core_lib::Pubkey = ksp_wallet_lib::WalletInfo::pubkey;
|
||||
let view_pubkey_method: fn(&ksp_wallet_lib::WalletView) -> &ksp_core_lib::Pubkey = ksp_wallet_lib::WalletView::pubkey;
|
||||
let owner_pubkey_method: fn(&ksp_wallet_lib::WalletOwner) -> &ksp_core_lib::Pubkey = ksp_wallet_lib::WalletOwner::pubkey;
|
||||
let _ = pubkey_method;
|
||||
let _ = view_pubkey_method;
|
||||
let _ = owner_pubkey_method;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wallet_error_codes_are_available_from_crate_root() {
|
||||
let codes = [
|
||||
ksp_wallet_lib::ERROR_CODE_FORMAT_INVALID,
|
||||
ksp_wallet_lib::ERROR_CODE_FORMAT_VERSION_UNSUPPORTED,
|
||||
ksp_wallet_lib::ERROR_CODE_CRYPTO_PARAMETERS_INVALID,
|
||||
ksp_wallet_lib::ERROR_CODE_AUTHENTICATION_FAILED,
|
||||
ksp_wallet_lib::ERROR_CODE_VIEW_UNLOCK_FAILED,
|
||||
ksp_wallet_lib::ERROR_CODE_OWNER_UNLOCK_FAILED,
|
||||
ksp_wallet_lib::ERROR_CODE_CAPABILITY_INSUFFICIENT,
|
||||
ksp_wallet_lib::ERROR_CODE_IO_FAILED,
|
||||
ksp_wallet_lib::ERROR_CODE_DESTINATION_EXISTS,
|
||||
ksp_wallet_lib::ERROR_CODE_ATOMIC_PERSISTENCE_FAILED,
|
||||
ksp_wallet_lib::ERROR_CODE_TRANSFER_FORMAT_UNSUPPORTED,
|
||||
ksp_wallet_lib::ERROR_CODE_KEY_MATERIAL_INVALID,
|
||||
ksp_wallet_lib::ERROR_CODE_SIGNATURE_FAILED,
|
||||
];
|
||||
assert_eq!(codes.len(), 13);
|
||||
for code in codes {
|
||||
assert_eq!(code.domain(), "wallet");
|
||||
}
|
||||
}
|
||||
9
crates/ksp-wallet-lib/unit_tests/capability.rs
Normal file
9
crates/ksp-wallet-lib/unit_tests/capability.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
// file: crates/ksp-wallet-lib/unit_tests/capability.rs
|
||||
// version: 1
|
||||
|
||||
#[test]
|
||||
fn capability_variants_are_distinct_and_stable() {
|
||||
assert_ne!(super::WalletCapability::View, super::WalletCapability::Owner);
|
||||
assert_eq!(format!("{:?}", super::WalletCapability::View), "View");
|
||||
assert_eq!(format!("{:?}", super::WalletCapability::Owner), "Owner");
|
||||
}
|
||||
56
crates/ksp-wallet-lib/unit_tests/metadata.rs
Normal file
56
crates/ksp-wallet-lib/unit_tests/metadata.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
// file: crates/ksp-wallet-lib/unit_tests/metadata.rs
|
||||
// version: 1
|
||||
|
||||
fn test_pubkey() -> ksp_core_lib::Pubkey {
|
||||
return ksp_core_lib::PRGIDPK_SOLANA_SYSTEM;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn authorized_info_exposes_metadata_after_authorization() {
|
||||
let note = super::WalletNote { id: std::string::String::from("purpose"), text: std::string::String::from("devnet test wallet") };
|
||||
let info = super::WalletInfo {
|
||||
format_version: 1,
|
||||
capability: crate::WalletCapability::View,
|
||||
pubkey: test_pubkey(),
|
||||
alias: std::option::Option::Some(std::string::String::from("devnet-owner")),
|
||||
notes: std::vec![note],
|
||||
};
|
||||
assert_eq!(info.format_version(), 1);
|
||||
assert_eq!(info.capability(), crate::WalletCapability::View);
|
||||
assert_eq!(info.pubkey(), &test_pubkey());
|
||||
assert_eq!(info.alias(), std::option::Option::Some("devnet-owner"));
|
||||
assert_eq!(info.notes()[0].id(), "purpose");
|
||||
assert_eq!(info.notes()[0].text(), "devnet test wallet");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn authorized_info_debug_redacts_alias_and_note_text() {
|
||||
let note = super::WalletNote {
|
||||
id: std::string::String::from("NOTE-ID-CANARY"),
|
||||
text: std::string::String::from("NOTE-SECRET-CANARY"),
|
||||
};
|
||||
let info = super::WalletInfo {
|
||||
format_version: 1,
|
||||
capability: crate::WalletCapability::Owner,
|
||||
pubkey: test_pubkey(),
|
||||
alias: std::option::Option::Some(std::string::String::from("ALIAS-SECRET-CANARY")),
|
||||
notes: std::vec![note],
|
||||
};
|
||||
let rendered = format!("{info:?}");
|
||||
assert!(rendered.contains("<redacted>"));
|
||||
assert!(rendered.contains("note_count"));
|
||||
assert!(!rendered.contains("ALIAS-SECRET-CANARY"));
|
||||
assert!(!rendered.contains("NOTE-ID-CANARY"));
|
||||
assert!(!rendered.contains("NOTE-SECRET-CANARY"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn locked_info_does_not_carry_authorized_identity_or_metadata() {
|
||||
let info = super::LockedWalletInfo { format_version: 1, view_enabled: true };
|
||||
assert_eq!(info.format_version(), 1);
|
||||
assert!(info.view_enabled());
|
||||
let rendered = format!("{info:?}");
|
||||
assert!(!rendered.contains("Pubkey"));
|
||||
assert!(!rendered.contains("alias"));
|
||||
assert!(!rendered.contains("notes"));
|
||||
}
|
||||
24
crates/ksp-wallet-lib/unit_tests/password.rs
Normal file
24
crates/ksp-wallet-lib/unit_tests/password.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
// file: crates/ksp-wallet-lib/unit_tests/password.rs
|
||||
// version: 1
|
||||
|
||||
#[test]
|
||||
fn view_password_debug_is_redacted() {
|
||||
let password = super::ViewPassword::new(std::string::String::from("VIEW-SECRET-CANARY"));
|
||||
let rendered = format!("{password:?}");
|
||||
assert_eq!(rendered, "ViewPassword(<redacted>)");
|
||||
assert!(!rendered.contains("VIEW-SECRET-CANARY"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn owner_password_debug_is_redacted() {
|
||||
let password = super::OwnerPassword::new(std::string::String::from("OWNER-SECRET-CANARY"));
|
||||
let rendered = format!("{password:?}");
|
||||
assert_eq!(rendered, "OwnerPassword(<redacted>)");
|
||||
assert!(!rendered.contains("OWNER-SECRET-CANARY"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn password_wrappers_require_drop_for_zeroization() {
|
||||
assert!(std::mem::needs_drop::<super::ViewPassword>());
|
||||
assert!(std::mem::needs_drop::<super::OwnerPassword>());
|
||||
}
|
||||
365
deltas/0.2.5/pre.002.md
Normal file
365
deltas/0.2.5/pre.002.md
Normal file
@@ -0,0 +1,365 @@
|
||||
<!-- file: deltas/0.2.5/pre.002.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Delta `0.2.5-pre.002` — foundation crate/capabilities/passwords/errors/logging
|
||||
|
||||
## Base requise
|
||||
|
||||
```text
|
||||
release : 0.2.5
|
||||
prerelease : pre.002
|
||||
identifiant de commit attendu : v0.2.5-pre.002
|
||||
workspace.package.version : 0.2.5-pre.2
|
||||
base : v0.2.5-pre.001-fix.001
|
||||
```
|
||||
|
||||
Le delta `pre.001-fix.001` est appliqué avant cette tranche. Les deltas historiques `pre.001.md` et `pre.001-fix.001.md` restent inchangés.
|
||||
|
||||
## Objectif
|
||||
|
||||
Créer la foundation Rust de `ksp-wallet-lib` sans commencer encore le codec `.kspwallet`, le KDF/AEAD, la persistence, la keypair Solana ou la signature.
|
||||
|
||||
Cette tranche matérialise uniquement :
|
||||
|
||||
```text
|
||||
crate + frontières Cargo
|
||||
WalletCapability
|
||||
WalletView / WalletOwner opaques
|
||||
LockedWalletInfo / WalletInfo / WalletNote
|
||||
ViewPassword / OwnerPassword
|
||||
codes d'erreur Wallet
|
||||
contrat de logging KSP
|
||||
canaries public API / dependency firewall
|
||||
```
|
||||
|
||||
Les opérations `create/open/rotate/sign/import/export` restent volontairement absentes tant que leurs invariants cryptographiques/persistence ne sont pas implémentés dans les tranches prévues.
|
||||
|
||||
## Version Cargo
|
||||
|
||||
Conformément à `VER-ID-009` :
|
||||
|
||||
```text
|
||||
0.2.5-pre.1 -> 0.2.5-pre.2
|
||||
```
|
||||
|
||||
Toutes les crates membres continuent d'hériter `version.workspace = true`.
|
||||
|
||||
## Workspace et dépendances
|
||||
|
||||
Nouveau membre :
|
||||
|
||||
```text
|
||||
crates/ksp-wallet-lib
|
||||
```
|
||||
|
||||
Dépendances directes :
|
||||
|
||||
```text
|
||||
ksp-core-lib
|
||||
ksp-logging-lib
|
||||
zeroize.workspace = true
|
||||
```
|
||||
|
||||
`zeroize` est centralisé dans `[workspace.dependencies]` :
|
||||
|
||||
```toml
|
||||
zeroize = { version = "^1.9" }
|
||||
```
|
||||
|
||||
La génération courante `zeroize 1.9.0` a été réauditée avant insertion. La crate est utilisée immédiatement pour nettoyer au `Drop` les buffers `String` possédés par les wrappers de passwords. Aucun `secrecy`, Argon2, AEAD, CSPRNG, Solana keypair/signer/signature ou codec supplémentaire n'est ajouté par anticipation.
|
||||
|
||||
Interdictions confirmées dans le manifest Wallet :
|
||||
|
||||
```text
|
||||
ksp-config-lib
|
||||
ksp-onchain-transport-lib
|
||||
ksp-execution-policy-api
|
||||
ksp-store-api / ksp-store-lib
|
||||
Tauri
|
||||
tracing direct
|
||||
solana-pubkey direct
|
||||
```
|
||||
|
||||
### Ownership de `Pubkey`
|
||||
|
||||
`ksp-wallet-lib` ne dépend pas directement de `solana-pubkey`.
|
||||
|
||||
Toute surface Wallet utilise :
|
||||
|
||||
```text
|
||||
ksp_core_lib::Pubkey
|
||||
```
|
||||
|
||||
qui est le re-export possédé par `ksp-core-lib`. Les futures crates `solana-keypair`/`solana-signer` ne seront ajoutées que lorsqu'un chemin de compilation les consommera réellement ; leur compatibilité avec la génération de `Pubkey` possédée par Core sera alors contrôlée par compilation et `cargo tree`.
|
||||
|
||||
### Aucun ownership Config
|
||||
|
||||
Wallet n'a aucune configuration runtime propre et ne lit ni document Config, ni `.env`, ni environnement processus.
|
||||
|
||||
Le futur chemin/répertoire de destination d'une création/import sera fourni explicitement par le caller. Une application pourra faire résoudre son propre défaut par Config puis transmettre ce chemin à Wallet sans créer de dépendance inverse Wallet -> Config.
|
||||
|
||||
## Capabilities publiques
|
||||
|
||||
### `WalletCapability`
|
||||
|
||||
Deux rôles uniquement :
|
||||
|
||||
```text
|
||||
View
|
||||
Owner
|
||||
```
|
||||
|
||||
`View` signifie metadata autorisées plus future rotation de **son propre password VIEW uniquement**.
|
||||
|
||||
`Owner` signifie metadata autorisées plus futures capacités de signature/export explicite/administration.
|
||||
|
||||
Aucun rôle recovery/hardware/automation n'est ajouté en V1.
|
||||
|
||||
### `WalletView`
|
||||
|
||||
Handle opaque exposant actuellement uniquement la projection metadata :
|
||||
|
||||
```text
|
||||
capability() -> View
|
||||
pubkey()
|
||||
alias()
|
||||
notes()
|
||||
info()
|
||||
```
|
||||
|
||||
Il n'expose aucun constructeur public, aucune signature, aucun export secret, aucune mutation alias/notes et aucune administration OWNER.
|
||||
|
||||
La future méthode de rotation de son propre password sera ajoutée lorsque le slot VIEW réel et sa persistence existent ; `pre.002` ne simule pas cette opération.
|
||||
|
||||
### `WalletOwner`
|
||||
|
||||
Handle opaque exposant actuellement la même lecture metadata autorisée :
|
||||
|
||||
```text
|
||||
capability() -> Owner
|
||||
pubkey()
|
||||
alias()
|
||||
notes()
|
||||
info()
|
||||
```
|
||||
|
||||
Il n'expose encore ni secret ni signature ni méthode d'administration. Les opérations OWNER réelles sont ajoutées avec les matériaux cryptographiques/persistence correspondants dans les tranches suivantes.
|
||||
|
||||
### Projections metadata
|
||||
|
||||
`LockedWalletInfo` contient uniquement :
|
||||
|
||||
```text
|
||||
format_version
|
||||
view_enabled
|
||||
```
|
||||
|
||||
Il ne contient aucune Pubkey, alias ou note.
|
||||
|
||||
`WalletInfo` contient après autorisation :
|
||||
|
||||
```text
|
||||
format_version
|
||||
capability
|
||||
ksp_core_lib::Pubkey
|
||||
alias optionnel
|
||||
notes
|
||||
```
|
||||
|
||||
`WalletNote` expose un identifiant stable et un texte protégé après autorisation.
|
||||
|
||||
Les constructors de ces projections ne sont pas publics : elles sont destinées à être produites par Wallet, pas à fabriquer une capability.
|
||||
|
||||
Les `Debug` de `WalletInfo`/`WalletNote` ne rendent pas alias, identifiant de note ou contenu de note. La Pubkey peut apparaître dans `WalletInfo::Debug` car cette projection n'existe qu'après autorisation et la Pubkey n'est pas un secret blockchain ; Wallet verrouillé ne possède pas cette projection.
|
||||
|
||||
## Password wrappers
|
||||
|
||||
Deux types distincts :
|
||||
|
||||
```text
|
||||
ViewPassword
|
||||
OwnerPassword
|
||||
```
|
||||
|
||||
Ils :
|
||||
|
||||
- prennent ownership d'un `String` ;
|
||||
- n'implémentent ni `Clone` ni `Copy` ;
|
||||
- n'implémentent pas `Display` ;
|
||||
- exposent un `Debug` strictement redacted ;
|
||||
- implémentent un `Drop` appelant `zeroize::Zeroize` sur le buffer possédé ;
|
||||
- n'exposent aucun getter public du password en clair.
|
||||
|
||||
Deux doctests `compile_fail` canaris interdisent la régression vers `Clone`.
|
||||
|
||||
Cette zeroization reste une hygiène best-effort sur le buffer possédé et ne prétend pas effacer des copies antérieures conservées par le caller, l'allocateur, les registres ou d'autres couches du runtime.
|
||||
|
||||
## Erreurs
|
||||
|
||||
Le domaine stable est :
|
||||
|
||||
```text
|
||||
wallet
|
||||
```
|
||||
|
||||
Les 13 codes réservés/stabilisés dans cette foundation sont :
|
||||
|
||||
```text
|
||||
format_invalid
|
||||
format_version_unsupported
|
||||
crypto_parameters_invalid
|
||||
authentication_failed
|
||||
view_unlock_failed
|
||||
owner_unlock_failed
|
||||
capability_insufficient
|
||||
io_failed
|
||||
destination_exists
|
||||
atomic_persistence_failed
|
||||
transfer_format_unsupported
|
||||
key_material_invalid
|
||||
signature_failed
|
||||
```
|
||||
|
||||
Ils utilisent exclusivement `ksp_core_lib::ErrorCode`. Aucun type d'erreur parallèle n'est créé.
|
||||
|
||||
Les codes de déverrouillage restent volontairement orientés rôle/opération afin que l'implémentation crypto future puisse éviter de distinguer publiquement password incorrect, unwrap incorrect et tag AEAD incorrect lorsqu'une telle distinction créerait un oracle inutile.
|
||||
|
||||
## Logging
|
||||
|
||||
Le target Wallet est possédé explicitement par :
|
||||
|
||||
```text
|
||||
crates/ksp-wallet-lib/src/constants.rs
|
||||
```
|
||||
|
||||
avec :
|
||||
|
||||
```text
|
||||
pub(crate) const TRACING_TARGET: &str = "ksp-wallet-lib";
|
||||
```
|
||||
|
||||
Les émissions utilisent exclusivement `ksp_logging_lib::*` et jamais `tracing` directement ni `env!("CARGO_PKG_NAME")` comme target.
|
||||
|
||||
La foundation instrumente seulement la demande explicite de projection `info()` au niveau `trace`, avec :
|
||||
|
||||
```text
|
||||
operation=wallet_info_projection
|
||||
capability=view|owner
|
||||
```
|
||||
|
||||
Aucun alias, note, password, secret, payload arbitraire ou path n'est loggé.
|
||||
|
||||
## Tests ajoutés
|
||||
|
||||
La tranche définit **13 tests Rust déterministes** plus **2 doctests `compile_fail`** :
|
||||
|
||||
- variants `WalletCapability` ;
|
||||
- redaction `Debug` des deux password wrappers ;
|
||||
- présence d'un `Drop` pour les wrappers secrets ;
|
||||
- projection metadata autorisée ;
|
||||
- redaction alias/note dans `Debug` ;
|
||||
- projection locked sans identité/metadata ;
|
||||
- canary crate-root pour `WalletView`/`WalletOwner`/`WalletCapability` ;
|
||||
- canary crate-root pour les wrappers de password ;
|
||||
- signatures de méthodes Pubkey typées en `ksp_core_lib::Pubkey` ;
|
||||
- disponibilité des 13 codes d'erreur depuis le crate root ;
|
||||
- manifest firewall Wallet ;
|
||||
- source ownership canary Core Pubkey / Logging facade / absence env direct.
|
||||
|
||||
Les tests d'intégration de firewall restent spécifiques à `ksp-wallet-lib`; aucun nouveau canary général du workspace n'est déplacé arbitrairement dans cette crate.
|
||||
|
||||
## Documentation synchronisée
|
||||
|
||||
Le plan Wallet est passé en version 5 pour :
|
||||
|
||||
- figer `ksp_core_lib::Pubkey` comme seule frontière Pubkey de Wallet ;
|
||||
- corriger le target de logging vers `ksp-wallet-lib` dans `src/constants.rs` conformément à `DEP-LOG-010` ;
|
||||
- enregistrer `zeroize ^1.9` comme seul ajout tiers de `pre.002` ;
|
||||
- repousser les crates Solana directes à leur premier usage réel ;
|
||||
- confirmer qu'aucun répertoire par défaut n'appartient à Wallet et que le path sera fourni par le caller ;
|
||||
- pointer la suite immédiate sur `pre.003`.
|
||||
|
||||
`ROADMAP.md` et `docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md` enregistrent la matérialisation de la foundation sans recopier le détail des futures prereleases.
|
||||
|
||||
## Fichiers ajoutés
|
||||
|
||||
```text
|
||||
crates/ksp-wallet-lib/Cargo.toml
|
||||
crates/ksp-wallet-lib/src/capability.rs
|
||||
crates/ksp-wallet-lib/src/constants.rs
|
||||
crates/ksp-wallet-lib/src/error.rs
|
||||
crates/ksp-wallet-lib/src/lib.rs
|
||||
crates/ksp-wallet-lib/src/metadata.rs
|
||||
crates/ksp-wallet-lib/src/owner.rs
|
||||
crates/ksp-wallet-lib/src/password.rs
|
||||
crates/ksp-wallet-lib/src/view.rs
|
||||
crates/ksp-wallet-lib/unit_tests/capability.rs
|
||||
crates/ksp-wallet-lib/unit_tests/metadata.rs
|
||||
crates/ksp-wallet-lib/unit_tests/password.rs
|
||||
crates/ksp-wallet-lib/tests/dependency_boundary.rs
|
||||
crates/ksp-wallet-lib/tests/public_api.rs
|
||||
deltas/0.2.5/pre.002.md
|
||||
```
|
||||
|
||||
## Fichiers modifiés
|
||||
|
||||
```text
|
||||
Cargo.toml
|
||||
ROADMAP.md
|
||||
docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md
|
||||
docs/plans/012-V0_2_5_WALLET_FOUNDATION_PLAN.md
|
||||
```
|
||||
|
||||
## Fichiers supprimés
|
||||
|
||||
Aucun.
|
||||
|
||||
## Validations exécutées dans l'environnement de préparation
|
||||
|
||||
Contrôles statiques réellement exécutés :
|
||||
|
||||
- parsing TOML du `Cargo.toml` racine et du manifest Wallet ;
|
||||
- comparaison de l'arbre avec la base `pre.001-fix.001` ;
|
||||
- inventaire exact des fichiers ajoutés/modifiés ;
|
||||
- scan des sources Wallet : aucun `use` statement ;
|
||||
- scan production : aucun `unwrap`, `expect`, `panic`, opérateur `?` ou bloc `unsafe` ajouté ;
|
||||
- manifest firewall : absence Config/Transport/ExecutionPolicy/Store/Tauri/tracing/solana-pubkey directs ;
|
||||
- source ownership : `ksp_core_lib::Pubkey`, `ksp_logging_lib::trace!`, target explicite `ksp-wallet-lib`, aucun `solana_pubkey::`, `tracing::` ou `std::env::` ;
|
||||
- contrôle des headers `file:` / `version:` des fichiers ajoutés/modifiés ;
|
||||
- contrôle de l'archive d'échange contre `VER-ARCHIVE-004` avant livraison.
|
||||
|
||||
## Validations non exécutées
|
||||
|
||||
L'environnement de préparation ne fournit pas la toolchain Rust/Cargo. Les validations suivantes doivent donc être exécutées par l'opérateur après application du delta :
|
||||
|
||||
```bash
|
||||
cargo fmt --all
|
||||
cargo check --workspace
|
||||
cargo clippy --workspace --all-targets
|
||||
cargo test -p ksp-wallet-lib
|
||||
```
|
||||
|
||||
Un `cargo tree` complet des primitives Solana/crypto n'est pas encore pertinent : aucune primitive Solana/keypair/KDF/AEAD nouvelle n'est ajoutée en `pre.002`. `zeroize` doit néanmoins apparaître sans duplication injustifiée lors du contrôle Cargo opérateur.
|
||||
|
||||
## Décisions prises
|
||||
|
||||
- `Pubkey` Wallet est exclusivement `ksp_core_lib::Pubkey`; aucune dépendance directe `solana-pubkey` dans Wallet.
|
||||
- Logging Wallet utilise exclusivement `ksp-logging-lib` et `TRACING_TARGET = "ksp-wallet-lib"` dans `src/constants.rs`.
|
||||
- Wallet ne dépend pas de Config et ne choisira pas lui-même un répertoire par défaut ; le caller fournit le path.
|
||||
- `zeroize ^1.9` est le seul nouveau tiers de `pre.002`.
|
||||
- Les password wrappers sont distincts OWNER/VIEW, owned, redacted, non-Clone et zeroized au drop.
|
||||
- Les handles VIEW/OWNER restent opaques et ne simulent aucune opération cryptographique non encore implémentée.
|
||||
- VIEW conserve comme contrat futur la seule mutation de son propre password ; OWNER conserve les futures rotations OWNER/VIEW et l'administration complète.
|
||||
|
||||
## Questions ouvertes
|
||||
|
||||
Aucune question bloquante pour `pre.003`.
|
||||
|
||||
Restent volontairement à décider/figer dans leurs tranches prévues :
|
||||
|
||||
- wire exact JSON/key slots/transcript/AAD (`pre.003`) ;
|
||||
- paramètres Argon2 benchmarkés et crypto effective (`pre.004`) ;
|
||||
- matériaux owner-control/metadata/secret et state signature (`pre.005`) ;
|
||||
- persistence async/atomique/no-clobber (`pre.006`) ;
|
||||
- keypair/signature et rotations réelles (`pre.007`) ;
|
||||
- adapters import/export (`pre.008`).
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md -->
|
||||
<!-- version: 46 -->
|
||||
<!-- version: 47 -->
|
||||
|
||||
# Séquence des releases fonctionnelles KSP
|
||||
|
||||
@@ -408,6 +408,8 @@ Le format V1 est cadré comme JSON UTF-8 strict avec binary Base64url sans paddi
|
||||
|
||||
La release doit fournir `docs/formats/KSPWALLET_V1.md` comme spécification séparée et indépendante de Rust, accompagnée de vecteurs publics auto-contenus permettant une réimplémentation dans un autre langage. La prévision est étendue jusqu'à `pre.010` afin de séparer codec, crypto, capabilities, persistence, administration, import/export, security audit et documentation interopérable. Le plan actif est `docs/plans/012-V0_2_5_WALLET_FOUNDATION_PLAN.md`.
|
||||
|
||||
`0.2.5-pre.002` matérialise la crate sans ouvrir encore le codec ou la cryptographie du fichier : `WalletView`/`WalletOwner`, `WalletCapability`, projections `LockedWalletInfo`/`WalletInfo`/`WalletNote`, wrappers `ViewPassword`/`OwnerPassword`, codes d’erreur Wallet et target de logging explicite `ksp-wallet-lib`. La crate dépend seulement de Core, Logging et `zeroize`; elle consomme la Pubkey exclusivement via `ksp_core_lib::Pubkey` et ne dépend directement ni de `solana-pubkey`, ni de Config, Transport, ExecutionPolicy, Store ou Tauri. Les primitives Solana keypair/signature ne seront ajoutées que lorsqu’elles seront réellement consommées.
|
||||
|
||||
## `0.2.6` — Wallet Desk
|
||||
|
||||
Mission : valider Config composite + `.kspwallet` + transport HTTP dans une application Tauri mince.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/plans/012-V0_2_5_WALLET_FOUNDATION_PLAN.md -->
|
||||
<!-- version: 4 -->
|
||||
<!-- version: 5 -->
|
||||
|
||||
# Plan `0.2.5` — Wallet foundation
|
||||
|
||||
@@ -77,7 +77,8 @@ Clarifications confirmées pendant la revue du gate :
|
||||
27. **OWNER peut changer son propre password OWNER et le password VIEW**, sans connaître l'ancien password VIEW ; OWNER reste seul capable de l'administration metadata et des key slots au-delà du self-service de rotation VIEW ;
|
||||
28. la keypair Solana est **immuable dans un wallet V1 après création/import**. V1 ne fournit pas d'opération de remplacement de keypair ; une autre keypair crée un autre wallet ;
|
||||
29. les permissions/ACL du système de fichiers ne constituent **ni une garantie cryptographique ni une responsabilité de sécurité de `ksp-wallet-lib`**. Le contrat Wallet porte sur les capacités : sans OWNER, aucune signature Solana, aucun export secret, aucune écriture acceptée de Pubkey/alias/notes et aucune mutation OWNER-controlled ; la seule mutation volontairement accordée à VIEW est la rotation de son propre password ;
|
||||
30. tout import produit un **nouveau `.kspwallet`** selon la sémantique no-clobber de `create`. Un import ne remplace, n'écrase et ne transforme jamais un `.kspwallet` existant.
|
||||
30. tout import produit un **nouveau `.kspwallet`** selon la sémantique no-clobber de `create`. Un import ne remplace, n'écrase et ne transforme jamais un `.kspwallet` existant ;
|
||||
31. `ksp-wallet-lib` ne possède aucun « répertoire Wallet par défaut » ni configuration correspondante. Les opérations de persistence reçoivent un chemin/répertoire explicite du caller ; une future application peut faire résoudre son propre défaut par Config puis transmettre le chemin à Wallet, sans créer de dépendance Wallet -> Config.
|
||||
|
||||
## 4. Réaudit de l'héritage bot2/bot3
|
||||
|
||||
@@ -368,16 +369,16 @@ Aucune dépendance ci-dessous n'est ajoutée dans `pre.001`. Ce sont les candida
|
||||
|
||||
### 8.1 Solana low-level
|
||||
|
||||
| Besoin | Crate publiée auditée | Décision candidate |
|
||||
|------------------|--------------------------|--------------------------------------------------------|
|
||||
| Pubkey | `solana-pubkey 4.3.0` | déjà workspace, conserver |
|
||||
| keypair concret | `solana-keypair 3.1.2` | retenir, features minimales |
|
||||
| interface signer | `solana-signer 3.0.1` | retenir si contrat public/impl l'exige |
|
||||
| signature | `solana-signature 3.5.2` | dépendance directe seulement si le type public l'exige |
|
||||
| Besoin | Crate publiée auditée | Décision candidate |
|
||||
|------------------|--------------------------|---------------------------------------------------------------------------------------------|
|
||||
| Pubkey | `solana-pubkey 4.3.0` | propriété Core ; Wallet consomme uniquement `ksp_core_lib::Pubkey`, sans dépendance directe |
|
||||
| keypair concret | `solana-keypair 3.1.2` | retenir, features minimales |
|
||||
| interface signer | `solana-signer 3.0.1` | retenir si contrat public/impl l'exige |
|
||||
| signature | `solana-signature 3.5.2` | dépendance directe seulement si le type public l'exige |
|
||||
|
||||
`solana-keypair` fournit le keypair Ed25519, la conversion stricte depuis 64 octets, la signature, le format JSON de 64 entiers et une représentation Base58 complète. Aucun client RPC Solana n'est nécessaire.
|
||||
|
||||
La génération SDK récente définit `Pubkey` comme alias du type `Address`, mais l'écosystème a déjà connu des incompatibilités lorsque plusieurs générations de `solana-address` coexistent. `pre.002` doit donc prouver par compilation/cargo-tree que le `Pubkey` possédé par `ksp-core-lib` et le `Address/Pubkey` exposé par `solana-keypair` sont de la même génération avant de figer les signatures publiques Wallet. Aucun second type d'adresse KSP n'est introduit pour contourner un mismatch.
|
||||
La génération SDK récente définit `Pubkey` comme alias du type `Address`, mais l'écosystème a déjà connu des incompatibilités lorsque plusieurs générations de `solana-address` coexistent. `pre.002` verrouille donc la surface publique Wallet sur **`ksp_core_lib::Pubkey` exclusivement** et n'ajoute aucune dépendance directe `solana-pubkey`. Lorsque `solana-keypair` sera réellement introduit, la tranche concernée devra prouver par compilation/cargo-tree que son `Address/Pubkey` est compatible avec la génération possédée par Core avant toute conversion interne. Aucun second type d'adresse KSP n'est introduit pour contourner un mismatch.
|
||||
|
||||
Un `cargo tree` est obligatoire lors de l'introduction réelle afin de contrôler les versions `ed25519-dalek`, `solana-signature`, `rand/getrandom` et éviter des duplications évitables.
|
||||
|
||||
@@ -412,7 +413,7 @@ AES-GCM-SIV apporte une meilleure tolérance à la réutilisation accidentelle d
|
||||
|
||||
### 8.5 Secret memory
|
||||
|
||||
`zeroize 1.9.0` est retenu. `secrecy` n'est pas ajouté tant qu'un besoin ergonomique concret n'est pas démontré ; des types KSP simples peuvent imposer eux-mêmes redaction/non-Clone et utiliser `Zeroize`/`Zeroizing`.
|
||||
`zeroize 1.9.0` est retenu et devient la seule nouvelle dépendance tierce de `pre.002`, car les wrappers `ViewPassword` / `OwnerPassword` l’utilisent immédiatement pour leur nettoyage au `Drop`. `secrecy` n'est pas ajouté tant qu'un besoin ergonomique concret n'est pas démontré ; des types KSP simples imposent eux-mêmes redaction/non-Clone et utilisent `zeroize`.
|
||||
|
||||
Pour la clé admin Ed25519 distincte, `ed25519-dalek 3.0.0` est une candidate standard, mais son ajout direct est **conditionné au cargo-tree** de la tranche qui implémente l'authentification afin d'éviter une génération concurrente inutile avec celle déjà tirée par `solana-keypair`.
|
||||
|
||||
@@ -435,7 +436,7 @@ Candidates :
|
||||
|
||||
```text
|
||||
base64 0.23.1 pour Base64url sans padding des champs binaires JSON
|
||||
tempfile 3.27.0 pour temp files privés same-directory + persist/persist_noclobber
|
||||
tempfile 3.27.0 pour temp files same-directory + persist/persist_noclobber
|
||||
```
|
||||
|
||||
Elles ne sont ajoutées que lorsqu'elles sont réellement consommées.
|
||||
@@ -766,10 +767,12 @@ La crate peut donc être utilisée depuis Tauri plus tard sans dépendre de Taur
|
||||
|
||||
### 14.1 Baseline portable
|
||||
|
||||
Le chemin de destination est toujours fourni explicitement par le caller ; Wallet ne lit aucune Config ni variable d'environnement pour choisir un répertoire par défaut.
|
||||
|
||||
Pour créer/remplacer un `.kspwallet` :
|
||||
|
||||
1. sérialiser complètement le nouvel état en mémoire bornée ;
|
||||
2. créer un temp file privé **dans le même répertoire** que la destination ;
|
||||
2. créer un temp file unique **dans le même répertoire** que la destination ;
|
||||
3. écrire tout le contenu ;
|
||||
4. `flush`/`sync_all` le temp file ;
|
||||
5. publier avec une primitive no-clobber pour `create` ou replace pour administration ;
|
||||
@@ -836,13 +839,13 @@ notes/alias par défaut
|
||||
|
||||
## 16. Logging
|
||||
|
||||
Cible KSP future proposée :
|
||||
Le target principal Wallet est explicitement possédé par `src/constants.rs` conformément à `DEP-LOG-010` :
|
||||
|
||||
```text
|
||||
ksp.wallet
|
||||
pub(crate) const TRACING_TARGET: &str = "ksp-wallet-lib";
|
||||
```
|
||||
|
||||
Via `ksp-logging-lib` uniquement.
|
||||
Toutes les émissions passent par `ksp-logging-lib` uniquement. Wallet ne dépend jamais directement de `tracing` et n’utilise pas `env!("CARGO_PKG_NAME")` comme target.
|
||||
|
||||
Événements sûrs :
|
||||
|
||||
@@ -1065,9 +1068,15 @@ Une `fix` ou tranche supplémentaire est préférable à la suppression d'une ga
|
||||
|
||||
## 22. Dépendances candidates par tranche
|
||||
|
||||
Aucune ajoutée en `pre.001`.
|
||||
`pre.001` n’ajoutait aucune dépendance. `pre.002` ajoute uniquement :
|
||||
|
||||
Liste de travail, à réauditer juste avant insertion sous `[workspace.dependencies]` :
|
||||
```text
|
||||
zeroize ^1.9
|
||||
```
|
||||
|
||||
La dépendance est centralisée sous `[workspace.dependencies]` puis consommée avec `zeroize.workspace = true`. Elle est utilisée immédiatement par les wrappers de password ; aucune crate crypto/KDF/Solana supplémentaire n’est ajoutée par anticipation. La génération courante `zeroize 1.9.0` a été réauditée avant insertion.
|
||||
|
||||
Liste de travail restante, à réauditer juste avant insertion sous `[workspace.dependencies]` :
|
||||
|
||||
```text
|
||||
argon2 ^0.5
|
||||
@@ -1079,7 +1088,6 @@ solana-keypair ^3.1
|
||||
solana-signer ^3.0 # si réellement nécessaire directement
|
||||
solana-signature ^3.5 # seulement si le type public l'exige
|
||||
tempfile ^3.27
|
||||
zeroize ^1.9
|
||||
```
|
||||
|
||||
Déjà présents et réutilisables :
|
||||
@@ -1088,11 +1096,12 @@ Déjà présents et réutilisables :
|
||||
serde
|
||||
serde_json
|
||||
tokio
|
||||
solana-pubkey
|
||||
ksp-core-lib
|
||||
ksp-core-lib # propriétaire/réexport de Pubkey pour Wallet
|
||||
ksp-logging-lib
|
||||
```
|
||||
|
||||
`ksp-wallet-lib` ne déclare pas `solana-pubkey` : toute Pubkey publique ou interne du domaine Wallet passe par `ksp_core_lib::Pubkey`.
|
||||
|
||||
Non retenus nativement en V1 :
|
||||
|
||||
```text
|
||||
@@ -1184,4 +1193,4 @@ Une future `format_version >= 2` pourra réétudier des facteurs/ancrages extern
|
||||
|
||||
## 26. Suite immédiate
|
||||
|
||||
`0.2.5-pre.002` crée seulement la foundation de `ksp-wallet-lib` : boundaries Cargo, types de capability/password/info, erreurs, façade logging et canaries architecturales. Le codec et la cryptographie du fichier restent à `pre.003+`.
|
||||
`0.2.5-pre.003` est la suite immédiate : codec JSON strict, limites, DTOs d’enveloppe/key slots, transcript/AAD et première spécification `docs/formats/KSPWALLET_V1.md`. La cryptographie effective KDF/AEAD reste à `pre.004+`.
|
||||
|
||||
Reference in New Issue
Block a user