key_vault/metadata.rs
1//! Public, non-secret metadata associated with a registered key.
2//!
3//! [`KeyMetadata`] is the information *about* a key that callers are allowed to
4//! see: when it was registered, how long the underlying material is, an
5//! optional hint about which algorithm family it belongs to. None of these
6//! fields contain key bytes; all of them are safe to log.
7//!
8//! Anything that would identify the *value* of the key (raw bytes, fragments,
9//! decoy bytes, codex tables) lives elsewhere and is unreachable through
10//! `KeyMetadata`.
11
12use core::time::Duration;
13
14/// Hint about which cryptographic algorithm a stored key is intended for.
15///
16/// This is advisory only. The vault does not verify that the registered bytes
17/// are actually a valid key for the named algorithm — that is the caller's
18/// responsibility, and the [`KeyFetch`](crate::KeyFetch) implementation's
19/// responsibility for hardware-backed sources. The variant exists so that
20/// audit-trail records and security monitors can label events meaningfully.
21#[non_exhaustive]
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
23pub enum AlgorithmHint {
24 /// 128-bit symmetric key (e.g. AES-128 KEK).
25 Symmetric128,
26 /// 256-bit symmetric key (e.g. AES-256, ChaCha20).
27 Symmetric256,
28 /// Ed25519 signing key (32-byte seed).
29 Ed25519,
30 /// X25519 ECDH private key (32 bytes).
31 X25519,
32 /// NIST P-256 ECDSA private key.
33 P256,
34 /// NIST P-384 ECDSA private key.
35 P384,
36 /// RSA-2048 private key (DER-encoded).
37 Rsa2048,
38 /// RSA-3072 private key.
39 Rsa3072,
40 /// RSA-4096 private key.
41 Rsa4096,
42 /// HMAC key (length given by [`KeyMetadata::length`]).
43 Hmac,
44 /// Other — caller supplies their own meaning out-of-band.
45 Other,
46}
47
48/// Public, non-secret information about a registered key.
49///
50/// `KeyMetadata` is safe to log, send to monitors, and include in audit
51/// records. It contains no information from which the key value could be
52/// derived.
53///
54/// The `length` field reports the *raw* key length in bytes — the size of the
55/// material that was registered with the vault before fragmentation. It is not
56/// the size of the in-memory fragmented representation (which is larger and
57/// implementation-defined).
58#[derive(Debug, Clone)]
59pub struct KeyMetadata {
60 /// Time at which the key was registered with the vault, expressed as a
61 /// `Duration` since the [`UNIX_EPOCH`](std::time::UNIX_EPOCH).
62 ///
63 /// We use `Duration` instead of `SystemTime` so the type is portable to
64 /// `no_std` builds in the future. Callers that need a wall-clock
65 /// representation can reconstruct one via
66 /// `UNIX_EPOCH + metadata.registered_since_epoch()`.
67 registered_since_epoch: Duration,
68 /// Raw key length in bytes.
69 length: usize,
70 /// Optional hint for downstream audit and monitoring code.
71 algorithm: Option<AlgorithmHint>,
72}
73
74impl KeyMetadata {
75 /// Construct metadata from explicit fields.
76 ///
77 /// Crate-internal — produced by the vault at registration time.
78 #[allow(dead_code)] // produced by the vault when keys are registered in Phase 0.3.
79 #[must_use]
80 pub(crate) fn new(
81 registered_since_epoch: Duration,
82 length: usize,
83 algorithm: Option<AlgorithmHint>,
84 ) -> Self {
85 Self {
86 registered_since_epoch,
87 length,
88 algorithm,
89 }
90 }
91
92 /// Raw key length, in bytes.
93 ///
94 /// This is the length of the material that was originally registered, not
95 /// the size of the in-memory fragmented representation.
96 #[must_use]
97 pub fn length(&self) -> usize {
98 self.length
99 }
100
101 /// Algorithm hint, if one was provided at registration.
102 #[must_use]
103 pub fn algorithm(&self) -> Option<AlgorithmHint> {
104 self.algorithm
105 }
106
107 /// Time of registration, expressed as a `Duration` since the Unix epoch.
108 #[must_use]
109 pub fn registered_since_epoch(&self) -> Duration {
110 self.registered_since_epoch
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn fields_round_trip() {
120 let meta = KeyMetadata::new(
121 Duration::from_secs(1_700_000_000),
122 32,
123 Some(AlgorithmHint::Symmetric256),
124 );
125 assert_eq!(meta.length(), 32);
126 assert_eq!(meta.algorithm(), Some(AlgorithmHint::Symmetric256));
127 assert_eq!(
128 meta.registered_since_epoch(),
129 Duration::from_secs(1_700_000_000)
130 );
131 }
132
133 #[test]
134 fn algorithm_hint_is_optional() {
135 let meta = KeyMetadata::new(Duration::ZERO, 16, None);
136 assert!(meta.algorithm().is_none());
137 }
138}