keygen_openssh_test/
main.rs
1mod args;
2
3use args::{Args, KeyType};
4use tor_llcrypto::pk::ed25519::Ed25519PublicKey as _;
5use tor_llcrypto::util::rng::RngCompat;
6
7use std::fs;
8
9use clap::Parser;
10
11use ssh_key::private::{DsaKeypair, Ed25519Keypair, Ed25519PrivateKey, OpaqueKeypair};
12use ssh_key::public::{DsaPublicKey, Ed25519PublicKey, OpaquePublicKey};
13use ssh_key::{self, Algorithm, AlgorithmName, PrivateKey, PublicKey};
14use tor_basic_utils::test_rng::testing_rng;
15use tor_llcrypto::pk::{curve25519, ed25519};
16
17macro_rules! make_openssh_key {
19 ($kind:tt, $args:expr, $keypair:expr, $public:expr) => {{
20 let comment = $args.comment.clone().unwrap_or("test-key".into());
21 let openssh_key = ssh_key::public::PublicKey::new(
22 ssh_key::public::KeyData::$kind($public),
23 comment.clone(),
24 );
25 let openssh_private = ssh_key::private::PrivateKey::new(
26 ssh_key::private::KeypairData::$kind($keypair),
27 comment,
28 )
29 .unwrap();
30
31 (openssh_private, openssh_key)
32 }};
33}
34
35fn generate_expanded_ed25519(args: &Args) -> (PrivateKey, PublicKey) {
37 let algo = args
38 .algorithm
39 .clone()
40 .unwrap_or("ed25519-expanded@spec.torproject.org".into());
41 let algorithm_name = AlgorithmName::new(algo).unwrap();
42
43 let mut rng = testing_rng();
44 let ed25519_kp = ed25519::Keypair::generate(&mut rng);
45 let expanded_kp: ed25519::ExpandedKeypair = (&ed25519_kp).into();
46 let ssh_public = OpaquePublicKey::new(
47 expanded_kp.public().to_bytes().to_vec(),
48 Algorithm::Other(algorithm_name),
49 );
50 let keypair = OpaqueKeypair::new(
51 expanded_kp.to_secret_key_bytes().to_vec(),
52 ssh_public.clone(),
53 );
54
55 make_openssh_key!(Other, args, keypair, ssh_public)
56}
57
58fn generate_ed25519(args: &Args) -> (PrivateKey, PublicKey) {
60 let mut rng = testing_rng();
61 let ed25519_kp = ed25519::Keypair::generate(&mut rng);
62 let public_key_bytes: [u8; 32] = ed25519_kp
63 .public_key()
64 .to_bytes()
65 .to_vec()
66 .try_into()
67 .unwrap();
68 let public = Ed25519PublicKey(public_key_bytes);
69 let secret_key_bytes: [u8; 32] = ed25519_kp.to_bytes().to_vec().try_into().unwrap();
70 let private = Ed25519PrivateKey::from_bytes(&secret_key_bytes);
71 let keypair = Ed25519Keypair { public, private };
72
73 make_openssh_key!(Ed25519, args, keypair, public)
74}
75
76fn generate_dsa(args: &Args) -> (PrivateKey, PublicKey) {
78 let mut rng = RngCompat::new(testing_rng());
79 let keypair = DsaKeypair::random(&mut rng).unwrap();
80 let public = DsaPublicKey::from(&keypair);
81
82 make_openssh_key!(Dsa, args, keypair, public)
83}
84
85fn generate_x25519(args: &Args) -> (PrivateKey, PublicKey) {
87 let rng = testing_rng();
88 let x25519_sk = curve25519::StaticSecret::random_from_rng(rng);
89 let x25519_pk = curve25519::PublicKey::from(&x25519_sk);
90
91 let algo = args
92 .algorithm
93 .clone()
94 .unwrap_or("x25519@spec.torproject.org".into());
95 let algorithm_name = AlgorithmName::new(algo).unwrap();
96
97 let public = OpaquePublicKey::new(
98 x25519_pk.to_bytes().to_vec(),
99 Algorithm::Other(algorithm_name),
100 );
101 let keypair = OpaqueKeypair::new(x25519_sk.to_bytes().to_vec(), public.clone());
102
103 make_openssh_key!(Other, args, keypair, public)
104}
105
106fn main() {
107 let args = Args::parse();
108
109 let (gen_pub, gen_priv) = match (args.public, args.private) {
111 (false, false) => {
112 (true, true)
114 }
115 (gen_pub, gen_priv) => (gen_pub, gen_priv),
116 };
117
118 let (openssh_private, openssh_public) = match &args.key_type {
119 KeyType::ExpandedEd25519 => generate_expanded_ed25519(&args),
120 KeyType::Ed25519 => generate_ed25519(&args),
121 KeyType::Dsa => generate_dsa(&args),
122 KeyType::X25519 => generate_x25519(&args),
123 };
124
125 let public = openssh_public.to_openssh().unwrap();
126 let private = openssh_private
127 .to_openssh(ssh_key::LineEnding::LF)
128 .unwrap()
129 .to_string();
130
131 let pub_file = format!("{}.public", args.name);
132 let priv_file = format!("{}.private", args.name);
133
134 if gen_pub {
135 fs::write(&pub_file, public).unwrap();
136 println!("created {pub_file}");
137 }
138
139 if gen_priv {
140 fs::write(&priv_file, private).unwrap();
141 println!("created {priv_file}");
142 }
143}