Skip to main content

age_setup/types/
secret_key.rs

1//! Secret key type with automatic zeroization on drop.
2
3use crate::security::zeroize::wipe_memory;
4use std::fmt;
5
6/// An age secret key that securely wipes its memory when dropped.
7///
8/// The secret key is stored as bytes. The `Display` implementation redacts the value,
9/// showing `[REDACTED]`. Use `expose()` to get the raw string (use with caution).
10#[derive(Debug, Clone)]
11pub struct SecretKey {
12    inner: Vec<u8>,
13}
14
15impl SecretKey {
16    /// Creates a new secret key from a string (internal).
17    pub(crate) fn new(raw: String) -> Self {
18        Self {
19            inner: raw.into_bytes(),
20        }
21    }
22
23    /// Exposes the raw secret key as a string.
24    ///
25    /// # Panics
26    /// Panics if the inner bytes are not valid UTF-8 (should never happen because
27    /// the key is created from a valid UTF-8 string).
28    ///
29    /// # Security
30    /// Only use this when absolutely necessary, as it exposes the secret.
31    #[must_use]
32    pub fn expose(&self) -> &str {
33        std::str::from_utf8(&self.inner).expect("SecretKey inner buffer must be valid UTF-8")
34    }
35}
36
37impl Drop for SecretKey {
38    fn drop(&mut self) {
39        let _ = wipe_memory(&mut self.inner);
40    }
41}
42
43impl fmt::Display for SecretKey {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        write!(f, "[REDACTED]")
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52
53    #[test]
54    fn test_secret_key_expose() {
55        let sk = SecretKey::new("test".to_string());
56        assert_eq!(sk.expose(), "test");
57    }
58
59    #[test]
60    fn test_secret_key_display() {
61        let sk = SecretKey::new("test".to_string());
62        assert_eq!(format!("{}", sk), "[REDACTED]");
63    }
64
65    #[test]
66    fn test_secret_key_clone() {
67        let sk1 = SecretKey::new("secret".to_string());
68        let sk2 = sk1.clone();
69        assert_eq!(sk1.expose(), sk2.expose());
70    }
71
72    #[test]
73    fn test_secret_key_drop_calls_wipe() {
74        let sk = SecretKey::new("secret".to_string());
75        drop(sk); // Tidak panic, wipe_memory dipanggil
76    }
77}