Skip to main content

kovra_core/
secret.rs

1//! In-memory secret value: zeroized on drop, never printed.
2
3use secrecy::{ExposeSecret, SecretBox};
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5
6/// A secret value held in protected memory.
7///
8/// Wraps [`secrecy::SecretBox`] so the bytes are zeroized on drop and never
9/// appear in `Debug` output; there is **no** `Display` impl by design (I12).
10/// The value is reachable only via [`SecretValue::expose`], which callers must
11/// invoke deliberately.
12///
13/// The `serde` impls (de)serialize the raw bytes and exist **only** so a record
14/// can be serialized into the buffer that is immediately AEAD-sealed (see
15/// [`crate::crypto`]); the plaintext serialization is never persisted or logged.
16pub struct SecretValue(SecretBox<Vec<u8>>);
17
18impl SecretValue {
19    /// Wrap raw secret bytes.
20    pub fn new(bytes: Vec<u8>) -> Self {
21        Self(SecretBox::new(Box::new(bytes)))
22    }
23
24    /// Borrow the protected bytes. Use deliberately — this is the one path out.
25    pub fn expose(&self) -> &[u8] {
26        self.0.expose_secret()
27    }
28}
29
30impl From<String> for SecretValue {
31    fn from(s: String) -> Self {
32        Self::new(s.into_bytes())
33    }
34}
35
36impl From<&str> for SecretValue {
37    fn from(s: &str) -> Self {
38        Self::new(s.as_bytes().to_vec())
39    }
40}
41
42/// Redacted `Debug`: never reveals the value (I12). No `Display` impl exists.
43impl core::fmt::Debug for SecretValue {
44    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
45        f.write_str("SecretValue(REDACTED)")
46    }
47}
48
49impl PartialEq for SecretValue {
50    fn eq(&self, other: &Self) -> bool {
51        self.expose() == other.expose()
52    }
53}
54
55impl Serialize for SecretValue {
56    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
57        self.expose().serialize(serializer)
58    }
59}
60
61impl<'de> Deserialize<'de> for SecretValue {
62    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
63        let bytes = Vec::<u8>::deserialize(deserializer)?;
64        Ok(Self::new(bytes))
65    }
66}