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