Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions walletkit-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ world-id-core = { workspace = true, features = ["authenticator"] }
world-id-proof = { workspace = true }
ciborium = "0.2.2"
walletkit-db = { workspace = true }
walletkit-secure-store = { workspace = true }

[target.'cfg(target_arch = "wasm32")'.dependencies]
getrandom = { version = "0.3", features = ["wasm_js"] }
Expand Down
2 changes: 1 addition & 1 deletion walletkit-core/src/storage/credential_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ impl CredentialStoreInner {
}

fn guard(&self) -> StorageResult<StorageLockGuard> {
self.lock.lock()
Ok(self.lock.lock()?)
}

fn state(&self) -> StorageResult<&StorageState> {
Expand Down
73 changes: 0 additions & 73 deletions walletkit-core/src/storage/envelope.rs

This file was deleted.

19 changes: 19 additions & 0 deletions walletkit-core/src/storage/error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Error types for credential storage components.

use thiserror::Error;
use walletkit_secure_store::StoreError;

/// Result type for storage operations.
pub type StorageResult<T> = Result<T, StorageError>;
Expand Down Expand Up @@ -93,3 +94,21 @@ impl From<uniffi::UnexpectedUniFFICallbackError> for StorageError {
Self::UnexpectedUniFFICallbackError(error.reason)
}
}

impl From<StoreError> for StorageError {
fn from(err: StoreError) -> Self {
match err {
StoreError::Keystore(msg) => Self::Keystore(msg),
StoreError::BlobStore(msg) => Self::BlobStore(msg),
StoreError::Lock(msg) => Self::Lock(msg),
StoreError::Serialization(msg) => Self::Serialization(msg),
StoreError::Crypto(msg) => Self::Crypto(msg),
StoreError::InvalidEnvelope(msg) => Self::InvalidEnvelope(msg),
StoreError::UnsupportedEnvelopeVersion(version) => {
Self::UnsupportedEnvelopeVersion(version)
}
StoreError::Db(msg) => Self::VaultDb(msg),
StoreError::IntegrityCheckFailed(msg) => Self::CorruptedVault(msg),
}
}
}
74 changes: 24 additions & 50 deletions walletkit-core/src/storage/keys.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
//! Key hierarchy management for credential storage.
//!
//! Delegates the actual envelope load/create logic to
//! [`walletkit_secure_store::init_or_open_envelope_key`], passing the
//! credential-store-specific envelope filename and associated data so the
//! intermediate key is bound to this consumer's vault.

use rand::{rngs::OsRng, RngCore};
use secrecy::SecretBox;
use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
use walletkit_secure_store::init_or_open_envelope_key;
use zeroize::{Zeroize, ZeroizeOnDrop};

use super::{
envelope::AccountKeyEnvelope,
error::{StorageError, StorageResult},
error::StorageResult,
lock::StorageLockGuard,
traits::{AtomicBlobStore, DeviceKeystore},
traits::{
AtomicBlobStore, AtomicBlobStoreAdapter, DeviceKeystore, DeviceKeystoreAdapter,
},
ACCOUNT_KEYS_FILENAME, ACCOUNT_KEY_ENVELOPE_AD,
};

Expand All @@ -31,35 +37,20 @@ impl StorageKeys {
pub fn init(
keystore: &dyn DeviceKeystore,
blob_store: &dyn AtomicBlobStore,
_lock: &StorageLockGuard,
lock: &StorageLockGuard,
now: u64,
) -> StorageResult<Self> {
if let Some(bytes) = blob_store.read(ACCOUNT_KEYS_FILENAME.to_string())? {
let envelope = AccountKeyEnvelope::deserialize(&bytes)?;
let wrapped_k_intermediate = envelope.wrapped_k_intermediate.clone();
let k_intermediate_bytes = Zeroizing::new(keystore.open_sealed(
ACCOUNT_KEY_ENVELOPE_AD.to_vec(),
wrapped_k_intermediate,
)?);
let k_intermediate =
parse_key_32(k_intermediate_bytes.as_slice(), "K_intermediate")?;
Ok(Self {
intermediate_key: SecretBox::init_with(|| k_intermediate),
})
} else {
let k_intermediate = random_key();
// TODO: At this moment, the key needs to be temporarily heap allocated in order
// to be bridged via UniFFI. This needs to be improved to use pointers that can
// be zeroized after use.
let wrapped_k_intermediate = keystore
.seal(ACCOUNT_KEY_ENVELOPE_AD.to_vec(), k_intermediate.to_vec())?;
let envelope = AccountKeyEnvelope::new(wrapped_k_intermediate, now);
let bytes = envelope.serialize()?;
blob_store.write_atomic(ACCOUNT_KEYS_FILENAME.to_string(), bytes)?;
Ok(Self {
intermediate_key: SecretBox::init_with(|| k_intermediate),
})
}
let keystore_adapter = DeviceKeystoreAdapter::new(keystore);
let blob_store_adapter = AtomicBlobStoreAdapter::new(blob_store);
let intermediate_key = init_or_open_envelope_key(
&keystore_adapter,
&blob_store_adapter,
ACCOUNT_KEYS_FILENAME,
ACCOUNT_KEY_ENVELOPE_AD,
lock,
now,
)?;
Ok(Self { intermediate_key })
}

/// Returns a reference to the intermediate key's [`SecretBox`].
Expand All @@ -69,29 +60,12 @@ impl StorageKeys {
}
}

fn random_key() -> [u8; 32] {
let mut key = [0u8; 32];
OsRng.fill_bytes(&mut key);
key
}

fn parse_key_32(bytes: &[u8], label: &str) -> StorageResult<[u8; 32]> {
if bytes.len() != 32 {
return Err(StorageError::InvalidEnvelope(format!(
"{label} length mismatch: expected 32, got {}",
bytes.len()
)));
}
let mut out = [0u8; 32];
out.copy_from_slice(bytes);
Ok(out)
}

#[cfg(test)]
mod tests {
use super::*;
use crate::storage::lock::StorageLock;
use crate::storage::tests_utils::{InMemoryBlobStore, InMemoryKeystore};
use crate::storage::error::StorageError;
use secrecy::ExposeSecret;
use uuid::Uuid;

Expand Down
Loading
Loading