Skip to main content

age_setup/
keypair.rs

1//! Age key pair.
2//!
3//! This module defines the [`KeyPair`] type, which bundles a validated
4//! [`PublicKey`] with its corresponding [`SecretKey`]. Key pairs are created
5//! via the [`build_keypair`](crate::build_keypair) function, which handles
6//! generation and validation, ensuring that the two keys are mathematically
7//! related and conform to the age specification.
8
9use crate::public_key::PublicKey;
10use crate::secret_key::SecretKey;
11
12/// An age X25519 key pair consisting of a public key and a secret key.
13///
14/// `KeyPair` is the central type of this crate. It is produced by
15/// [`build_keypair`](crate::build_keypair) and provides access to both the
16/// public identity (safe to share) and the secret identity (must be kept
17/// confidential).
18///
19/// # Fields
20///
21/// * `public: PublicKey` – The public key. It is guaranteed to start with
22///   `"age1"` and can be safely displayed, cloned, and shared.
23/// * `secret: SecretKey` – The secret key. It is automatically zeroized when
24///   dropped, and its `Display` and `Debug` implementations redact the actual
25///   key material.
26///
27/// # Creation
28///
29/// `KeyPair` cannot be constructed directly from outside the crate because
30/// its constructor is `pub(crate)`. This ensures that all key pairs are
31/// produced by [`build_keypair`], which properly generates a fresh identity
32/// and validates both keys.
33///
34/// # Examples
35///
36/// ```rust
37/// use age_setup::build_keypair;
38///
39/// let kp = build_keypair()?;
40/// println!("Public key: {}", kp.public);
41/// // Secret key is redacted when printed:
42/// println!("Secret key: {}", kp.secret);   // prints [REDACTED]
43/// # Ok::<(), age_setup::Error>(())
44/// ```
45#[derive(Debug)]
46pub struct KeyPair {
47    /// The public half of the key pair.
48    pub public: PublicKey,
49    /// The secret half of the key pair (zeroized on drop).
50    pub secret: SecretKey,
51}
52
53impl KeyPair {
54    /// Creates a new `KeyPair` from an already validated public and secret key.
55    ///
56    /// This constructor is `pub(crate)` – only accessible within the crate.
57    /// External users obtain a `KeyPair` exclusively through
58    /// [`build_keypair`](crate::build_keypair), which performs the necessary
59    /// generation and validation steps.
60    pub(crate) fn new(public: PublicKey, secret: SecretKey) -> Self {
61        Self { public, secret }
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use crate::build_keypair;
68
69    /// Verifies that a freshly generated key pair has fields that are
70    /// accessible and conform to the expected format.
71    #[test]
72    fn generated_keypair_fields_are_valid() {
73        let kp = build_keypair().unwrap();
74        assert!(kp.public.expose().starts_with("age1"));
75        assert!(kp.secret.expose_secret().starts_with("AGE-SECRET-KEY-1"));
76    }
77
78    /// Ensures that the public and secret keys of a newly generated
79    /// `KeyPair` are non‑empty.
80    #[test]
81    fn generated_keypair_fields_are_non_empty() {
82        let kp = build_keypair().unwrap();
83        assert!(!kp.public.expose().is_empty());
84        assert!(!kp.secret.expose_secret().is_empty());
85    }
86
87    /// The `Debug` representation of a `KeyPair` must not expose the
88    /// secret key (it should be redacted by `SecretKey`'s `Debug` impl).
89    #[test]
90    fn debug_does_not_leak_secret() {
91        let kp = build_keypair().unwrap();
92        let debug_str = format!("{:?}", kp);
93        // The debug output should contain the public key (it is safe) ...
94        assert!(debug_str.contains(kp.public.expose()));
95        // ... but NOT the raw secret key material.
96        assert!(!debug_str.contains(kp.secret.expose_secret()));
97        // It should mention the redacted marker instead.
98        assert!(debug_str.contains("[REDACTED]"));
99    }
100}