bitwarden_vault/cipher/
field.rs

1use bitwarden_api_api::models::CipherFieldModel;
2use bitwarden_core::{
3    key_management::{KeyIds, SymmetricKeyId},
4    require,
5};
6use bitwarden_crypto::{
7    CompositeEncryptable, CryptoError, Decryptable, EncString, KeyStoreContext,
8    PrimitiveEncryptable,
9};
10use serde::{Deserialize, Serialize};
11use serde_repr::{Deserialize_repr, Serialize_repr};
12#[cfg(feature = "wasm")]
13use tsify::Tsify;
14#[cfg(feature = "wasm")]
15use wasm_bindgen::prelude::wasm_bindgen;
16
17use super::linked_id::LinkedIdType;
18use crate::VaultParseError;
19
20#[derive(Clone, Copy, Serialize_repr, Deserialize_repr, Debug)]
21#[repr(u8)]
22#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
23#[cfg_attr(feature = "wasm", wasm_bindgen)]
24pub enum FieldType {
25    Text = 0,
26    Hidden = 1,
27    Boolean = 2,
28    Linked = 3,
29}
30
31#[derive(Serialize, Deserialize, Debug, Clone)]
32#[serde(rename_all = "camelCase", deny_unknown_fields)]
33#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
34#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
35pub struct Field {
36    name: Option<EncString>,
37    value: Option<EncString>,
38    r#type: FieldType,
39
40    linked_id: Option<LinkedIdType>,
41}
42
43#[allow(missing_docs)]
44#[derive(Serialize, Deserialize, Debug, Clone)]
45#[serde(rename_all = "camelCase", deny_unknown_fields)]
46#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
47#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
48pub struct FieldView {
49    pub name: Option<String>,
50    pub value: Option<String>,
51    pub r#type: FieldType,
52
53    pub linked_id: Option<LinkedIdType>,
54}
55
56impl CompositeEncryptable<KeyIds, SymmetricKeyId, Field> for FieldView {
57    fn encrypt_composite(
58        &self,
59        ctx: &mut KeyStoreContext<KeyIds>,
60        key: SymmetricKeyId,
61    ) -> Result<Field, CryptoError> {
62        Ok(Field {
63            name: self.name.encrypt(ctx, key)?,
64            value: self.value.encrypt(ctx, key)?,
65            r#type: self.r#type,
66            linked_id: self.linked_id,
67        })
68    }
69}
70
71impl Decryptable<KeyIds, SymmetricKeyId, FieldView> for Field {
72    fn decrypt(
73        &self,
74        ctx: &mut KeyStoreContext<KeyIds>,
75        key: SymmetricKeyId,
76    ) -> Result<FieldView, CryptoError> {
77        Ok(FieldView {
78            name: self.name.decrypt(ctx, key).ok().flatten(),
79            value: self.value.decrypt(ctx, key).ok().flatten(),
80            r#type: self.r#type,
81            linked_id: self.linked_id,
82        })
83    }
84}
85
86impl TryFrom<CipherFieldModel> for Field {
87    type Error = VaultParseError;
88
89    fn try_from(model: CipherFieldModel) -> Result<Self, Self::Error> {
90        Ok(Self {
91            name: EncString::try_from_optional(model.name)?,
92            value: EncString::try_from_optional(model.value)?,
93            r#type: require!(model.r#type).into(),
94            linked_id: model
95                .linked_id
96                .map(|id| (id as u32).try_into())
97                .transpose()?,
98        })
99    }
100}
101
102impl From<bitwarden_api_api::models::FieldType> for FieldType {
103    fn from(model: bitwarden_api_api::models::FieldType) -> Self {
104        match model {
105            bitwarden_api_api::models::FieldType::Text => FieldType::Text,
106            bitwarden_api_api::models::FieldType::Hidden => FieldType::Hidden,
107            bitwarden_api_api::models::FieldType::Boolean => FieldType::Boolean,
108            bitwarden_api_api::models::FieldType::Linked => FieldType::Linked,
109        }
110    }
111}