bitwarden_ssh/
generator.rs1use rand::CryptoRng;
2use serde::{Deserialize, Serialize};
3use ssh_key::{Algorithm, EcdsaCurve};
4
5use crate::{
6 SshKeyData,
7 error::{self, KeyGenerationError},
8 ssh_private_key_to_data,
9};
10
11#[derive(Serialize, Deserialize)]
12#[bitwarden_ffi::wasm_record]
13#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
14pub enum KeyAlgorithm {
15 Ed25519,
16 Rsa3072,
17 Rsa4096,
18 EcdsaP256,
19 EcdsaP384,
20 EcdsaP521,
21}
22
23pub fn generate_sshkey(
29 key_algorithm: KeyAlgorithm,
30) -> Result<SshKeyData, error::KeyGenerationError> {
31 let mut rng = bitwarden_random::rng();
32 generate_sshkey_internal(key_algorithm, &mut rng)
33}
34
35fn generate_sshkey_internal<R: CryptoRng + ?Sized>(
36 key_algorithm: KeyAlgorithm,
37 rng: &mut R,
38) -> Result<SshKeyData, error::KeyGenerationError> {
39 let private_key = match key_algorithm {
40 KeyAlgorithm::Ed25519 => ssh_key::PrivateKey::random(rng, Algorithm::Ed25519)
41 .map_err(KeyGenerationError::KeyGeneration),
42 KeyAlgorithm::Rsa3072 => create_rsa_key(rng, 3072),
43 KeyAlgorithm::Rsa4096 => create_rsa_key(rng, 4096),
44 KeyAlgorithm::EcdsaP256 => ssh_key::PrivateKey::random(
45 rng,
46 Algorithm::Ecdsa {
47 curve: EcdsaCurve::NistP256,
48 },
49 )
50 .map_err(KeyGenerationError::KeyGeneration),
51 KeyAlgorithm::EcdsaP384 => ssh_key::PrivateKey::random(
52 rng,
53 Algorithm::Ecdsa {
54 curve: EcdsaCurve::NistP384,
55 },
56 )
57 .map_err(KeyGenerationError::KeyGeneration),
58 KeyAlgorithm::EcdsaP521 => ssh_key::PrivateKey::random(
59 rng,
60 Algorithm::Ecdsa {
61 curve: EcdsaCurve::NistP521,
62 },
63 )
64 .map_err(KeyGenerationError::KeyGeneration),
65 }?;
66
67 ssh_private_key_to_data(private_key).map_err(|_| KeyGenerationError::KeyConversion)
68}
69
70fn create_rsa_key<R: CryptoRng + ?Sized>(
71 rng: &mut R,
72 bits: usize,
73) -> Result<ssh_key::PrivateKey, error::KeyGenerationError> {
74 let rsa_keypair = ssh_key::private::RsaKeypair::random(rng, bits)
75 .map_err(KeyGenerationError::KeyGeneration)?;
76 let private_key =
77 ssh_key::PrivateKey::new(ssh_key::private::KeypairData::from(rsa_keypair), "")
78 .map_err(KeyGenerationError::KeyGeneration)?;
79 Ok(private_key)
80}
81
82#[cfg(test)]
83mod tests {
84 use rand::SeedableRng;
85
86 use super::KeyAlgorithm;
87 use crate::generator::generate_sshkey_internal;
88
89 #[test]
90 fn generate_ssh_key_ed25519() {
91 let mut rng = rand_chacha::ChaCha12Rng::from_seed([0u8; 32]);
92 let key_algorithm = KeyAlgorithm::Ed25519;
93 let result = generate_sshkey_internal(key_algorithm, &mut rng);
94 let target = include_str!("../resources/generator/ed25519_key").replace("\r\n", "\n");
95 assert_eq!(result.unwrap().private_key, target);
96 }
97
98 #[test]
99 fn generate_ssh_key_rsa3072() {
100 let mut rng = rand_chacha::ChaCha12Rng::from_seed([0u8; 32]);
101 let key_algorithm = KeyAlgorithm::Rsa3072;
102 let result = generate_sshkey_internal(key_algorithm, &mut rng);
103 let target = include_str!("../resources/generator/rsa3072_key").replace("\r\n", "\n");
104 assert_eq!(result.unwrap().private_key, target);
105 }
106
107 #[test]
108 fn generate_ssh_key_rsa4096() {
109 let mut rng = rand_chacha::ChaCha12Rng::from_seed([0u8; 32]);
110 let key_algorithm = KeyAlgorithm::Rsa4096;
111 let result = generate_sshkey_internal(key_algorithm, &mut rng);
112 let target = include_str!("../resources/generator/rsa4096_key").replace("\r\n", "\n");
113 assert_eq!(result.unwrap().private_key, target);
114 }
115
116 #[test]
117 fn generate_ssh_key_ecdsa_p256() {
118 let mut rng = rand_chacha::ChaCha12Rng::from_seed([0u8; 32]);
119 let key_algorithm = KeyAlgorithm::EcdsaP256;
120 let result = generate_sshkey_internal(key_algorithm, &mut rng);
121 let target = include_str!("../resources/generator/ecdsa_p256_key").replace("\r\n", "\n");
122 assert_eq!(result.unwrap().private_key, target);
123 }
124
125 #[test]
126 fn generate_ssh_key_ecdsa_p384() {
127 let mut rng = rand_chacha::ChaCha12Rng::from_seed([0u8; 32]);
128 let key_algorithm = KeyAlgorithm::EcdsaP384;
129 let result = generate_sshkey_internal(key_algorithm, &mut rng);
130 let target = include_str!("../resources/generator/ecdsa_p384_key").replace("\r\n", "\n");
131 assert_eq!(result.unwrap().private_key, target);
132 }
133
134 #[test]
135 fn generate_ssh_key_ecdsa_p521() {
136 let mut rng = rand_chacha::ChaCha12Rng::from_seed([0u8; 32]);
137 let key_algorithm = KeyAlgorithm::EcdsaP521;
138 let result = generate_sshkey_internal(key_algorithm, &mut rng);
139 let target = include_str!("../resources/generator/ecdsa_p521_key").replace("\r\n", "\n");
140 assert_eq!(result.unwrap().private_key, target);
141 }
142}