age_setup/keypair.rs
1use crate::public_key::PublicKey;
2use crate::secret_key::SecretKey;
3
4/// A cryptographic key pair for the age protocol.
5///
6/// Contains a [`PublicKey`] and a [`SecretKey`], both guaranteed to be valid
7/// age keys. The [`Debug`] implementation redacts the secret key value while
8/// displaying the public key in full.
9///
10/// # Obtaining a KeyPair
11///
12/// Use [`build_keypair`](crate::build_keypair) to generate a fresh key pair:
13///
14/// ```no_run
15/// use age_setup::build_keypair;
16///
17/// let kp = build_keypair()?;
18/// println!("Public: {}", kp.public);
19/// # Ok::<(), age_setup::Error>(())
20/// ```
21///
22/// # Debug Safety
23///
24/// The debug representation does **not** leak the secret key:
25///
26/// ```rust
27/// use age_setup::build_keypair;
28///
29/// let kp = build_keypair()?;
30/// let debug_str = format!("{:?}", kp);
31/// assert!(debug_str.contains(kp.public.expose()));
32/// assert!(!debug_str.contains(kp.secret.expose_secret()));
33/// # Ok::<(), age_setup::Error>(())
34/// ```
35///
36/// # See Also
37///
38/// * [`build_keypair`](crate::build_keypair) – Generates a new `KeyPair`.
39/// * [`SecretKey`](crate::SecretKey) – Zeroizing secret key wrapper.
40/// * [`PublicKey`](crate::PublicKey) – Validated public key wrapper.
41#[derive(Debug)]
42pub struct KeyPair {
43 /// The public key component.
44 pub public: PublicKey,
45 /// The secret key component (redacted in debug output).
46 pub secret: SecretKey,
47}
48
49impl KeyPair {
50 /// Creates a new `KeyPair` from existing keys.
51 ///
52 /// This constructor is crate-internal. External users should call
53 /// [`build_keypair`](crate::build_keypair) to generate a new pair.
54 ///
55 /// # Parameters
56 ///
57 /// * `public` – A validated [`PublicKey`].
58 /// * `secret` – A validated [`SecretKey`].
59 pub(crate) fn new(public: PublicKey, secret: SecretKey) -> Self {
60 Self { public, secret }
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use crate::build_keypair;
67
68 #[test]
69 fn generated_keypair_fields_are_valid() {
70 let kp = build_keypair().unwrap();
71 assert!(kp.public.expose().starts_with("age1"));
72 assert!(kp.secret.expose_secret().starts_with("AGE-SECRET-KEY-1"));
73 }
74
75 #[test]
76 fn generated_keypair_fields_are_non_empty() {
77 let kp = build_keypair().unwrap();
78 assert!(!kp.public.expose().is_empty());
79 assert!(!kp.secret.expose_secret().is_empty());
80 }
81
82 #[test]
83 fn debug_does_not_leak_secret() {
84 let kp = build_keypair().unwrap();
85 let debug_str = format!("{:?}", kp);
86 assert!(debug_str.contains(kp.public.expose()));
87 assert!(!debug_str.contains(kp.secret.expose_secret()));
88 assert!(debug_str.contains("[REDACTED]"));
89 }
90}