Skip to main content

bitwarden_vault/cipher/attachment_client/
mod.rs

1use std::{path::Path, sync::Arc};
2
3use bitwarden_core::{FromClient, client::ApiConfigurations, key_management::KeySlotIds};
4use bitwarden_crypto::{EncString, KeyStore};
5use bitwarden_error::bitwarden_error;
6use bitwarden_state::repository::Repository;
7use thiserror::Error;
8#[cfg(feature = "wasm")]
9use wasm_bindgen::prelude::*;
10
11use crate::{
12    Attachment, AttachmentEncryptResult, AttachmentFile, AttachmentFileView, AttachmentView,
13    Cipher, DecryptError, EncryptError,
14};
15
16mod admin;
17mod create;
18mod delete;
19mod download_url;
20mod renew;
21mod upgrade;
22
23pub use admin::{
24    AttachmentAdminClient, CipherAdminGetAttachmentDownloadUrlError, DeleteAttachmentAdminError,
25};
26pub use create::{
27    AttachmentFileUploadType, CipherCreateAttachmentError, CreateAttachmentRequest,
28    CreatedAttachment,
29};
30pub use delete::CipherDeleteAttachmentError;
31pub use download_url::CipherGetAttachmentDownloadUrlError;
32pub use renew::CipherRenewFileUploadUrlError;
33pub use upgrade::CipherUpgradeAttachmentError;
34
35/// Generic error type for vault encryption errors.
36#[allow(missing_docs)]
37#[bitwarden_error(flat)]
38#[derive(Debug, Error)]
39pub enum EncryptFileError {
40    #[error(transparent)]
41    Encrypt(#[from] EncryptError),
42    #[error(transparent)]
43    Io(#[from] std::io::Error),
44}
45
46/// Generic error type for decryption errors
47#[allow(missing_docs)]
48#[bitwarden_error(flat)]
49#[derive(Debug, Error)]
50pub enum DecryptFileError {
51    #[error(transparent)]
52    Decrypt(#[from] DecryptError),
53    #[error(transparent)]
54    Io(#[from] std::io::Error),
55}
56
57/// Wrapper for attachment-specific cipher operations.
58#[cfg_attr(feature = "wasm", wasm_bindgen)]
59#[derive(FromClient)]
60pub struct AttachmentsClient {
61    pub(crate) key_store: KeyStore<KeySlotIds>,
62    pub(crate) api_configurations: Arc<ApiConfigurations>,
63    pub(crate) repository: Option<Arc<dyn Repository<Cipher>>>,
64    pub(crate) http_client: reqwest::Client,
65}
66
67#[cfg_attr(feature = "wasm", wasm_bindgen)]
68impl AttachmentsClient {
69    /// Returns a new client for performing attachment admin operations.
70    /// Uses the admin server API endpoints and does not modify local state.
71    pub fn admin(&self) -> AttachmentAdminClient {
72        AttachmentAdminClient {
73            api_configurations: self.api_configurations.clone(),
74        }
75    }
76
77    #[allow(missing_docs)]
78    pub fn decrypt_buffer(
79        &self,
80        cipher: Cipher,
81        attachment: AttachmentView,
82        encrypted_buffer: &[u8],
83    ) -> Result<Vec<u8>, DecryptError> {
84        Ok(self.key_store.decrypt(&AttachmentFile {
85            cipher,
86            attachment,
87            contents: EncString::from_buffer(encrypted_buffer)?,
88        })?)
89    }
90}
91
92impl AttachmentsClient {
93    #[allow(missing_docs)]
94    pub fn encrypt_buffer(
95        &self,
96        cipher: Cipher,
97        attachment: AttachmentView,
98        buffer: &[u8],
99    ) -> Result<AttachmentEncryptResult, EncryptError> {
100        Ok(self.key_store.encrypt(AttachmentFileView {
101            cipher,
102            attachment,
103            contents: buffer,
104        })?)
105    }
106
107    #[allow(missing_docs)]
108    pub fn encrypt_file(
109        &self,
110        cipher: Cipher,
111        attachment: AttachmentView,
112        decrypted_file_path: &Path,
113        encrypted_file_path: &Path,
114    ) -> Result<Attachment, EncryptFileError> {
115        let data = std::fs::read(decrypted_file_path)?;
116        let AttachmentEncryptResult {
117            attachment,
118            contents,
119        } = self.encrypt_buffer(cipher, attachment, &data)?;
120        std::fs::write(encrypted_file_path, contents)?;
121        Ok(attachment)
122    }
123
124    #[allow(missing_docs)]
125    pub fn decrypt_file(
126        &self,
127        cipher: Cipher,
128        attachment: AttachmentView,
129        encrypted_file_path: &Path,
130        decrypted_file_path: &Path,
131    ) -> Result<(), DecryptFileError> {
132        let data = std::fs::read(encrypted_file_path)?;
133        let decrypted = self.decrypt_buffer(cipher, attachment, &data)?;
134        std::fs::write(decrypted_file_path, decrypted)?;
135        Ok(())
136    }
137}