bitwarden_ssh/
generator.rs1use bitwarden_vault::SshKeyView;
2use rand::CryptoRng;
3use serde::{Deserialize, Serialize};
4use ssh_key::Algorithm;
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}
21
22pub fn generate_sshkey(
28 key_algorithm: KeyAlgorithm,
29) -> Result<SshKeyView, error::KeyGenerationError> {
30 let mut rng = rand::rng();
31 generate_sshkey_internal(key_algorithm, &mut rng)
32}
33
34fn generate_sshkey_internal<R: CryptoRng + ?Sized>(
35 key_algorithm: KeyAlgorithm,
36 rng: &mut R,
37) -> Result<SshKeyView, error::KeyGenerationError> {
38 let private_key = match key_algorithm {
39 KeyAlgorithm::Ed25519 => ssh_key::PrivateKey::random(rng, Algorithm::Ed25519)
40 .map_err(KeyGenerationError::KeyGeneration),
41 KeyAlgorithm::Rsa3072 => create_rsa_key(rng, 3072),
42 KeyAlgorithm::Rsa4096 => create_rsa_key(rng, 4096),
43 }?;
44
45 ssh_private_key_to_view(private_key).map_err(|_| KeyGenerationError::KeyConversion)
46}
47
48fn create_rsa_key<R: CryptoRng + ?Sized>(
49 rng: &mut R,
50 bits: usize,
51) -> Result<ssh_key::PrivateKey, error::KeyGenerationError> {
52 let rsa_keypair = ssh_key::private::RsaKeypair::random(rng, bits)
53 .map_err(KeyGenerationError::KeyGeneration)?;
54 let private_key =
55 ssh_key::PrivateKey::new(ssh_key::private::KeypairData::from(rsa_keypair), "")
56 .map_err(KeyGenerationError::KeyGeneration)?;
57 Ok(private_key)
58}
59
60#[cfg(test)]
61mod tests {
62 use rand::SeedableRng;
63
64 use super::KeyAlgorithm;
65 use crate::generator::generate_sshkey_internal;
66
67 #[test]
68 fn generate_ssh_key_ed25519() {
69 let mut rng = rand_chacha::ChaCha12Rng::from_seed([0u8; 32]);
70 let key_algorithm = KeyAlgorithm::Ed25519;
71 let result = generate_sshkey_internal(key_algorithm, &mut rng);
72 let target = include_str!("../resources/generator/ed25519_key").replace("\r\n", "\n");
73 assert_eq!(result.unwrap().private_key, target);
74 }
75
76 #[test]
77 fn generate_ssh_key_rsa3072() {
78 let mut rng = rand_chacha::ChaCha12Rng::from_seed([0u8; 32]);
79 let key_algorithm = KeyAlgorithm::Rsa3072;
80 let result = generate_sshkey_internal(key_algorithm, &mut rng);
81 let target = include_str!("../resources/generator/rsa3072_key").replace("\r\n", "\n");
82 assert_eq!(result.unwrap().private_key, target);
83 }
84
85 #[test]
86 fn generate_ssh_key_rsa4096() {
87 let mut rng = rand_chacha::ChaCha12Rng::from_seed([0u8; 32]);
88 let key_algorithm = KeyAlgorithm::Rsa4096;
89 let result = generate_sshkey_internal(key_algorithm, &mut rng);
90 let target = include_str!("../resources/generator/rsa4096_key").replace("\r\n", "\n");
91 assert_eq!(result.unwrap().private_key, target);
92 }
93}