v0.2.5-pre.009

This commit is contained in:
2026-08-20 09:24:10 +02:00
parent 1125ade4c1
commit e91bf36e9e
83 changed files with 2467 additions and 1801 deletions

View File

@@ -1,10 +1,10 @@
// file: crates/ksp-wallet-lib/src/crypto.rs
// version: 2
// version: 3
//! In-memory cryptographic primitives for native `.kspwallet` V1.
use chacha20poly1305::KeyInit as _;
use chacha20poly1305::aead::Aead as _;
use chacha20poly1305::KeyInit; // rust-rules: derive-import
use chacha20poly1305::aead::Aead; // rust-rules: derive-import
/// Exact V1 content-key and password-derived-key size in bytes.
pub(crate) const SECRET_KEY_BYTES: usize = 32;
@@ -14,7 +14,7 @@ pub(crate) struct SecretKeyV1 {
bytes: [u8; SECRET_KEY_BYTES],
}
impl SecretKeyV1 {
impl crate::SecretKeyV1 {
/// Takes ownership of exact 32-byte secret material.
pub(crate) const fn from_bytes(bytes: [u8; SECRET_KEY_BYTES]) -> Self {
return Self { bytes };
@@ -35,13 +35,13 @@ impl SecretKeyV1 {
}
}
impl std::fmt::Debug for SecretKeyV1 {
impl std::fmt::Debug for crate::SecretKeyV1 {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return formatter.write_str("SecretKeyV1(<redacted>)");
}
}
impl std::ops::Drop for SecretKeyV1 {
impl std::ops::Drop for crate::SecretKeyV1 {
fn drop(&mut self) {
zeroize::Zeroize::zeroize(&mut self.bytes);
}
@@ -64,28 +64,28 @@ pub(crate) fn random_nonce() -> ksp_core_lib::Result<[u8; crate::KSPWALLET_V1_XC
}
/// Derives one V1 password wrapping key from serialized Argon2id parameters.
pub(crate) fn derive_password_key(password: &[u8], kdf: &crate::WalletKdfParametersV1) -> ksp_core_lib::Result<SecretKeyV1> {
pub(crate) fn derive_password_key(password: &[u8], kdf: &crate::WalletKdfParametersV1) -> ksp_core_lib::Result<crate::SecretKeyV1> {
return derive_argon2id(password, kdf.salt(), kdf.memory_kib(), kdf.iterations(), kdf.parallelism());
}
/// Wraps one 32-byte content key with XChaCha20-Poly1305 and caller-provided domain-separated AAD.
pub(crate) fn wrap_key(
wrapping_key: &SecretKeyV1,
key_to_wrap: &SecretKeyV1,
wrapping_key: &crate::SecretKeyV1,
key_to_wrap: &crate::SecretKeyV1,
nonce: &[u8; crate::KSPWALLET_V1_XCHACHA_NONCE_BYTES],
aad: &[u8],
) -> ksp_core_lib::Result<std::vec::Vec<u8>> {
return encrypt_bytes(wrapping_key, nonce, aad, key_to_wrap.as_bytes());
return crate::encrypt_bytes(wrapping_key, nonce, aad, key_to_wrap.as_bytes());
}
/// Unwraps one 32-byte content key and maps every AEAD authentication failure to the generic Wallet authentication error.
pub(crate) fn unwrap_key(
wrapping_key: &SecretKeyV1,
wrapping_key: &crate::SecretKeyV1,
nonce: &[u8; crate::KSPWALLET_V1_XCHACHA_NONCE_BYTES],
aad: &[u8],
ciphertext: &[u8],
) -> ksp_core_lib::Result<SecretKeyV1> {
let plaintext_result = decrypt_bytes(wrapping_key, nonce, aad, ciphertext);
) -> ksp_core_lib::Result<crate::SecretKeyV1> {
let plaintext_result = crate::decrypt_bytes(wrapping_key, nonce, aad, ciphertext);
let mut plaintext = match plaintext_result {
std::result::Result::Ok(plaintext) => plaintext,
std::result::Result::Err(error) => return std::result::Result::Err(error),
@@ -96,7 +96,7 @@ pub(crate) fn unwrap_key(
}
let converted = <[u8; SECRET_KEY_BYTES]>::try_from(plaintext.as_slice());
let key = match converted {
std::result::Result::Ok(key) => SecretKeyV1::from_bytes(key),
std::result::Result::Ok(key) => crate::SecretKeyV1::from_bytes(key),
std::result::Result::Err(_) => {
zeroize::Zeroize::zeroize(plaintext.as_mut_slice());
return std::result::Result::Err(authentication_error());
@@ -108,7 +108,7 @@ pub(crate) fn unwrap_key(
/// Encrypts bounded plaintext bytes with XChaCha20-Poly1305 and caller-provided domain-separated AAD.
pub(crate) fn encrypt_bytes(
key: &SecretKeyV1,
key: &crate::SecretKeyV1,
nonce: &[u8; crate::KSPWALLET_V1_XCHACHA_NONCE_BYTES],
aad: &[u8],
plaintext: &[u8],
@@ -129,7 +129,7 @@ pub(crate) fn encrypt_bytes(
/// Decrypts authenticated ciphertext bytes and returns a generic authentication error on tag failure.
pub(crate) fn decrypt_bytes(
key: &SecretKeyV1,
key: &crate::SecretKeyV1,
nonce: &[u8; crate::KSPWALLET_V1_XCHACHA_NONCE_BYTES],
aad: &[u8],
ciphertext: &[u8],
@@ -148,7 +148,7 @@ pub(crate) fn decrypt_bytes(
};
}
pub(crate) fn derive_argon2id(password: &[u8], salt: &[u8], memory_kib: u32, iterations: u32, parallelism: u32) -> ksp_core_lib::Result<SecretKeyV1> {
fn derive_argon2id(password: &[u8], salt: &[u8], memory_kib: u32, iterations: u32, parallelism: u32) -> ksp_core_lib::Result<crate::SecretKeyV1> {
if password.is_empty() || password.len() > crate::KSPWALLET_V1_MAX_PASSWORD_BYTES {
return std::result::Result::Err(crypto_parameter_error());
}
@@ -178,7 +178,7 @@ pub(crate) fn derive_argon2id(password: &[u8], salt: &[u8], memory_kib: u32, ite
zeroize::Zeroize::zeroize(&mut output);
return std::result::Result::Err(crypto_parameter_error());
}
return std::result::Result::Ok(SecretKeyV1::from_bytes(output));
return std::result::Result::Ok(crate::SecretKeyV1::from_bytes(output));
}
fn randomness_error() -> ksp_core_lib::Error {