memory.rs (8246B)
1 //! Deterministic in-process secret adapters. 2 3 use crate::SecretRef; 4 use crate::error::{Error, Operation}; 5 use crate::id::BackendKind; 6 use crate::provider::{CapabilitySupport, ResidencySupport, SecretCapabilities, SecretProvider}; 7 use crate::wrapping::{ 8 BoxFuture, KeyWrapping, LegacyV1UnwrapRequest, SecretMaterial, UnwrapRequest, WrapRequest, 9 WrappedSecret, 10 }; 11 use alloc::collections::BTreeMap; 12 use alloc::string::{String, ToString}; 13 use alloc::vec::Vec; 14 use core::fmt; 15 use std::sync::RwLock; 16 17 const TOKEN_MAGIC: &[u8] = b"radroots-memory-key-v1\0"; 18 19 /// Explicit-lifecycle, in-process provider for development and tests. 20 #[derive(Default)] 21 pub struct MemoryProvider { 22 entries: RwLock<BTreeMap<MemoryKey, SecretMaterial>>, 23 } 24 25 impl MemoryProvider { 26 /// Creates an empty provider without generating or installing keys. 27 #[must_use] 28 pub fn new() -> Self { 29 Self::default() 30 } 31 32 /// Installs caller-owned material at one exact reference. 33 pub fn provision(&self, reference: &SecretRef, material: SecretMaterial) -> Result<(), Error> { 34 validate_memory_reference(reference)?; 35 let key = MemoryKey::from_reference(reference); 36 let mut entries = self 37 .entries 38 .write() 39 .map_err(|_| backend_failure(Operation::Provision))?; 40 if entries.contains_key(&key) { 41 return Err(Error::SecretAlreadyExists { 42 backend: BackendKind::Memory, 43 key_version: reference.key_version().get(), 44 }); 45 } 46 entries.insert(key, material); 47 Ok(()) 48 } 49 50 /// Atomically replaces one version with a higher version of the same ID. 51 pub fn rotate( 52 &self, 53 current: &SecretRef, 54 next: &SecretRef, 55 material: SecretMaterial, 56 ) -> Result<(), Error> { 57 validate_memory_reference(current)?; 58 validate_memory_reference(next)?; 59 if current.id().as_str() != next.id().as_str() 60 || next.key_version() <= current.key_version() 61 { 62 return Err(Error::InvalidRotation); 63 } 64 let current_key = MemoryKey::from_reference(current); 65 let next_key = MemoryKey::from_reference(next); 66 let mut entries = self 67 .entries 68 .write() 69 .map_err(|_| backend_failure(Operation::Rotate))?; 70 if !entries.contains_key(¤t_key) { 71 return Err(not_found(current)); 72 } 73 if entries.contains_key(&next_key) { 74 return Err(Error::SecretAlreadyExists { 75 backend: BackendKind::Memory, 76 key_version: next.key_version().get(), 77 }); 78 } 79 entries.remove(¤t_key); 80 entries.insert(next_key, material); 81 Ok(()) 82 } 83 84 /// Removes and zeroizes provider-owned material when present. 85 pub fn remove(&self, reference: &SecretRef) -> Result<bool, Error> { 86 validate_memory_reference(reference)?; 87 self.entries 88 .write() 89 .map(|mut entries| { 90 entries 91 .remove(&MemoryKey::from_reference(reference)) 92 .is_some() 93 }) 94 .map_err(|_| backend_failure(Operation::Remove)) 95 } 96 97 /// Reports whether one exact reference is provisioned. 98 pub fn contains(&self, reference: &SecretRef) -> Result<bool, Error> { 99 validate_memory_reference(reference)?; 100 self.entries 101 .read() 102 .map(|entries| entries.contains_key(&MemoryKey::from_reference(reference))) 103 .map_err(|_| backend_failure(Operation::Unwrap)) 104 } 105 106 fn wrapped_token( 107 reference: &SecretRef, 108 context: &crate::context::EnvelopeContext, 109 ) -> Result<WrappedSecret, Error> { 110 let mut token = Self::legacy_wrapped_token(reference)?; 111 token.extend_from_slice(&context.authentication_digest()); 112 WrappedSecret::from_bytes(token) 113 } 114 115 fn legacy_wrapped_token(reference: &SecretRef) -> Result<Vec<u8>, Error> { 116 let id = reference.id().as_str().as_bytes(); 117 let mut token = Vec::with_capacity(TOKEN_MAGIC.len() + 4 + 2 + id.len()); 118 token.extend_from_slice(TOKEN_MAGIC); 119 token.extend_from_slice(&reference.key_version().get().to_be_bytes()); 120 let id_len = u16::try_from(id.len()).map_err(|_| backend_failure(Operation::Wrap))?; 121 token.extend_from_slice(&id_len.to_be_bytes()); 122 token.extend_from_slice(id); 123 Ok(token) 124 } 125 126 fn clone_material( 127 &self, 128 reference: &SecretRef, 129 operation: Operation, 130 ) -> Result<SecretMaterial, Error> { 131 let entries = self 132 .entries 133 .read() 134 .map_err(|_| backend_failure(operation))?; 135 let material = entries 136 .get(&MemoryKey::from_reference(reference)) 137 .ok_or_else(|| not_found(reference))?; 138 material.expose_secret(SecretMaterial::from_slice) 139 } 140 } 141 142 impl fmt::Debug for MemoryProvider { 143 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 144 formatter.write_str("MemoryProvider(<redacted>)") 145 } 146 } 147 148 impl KeyWrapping for MemoryProvider { 149 fn wrap<'a>(&'a self, request: WrapRequest<'a>) -> BoxFuture<'a, Result<WrappedSecret, Error>> { 150 Box::pin(async move { 151 validate_memory_reference(request.reference())?; 152 let provisioned = self.clone_material(request.reference(), Operation::Wrap)?; 153 let matches = provisioned.expose_secret(|expected| { 154 request 155 .plaintext() 156 .expose_secret(|actual| expected == actual) 157 }); 158 if !matches { 159 return Err(backend_failure(Operation::Wrap)); 160 } 161 Self::wrapped_token(request.reference(), request.context()) 162 }) 163 } 164 165 fn unwrap<'a>( 166 &'a self, 167 request: UnwrapRequest<'a>, 168 ) -> BoxFuture<'a, Result<SecretMaterial, Error>> { 169 Box::pin(async move { 170 validate_memory_reference(request.reference())?; 171 let expected = Self::wrapped_token(request.reference(), request.context())?; 172 if expected.as_bytes() != request.wrapped().as_bytes() { 173 return Err(backend_failure(Operation::Unwrap)); 174 } 175 self.clone_material(request.reference(), Operation::Unwrap) 176 }) 177 } 178 179 fn unwrap_legacy_v1<'a>( 180 &'a self, 181 request: LegacyV1UnwrapRequest<'a>, 182 ) -> BoxFuture<'a, Result<SecretMaterial, Error>> { 183 Box::pin(async move { 184 validate_memory_reference(request.reference())?; 185 if request.wrapped().as_bytes() 186 != Self::legacy_wrapped_token(request.reference())?.as_slice() 187 { 188 return Err(backend_failure(Operation::Unwrap)); 189 } 190 self.clone_material(request.reference(), Operation::Unwrap) 191 }) 192 } 193 } 194 195 impl SecretProvider for MemoryProvider { 196 fn backend_kind(&self) -> BackendKind { 197 BackendKind::Memory 198 } 199 200 fn capabilities(&self) -> SecretCapabilities { 201 SecretCapabilities::available( 202 ResidencySupport::Volatile, 203 CapabilitySupport::Unavailable, 204 CapabilitySupport::Unavailable, 205 ) 206 } 207 } 208 209 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] 210 struct MemoryKey { 211 id: String, 212 version: u32, 213 } 214 215 impl MemoryKey { 216 fn from_reference(reference: &SecretRef) -> Self { 217 Self { 218 id: reference.id().as_str().to_string(), 219 version: reference.key_version().get(), 220 } 221 } 222 } 223 224 fn validate_memory_reference(reference: &SecretRef) -> Result<(), Error> { 225 if reference.backend() != BackendKind::Memory { 226 return Err(Error::BackendMismatch { 227 provider: BackendKind::Memory, 228 reference: reference.backend(), 229 }); 230 } 231 Ok(()) 232 } 233 234 const fn backend_failure(operation: Operation) -> Error { 235 Error::BackendFailure { 236 backend: BackendKind::Memory, 237 operation, 238 } 239 } 240 241 fn not_found(reference: &SecretRef) -> Error { 242 Error::SecretNotFound { 243 backend: BackendKind::Memory, 244 key_version: reference.key_version().get(), 245 } 246 }