bitwarden_vault/cipher/
ssh_key.rs1use bitwarden_core::key_management::{KeyIds, SymmetricKeyId};
2use bitwarden_crypto::{
3 CompositeEncryptable, CryptoError, Decryptable, EncString, KeyStoreContext,
4 PrimitiveEncryptable,
5};
6use serde::{Deserialize, Serialize};
7#[cfg(feature = "wasm")]
8use tsify::Tsify;
9
10use super::cipher::CipherKind;
11use crate::{cipher::cipher::CopyableCipherFields, Cipher};
12
13#[derive(Serialize, Deserialize, Debug, Clone)]
14#[serde(rename_all = "camelCase", deny_unknown_fields)]
15#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
16#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
17pub struct SshKey {
18 pub private_key: EncString,
20 pub public_key: EncString,
22 pub fingerprint: EncString,
24}
25
26#[allow(missing_docs)]
27#[derive(Serialize, Deserialize, Debug, Clone)]
28#[serde(rename_all = "camelCase", deny_unknown_fields)]
29#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
30#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
31pub struct SshKeyView {
32 pub private_key: String,
34 pub public_key: String,
36 pub fingerprint: String,
38}
39
40impl CompositeEncryptable<KeyIds, SymmetricKeyId, SshKey> for SshKeyView {
41 fn encrypt_composite(
42 &self,
43 ctx: &mut KeyStoreContext<KeyIds>,
44 key: SymmetricKeyId,
45 ) -> Result<SshKey, CryptoError> {
46 Ok(SshKey {
47 private_key: self.private_key.encrypt(ctx, key)?,
48 public_key: self.public_key.encrypt(ctx, key)?,
49 fingerprint: self.fingerprint.encrypt(ctx, key)?,
50 })
51 }
52}
53
54impl Decryptable<KeyIds, SymmetricKeyId, SshKeyView> for SshKey {
55 fn decrypt(
56 &self,
57 ctx: &mut KeyStoreContext<KeyIds>,
58 key: SymmetricKeyId,
59 ) -> Result<SshKeyView, CryptoError> {
60 Ok(SshKeyView {
61 private_key: self.private_key.decrypt(ctx, key)?,
62 public_key: self.public_key.decrypt(ctx, key)?,
63 fingerprint: self.fingerprint.decrypt(ctx, key)?,
64 })
65 }
66}
67
68impl CipherKind for SshKey {
69 fn decrypt_subtitle(
70 &self,
71 ctx: &mut KeyStoreContext<KeyIds>,
72 key: SymmetricKeyId,
73 ) -> Result<String, CryptoError> {
74 self.fingerprint.decrypt(ctx, key)
75 }
76
77 fn get_copyable_fields(&self, _: Option<&Cipher>) -> Vec<CopyableCipherFields> {
78 [CopyableCipherFields::SshKey].into_iter().collect()
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use bitwarden_core::key_management::create_test_crypto_with_user_key;
85 use bitwarden_crypto::SymmetricCryptoKey;
86
87 use super::*;
88 use crate::cipher::cipher::CopyableCipherFields;
89
90 #[test]
91 fn test_subtitle_ssh_key() {
92 let key = SymmetricCryptoKey::try_from("hvBMMb1t79YssFZkpetYsM3deyVuQv4r88Uj9gvYe0+G8EwxvW3v1iywVmSl61iwzd17JW5C/ivzxSP2C9h7Tw==".to_string()).unwrap();
93 let key_store = create_test_crypto_with_user_key(key);
94 let key = SymmetricKeyId::User;
95 let mut ctx = key_store.context();
96
97 let original_subtitle = "SHA256:1JjFjvPRkj1Gbf2qRP1dgHiIzEuNAEvp+92x99jw3K0".to_string();
98 let fingerprint_encrypted = original_subtitle.to_owned().encrypt(&mut ctx, key).unwrap();
99 let private_key_encrypted = "".to_string().encrypt(&mut ctx, key).unwrap();
100 let public_key_encrypted = "".to_string().encrypt(&mut ctx, key).unwrap();
101
102 let ssh_key = SshKey {
103 private_key: private_key_encrypted,
104 public_key: public_key_encrypted,
105 fingerprint: fingerprint_encrypted,
106 };
107
108 assert_eq!(
109 ssh_key.decrypt_subtitle(&mut ctx, key).unwrap(),
110 original_subtitle
111 );
112 }
113
114 #[test]
115 fn test_get_copyable_fields_sshkey() {
116 let ssh_key = SshKey {
117 private_key: "2.tMIugb6zQOL+EuOizna1wQ==|W5dDLoNJtajN68yeOjrr6w==|qS4hwJB0B0gNLI0o+jxn+sKMBmvtVgJCRYNEXBZoGeE=".parse().unwrap(),
118 public_key: "2.tMIugb6zQOL+EuOizna1wQ==|W5dDLoNJtajN68yeOjrr6w==|qS4hwJB0B0gNLI0o+jxn+sKMBmvtVgJCRYNEXBZoGeE=".parse().unwrap(),
119 fingerprint: "2.tMIugb6zQOL+EuOizna1wQ==|W5dDLoNJtajN68yeOjrr6w==|qS4hwJB0B0gNLI0o+jxn+sKMBmvtVgJCRYNEXBZoGeE=".parse().unwrap(),
120 };
121
122 let copyable_fields = ssh_key.get_copyable_fields(None);
123 assert_eq!(copyable_fields, vec![CopyableCipherFields::SshKey]);
124 }
125}