1use crate::errors::Result;
2use crate::keypair::KeyPair;
3use crate::public_key::PublicKey;
4use crate::secret_key::SecretKey;
5use age::secrecy::ExposeSecret;
6use age::x25519::Identity;
7
8#[must_use = "generating a key pair is an expensive operation; consider reusing the result"]
41pub fn build_keypair() -> Result<KeyPair> {
42 let identity = Identity::generate();
43 let recipient = identity.to_public();
44 let public_raw = recipient.to_string();
45 let secret_raw = identity.to_string().expose_secret().to_string();
46 let public = PublicKey::new(public_raw)?;
47 let secret = SecretKey::new(secret_raw)?;
48 Ok(KeyPair::new(public, secret))
49}
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54
55 #[test]
56 fn generated_keypair_has_valid_format() {
57 let kp = build_keypair().unwrap();
58 assert!(kp.public.expose().starts_with("age1"));
59 assert!(kp.secret.expose_secret().starts_with("AGE-SECRET-KEY-1"));
60 }
61
62 #[test]
63 fn generated_keypairs_are_random() {
64 let kp1 = build_keypair().unwrap();
65 let kp2 = build_keypair().unwrap();
66 assert_ne!(kp1.public.expose(), kp2.public.expose());
67 assert_ne!(kp1.secret.expose_secret(), kp2.secret.expose_secret());
68 }
69
70 #[test]
71 fn secret_is_not_leaked() {
72 let kp = build_keypair().unwrap();
73 let debug = format!("{:?}", kp);
74 assert!(!debug.contains(kp.secret.expose_secret()));
75 }
76
77 #[test]
78 fn keys_have_body_after_prefix() {
79 let kp = build_keypair().unwrap();
80 assert!(kp.public.expose().len() > "age1".len());
81 assert!(kp.secret.expose_secret().len() > "AGE-SECRET-KEY-1".len());
82 }
83}