diff --git a/crates/enclaveapp-app-storage/Cargo.toml b/crates/enclaveapp-app-storage/Cargo.toml index eed2d36..c331e0d 100644 --- a/crates/enclaveapp-app-storage/Cargo.toml +++ b/crates/enclaveapp-app-storage/Cargo.toml @@ -12,16 +12,16 @@ workspace = true [features] default = [] -mock = ["aes-gcm", "rand"] [dependencies] enclaveapp-core = { workspace = true } thiserror = { workspace = true } tracing = "0.1" -# Mock backend (feature-gated) -aes-gcm = { workspace = true, optional = true } -rand = { workspace = true, optional = true } +# Mock backend — always compiled so `ENCLAVEAPP_MOCK_STORAGE=1` can +# swap it in at runtime without any feature flag gymnastics. +aes-gcm = { workspace = true } +rand = { workspace = true } [dev-dependencies] enclaveapp-test-support = { workspace = true } diff --git a/crates/enclaveapp-app-storage/src/lib.rs b/crates/enclaveapp-app-storage/src/lib.rs index 540849c..857e659 100644 --- a/crates/enclaveapp-app-storage/src/lib.rs +++ b/crates/enclaveapp-app-storage/src/lib.rs @@ -49,7 +49,6 @@ pub mod encryption; pub mod error; -#[cfg(feature = "mock")] pub mod mock; pub mod platform; pub mod signing; @@ -57,6 +56,7 @@ pub mod signing; // Re-export primary types for consumers. pub use encryption::{AppEncryptionStorage, EncryptionStorage}; pub use error::{Result, StorageError}; +pub use mock::MockEncryptionStorage; pub use platform::BackendKind; pub use signing::AppSigningBackend; @@ -88,9 +88,34 @@ pub struct StorageConfig { pub force_keyring: bool, } +/// Environment variable that, when set to a non-empty value, forces +/// [`create_encryption_storage`] to return a [`MockEncryptionStorage`] +/// instead of the real platform backend. +/// +/// This is the sanctioned escape hatch for CI environments that +/// cannot satisfy a real hardware-backed backend — typically GitHub +/// Actions macOS runners, which would otherwise block indefinitely +/// on a login-keychain ACL confirmation prompt. It is **not** a +/// production-safe mode: the mock uses a random in-memory AES key +/// and provides no hardware backing. +pub const MOCK_STORAGE_ENV: &str = "ENCLAVEAPP_MOCK_STORAGE"; + /// Create encryption storage with automatic platform detection. -/// Returns a trait object for use with mock backends in tests. +/// +/// Honours [`MOCK_STORAGE_ENV`]: when that env var is set to a +/// non-empty value the returned box wraps a fresh +/// [`MockEncryptionStorage`]. Callers get a uniform +/// [`EncryptionStorage`] trait object either way. pub fn create_encryption_storage(config: StorageConfig) -> Result> { + if let Ok(val) = std::env::var(MOCK_STORAGE_ENV) { + if !val.is_empty() { + tracing::warn!( + app = %config.app_name, + "{MOCK_STORAGE_ENV} is set — returning MockEncryptionStorage (no hardware backing)" + ); + return Ok(Box::new(MockEncryptionStorage::new())); + } + } let storage = AppEncryptionStorage::init(config)?; Ok(Box::new(storage)) } diff --git a/crates/enclaveapp-app-storage/src/mock.rs b/crates/enclaveapp-app-storage/src/mock.rs index 3ae82d7..8ddec10 100644 --- a/crates/enclaveapp-app-storage/src/mock.rs +++ b/crates/enclaveapp-app-storage/src/mock.rs @@ -6,6 +6,11 @@ //! Uses AES-256-GCM with a random in-memory key. Provides no hardware //! backing; suitable only for tests and development. +// aes-gcm 0.10 still uses generic-array 0.14 internally and emits a +// deprecation notice on `Nonce::from_slice` — the 1.x migration is +// upstream work, silence here so `-D warnings` doesn't trip. +#![allow(deprecated)] + use aes_gcm::aead::Aead; use aes_gcm::{Aes256Gcm, KeyInit, Nonce}; use rand::RngCore;