pub struct LocalKms { /* private fields */ }Expand description
File-based KEK store for dev / on-prem deployments.
§Layout
<dir>/
alpha.kek # 32 raw bytes — KEK for key_id "alpha"
beta.kek # 32 raw bytes — KEK for key_id "beta"Files are loaded eagerly at LocalKms::open time; subsequent
adds/removals require a restart. KEK files MUST be exactly 32
bytes (other formats — hex / base64 — are intentionally not
accepted here, unlike crate::sse::SseKey, because operators
generating KEKs for KMS use should produce raw randomness from
/dev/urandom rather than human-edited files).
§Wrap algorithm
LocalKms wraps DEKs with AES-256-GCM using the KEK as the cipher
key. The wrapped form is nonce(12) || ciphertext(32) || tag(16)
= 60 bytes for a 32-byte DEK. The nonce is fresh per wrap, drawn
from OsRng; the AAD is the UTF-8 key_id so a wrap under one id
can’t be replayed under another.
Implementations§
Source§impl LocalKms
impl LocalKms
Sourcepub fn open(dir: PathBuf) -> Result<Self, KmsError>
pub fn open(dir: PathBuf) -> Result<Self, KmsError>
Open a KEK directory. Reads every *.kek file; each must be
exactly 32 raw bytes. The basename (sans .kek) becomes the
key_id used in KmsBackend::generate_dek / WrappedDek.
An empty directory is a valid (but useless) state — callers
that haven’t loaded any KEKs will still see all generate_dek
calls return KmsError::KeyNotFound.
Trait Implementations§
Source§impl KmsBackend for LocalKms
impl KmsBackend for LocalKms
Source§fn generate_dek<'life0, 'life1, 'async_trait>(
&'life0 self,
key_id: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<(Zeroizing<Vec<u8>>, WrappedDek), KmsError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn generate_dek<'life0, 'life1, 'async_trait>(
&'life0 self,
key_id: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<(Zeroizing<Vec<u8>>, WrappedDek), KmsError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§fn decrypt_dek<'life0, 'life1, 'async_trait>(
&'life0 self,
wrapped: &'life1 WrappedDek,
) -> Pin<Box<dyn Future<Output = Result<Zeroizing<Vec<u8>>, KmsError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn decrypt_dek<'life0, 'life1, 'async_trait>(
&'life0 self,
wrapped: &'life1 WrappedDek,
) -> Pin<Box<dyn Future<Output = Result<Zeroizing<Vec<u8>>, KmsError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Zeroizing<Vec<u8>> so the
plaintext is wiped on Drop; callers in this crate also copy
it into a stack [u8; 32] (also Zeroizing-wrapped at the
service.rs call sites) for the duration of one GET.