bitwarden_ssh/
generator.rs1use bitwarden_vault::SshKeyView;
2use serde::{Deserialize, Serialize};
3use ssh_key::{rand_core::CryptoRngCore, Algorithm};
4#[cfg(feature = "wasm")]
5use tsify_next::Tsify;
6
7use crate::{
8 error::{self, KeyGenerationError},
9 ssh_private_key_to_view,
10};
11
12#[derive(Serialize, Deserialize)]
13#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
14#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
15pub enum KeyAlgorithm {
16 Ed25519,
17 Rsa3072,
18 Rsa4096,
19}
20
21pub fn generate_sshkey(
27 key_algorithm: KeyAlgorithm,
28) -> Result<SshKeyView, error::KeyGenerationError> {
29 let rng = rand::thread_rng();
30 generate_sshkey_internal(key_algorithm, rng)
31}
32
33fn generate_sshkey_internal(
34 key_algorithm: KeyAlgorithm,
35 mut rng: impl CryptoRngCore,
36) -> Result<SshKeyView, error::KeyGenerationError> {
37 let private_key = match key_algorithm {
38 KeyAlgorithm::Ed25519 => ssh_key::PrivateKey::random(&mut rng, Algorithm::Ed25519)
39 .map_err(KeyGenerationError::KeyGenerationError),
40 KeyAlgorithm::Rsa3072 => create_rsa_key(&mut rng, 3072),
41 KeyAlgorithm::Rsa4096 => create_rsa_key(&mut rng, 4096),
42 }?;
43
44 ssh_private_key_to_view(private_key).map_err(|_| KeyGenerationError::KeyConversionError)
45}
46
47fn create_rsa_key(
48 mut rng: impl CryptoRngCore,
49 bits: usize,
50) -> Result<ssh_key::PrivateKey, error::KeyGenerationError> {
51 let rsa_keypair = ssh_key::private::RsaKeypair::random(&mut rng, bits)
52 .map_err(KeyGenerationError::KeyGenerationError)?;
53 let private_key =
54 ssh_key::PrivateKey::new(ssh_key::private::KeypairData::from(rsa_keypair), "")
55 .map_err(KeyGenerationError::KeyGenerationError)?;
56 Ok(private_key)
57}
58
59#[cfg(test)]
60mod tests {
61 use rand::SeedableRng;
62
63 use super::KeyAlgorithm;
64 use crate::generator::generate_sshkey_internal;
65
66 #[test]
67 fn generate_ssh_key_ed25519() {
68 let rng = rand_chacha::ChaCha12Rng::from_seed([0u8; 32]);
69 let key_algorithm = KeyAlgorithm::Ed25519;
70 let result = generate_sshkey_internal(key_algorithm, rng);
71 let target = include_str!("../resources/generator/ed25519_key").replace("\r\n", "\n");
72 assert_eq!(result.unwrap().private_key, target);
73 }
74
75 #[test]
76 fn generate_ssh_key_rsa3072() {
77 let rng = rand_chacha::ChaCha12Rng::from_seed([0u8; 32]);
78 let key_algorithm = KeyAlgorithm::Rsa3072;
79 let result = generate_sshkey_internal(key_algorithm, rng);
80 let target = include_str!("../resources/generator/rsa3072_key").replace("\r\n", "\n");
81 assert_eq!(result.unwrap().private_key, target);
82 }
83
84 #[test]
85 fn generate_ssh_key_rsa4096() {
86 let rng = rand_chacha::ChaCha12Rng::from_seed([0u8; 32]);
87 let key_algorithm = KeyAlgorithm::Rsa4096;
88 let result = generate_sshkey_internal(key_algorithm, rng);
89 let target = include_str!("../resources/generator/rsa4096_key").replace("\r\n", "\n");
90 assert_eq!(result.unwrap().private_key, target);
91 }
92}