1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2026-present, Structured World Foundation
//! Key chain: caller-side registry mapping `KeyEpoch` → 32-byte key.
//!
//! Per the AAD-bound block format (`docs/aad-block-format.md` §5.1),
//! every encrypted block records a 1-byte `KeyEpoch` in its
//! `MetadataFrame`. On read the decoder uses the epoch to look up
//! the right 32-byte key from the caller's key chain; on write the
//! caller chooses which epoch to use (by setting
//! `EncryptionContext::key_epoch` before calling `encrypt_block`)
//! and the encoder records that choice into the frame. The
//! [`KeyChain`] trait itself has no "active epoch" concept — it is
//! a passive lookup table from `KeyEpoch` to key bytes; epoch
//! selection / rotation policy lives in the caller (typically a
//! key-management service or a configuration loader).
//!
//! The chain is caller-managed (not stored in the LSM): different
//! deployments have different key-rotation policies (hardware HSM,
//! cloud KMS, file-backed, in-memory), and the LSM only needs the
//! single trait method `key(epoch) -> Option<&[u8; 32]>` to seal
//! and unseal. Concrete implementations (a KMS-backed chain, a
//! file-backed chain, a one-shot chain for tests) live outside the
//! LSM crate; this module ships only the trait and an in-memory
//! reference impl that the LSM's own tests use.
use HashMap;
/// Caller-supplied registry mapping `KeyEpoch` bytes to the 32-byte
/// AEAD keys the LSM uses to seal / unseal blocks.
///
/// The trait carries only the lookup method; key rotation, key
/// derivation, and key storage are all caller concerns.
///
/// `key(epoch)` returning `None` surfaces as
/// [`crate::encryption::error::DecryptError::UnknownKeyEpoch`] on
/// the read path — distinct from
/// [`crate::encryption::error::DecryptError::AeadVerificationFailed`]
/// so operators can tell key-rotation drift apart from active
/// tampering.
/// In-memory `KeyChain` keyed by [`u8`] → 32-byte key.
///
/// Lives in this crate for the LSM's own unit / integration tests
/// and for deployments that load keys from a single file at
/// startup and never rotate at runtime (the simplest production
/// shape). KMS-backed chains, file-backed chains with
/// hot-rotation, etc. implement [`KeyChain`] outside the LSM.