Skip to main content

age_setup/
secret_key.rs

1use crate::errors::{Error, Result, ValidationError};
2use std::fmt;
3use zeroize::Zeroizing;
4#[derive(Clone)]
5pub struct SecretKey {
6    inner: Zeroizing<String>,
7}
8impl SecretKey {
9    pub fn new(raw: String) -> Result<Self> {
10        if raw.is_empty() {
11            return Err(Error::from(ValidationError::invalid_secret_key(
12                "Secret key is empty",
13            )));
14        }
15        if !raw.starts_with("AGE-SECRET-KEY-1") {
16            return Err(Error::from(ValidationError::invalid_secret_key(
17                "Secret key must start with 'AGE-SECRET-KEY-1'",
18            )));
19        }
20        Ok(Self {
21            inner: Zeroizing::new(raw),
22        })
23    }
24    #[must_use]
25    pub fn expose_secret(&self) -> &str {
26        &self.inner
27    }
28}
29impl fmt::Debug for SecretKey {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        f.debug_struct("SecretKey")
32            .field("value", &"[REDACTED]")
33            .finish()
34    }
35}
36impl fmt::Display for SecretKey {
37    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38        write!(f, "[REDACTED]")
39    }
40}
41#[cfg(test)]
42mod tests {
43    use super::*;
44    #[test]
45    fn valid() {
46        let sk = SecretKey::new("AGE-SECRET-KEY-1TEST".into()).unwrap();
47        assert_eq!(sk.expose_secret(), "AGE-SECRET-KEY-1TEST");
48    }
49    #[test]
50    fn debug_redacted() {
51        let sk = SecretKey::new("AGE-SECRET-KEY-1TEST".into()).unwrap();
52        let d = format!("{:?}", sk);
53        assert!(d.contains("[REDACTED]"));
54        assert!(!d.contains("TEST"));
55    }
56}