Skip to content

Commit e5bd251

Browse files
committed
Add CompositeEncryptable trait
1 parent 50d8f70 commit e5bd251

File tree

20 files changed

+132
-126
lines changed

20 files changed

+132
-126
lines changed

crates/bitwarden-core/src/mobile/crypto.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -859,7 +859,7 @@ mod tests {
859859
let invalid_private_key = "bad_key"
860860
.to_string()
861861
.into_bytes()
862-
.encrypt_with_key(&user_key.0, &bitwarden_crypto::ContentFormat::Utf8)
862+
.encrypt_with_key(&user_key.0, bitwarden_crypto::ContentFormat::Utf8)
863863
.unwrap();
864864

865865
let request = VerifyAsymmetricKeysRequest {

crates/bitwarden-crypto/src/cose.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,6 @@ pub enum ContentFormat {
3131
Pkcs8,
3232
CoseKey,
3333
OctetStream,
34-
/// Domain object should never be serialized. It is used to indicate when we call an encrypt
35-
/// operation on a complex object that consists of multiple, individually encrypted fields
36-
DomainObject,
3734
}
3835

3936
/// Encrypts a plaintext message using XChaCha20Poly1305 and returns a COSE Encrypt0 message
@@ -52,8 +49,6 @@ pub(crate) fn encrypt_xchacha20_poly1305(
5249
ContentFormat::OctetStream => {
5350
protected_header.content_format(CoapContentFormat::OctetStream)
5451
}
55-
// This should panic, and should never be implemented to be reachable!
56-
ContentFormat::DomainObject => unreachable!(),
5752
};
5853
let mut protected_header = protected_header.build();
5954
// This should be adjusted to use the builder pattern once implemented in coset.

crates/bitwarden-crypto/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ mod cose;
3434
mod traits;
3535
pub use cose::ContentFormat;
3636
mod xchacha20;
37-
pub use traits::{Decryptable, Encryptable, IdentifyKey, KeyId, KeyIds};
37+
pub use traits::{Decryptable, Encryptable, CompositeEncryptable, IdentifyKey, KeyId, KeyIds};
3838
pub use zeroizing_alloc::ZeroAlloc as ZeroizingAllocator;
3939

4040
#[cfg(feature = "uniffi")]

crates/bitwarden-crypto/src/store/context.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ use crate::{
5353
///
5454
/// const LOCAL_KEY: SymmKeyId = SymmKeyId::Local("local_key_id");
5555
///
56-
/// impl Encryptable<Ids, SymmKeyId, EncString> for Data {
57-
/// fn encrypt(&self, ctx: &mut KeyStoreContext<Ids>, key: SymmKeyId, content_format: ContentFormat) -> Result<EncString, CryptoError> {
56+
/// impl CompositeEncryptable<Ids, SymmKeyId, EncString> for Data {
57+
/// fn encrypt_composite(&self, ctx: &mut KeyStoreContext<Ids>, key: SymmKeyId) -> Result<EncString, CryptoError> {
5858
/// let local_key_id = ctx.unwrap_symmetric_key(key, LOCAL_KEY, &self.key)?;
5959
/// self.name.encrypt(ctx, local_key_id, content_format)
6060
/// }
@@ -382,13 +382,10 @@ impl<Ids: KeyIds> KeyStoreContext<'_, Ids> {
382382
#[allow(deprecated)]
383383
mod tests {
384384
use crate::{
385-
cose::ContentFormat,
386-
store::{
385+
cose::ContentFormat, store::{
387386
tests::{Data, DataView},
388387
KeyStore,
389-
},
390-
traits::tests::{TestIds, TestSymmKey},
391-
Decryptable, Encryptable, SymmetricCryptoKey,
388+
}, traits::tests::{TestIds, TestSymmKey}, CompositeEncryptable, Decryptable, Encryptable, SymmetricCryptoKey
392389
};
393390

394391
#[test]
@@ -409,7 +406,7 @@ mod tests {
409406
// Encrypt some data with the key
410407
let data = DataView("Hello, World!".to_string(), key_a0_id);
411408
let _encrypted: Data = data
412-
.encrypt(&mut store.context(), key_a0_id, ContentFormat::DomainObject)
409+
.encrypt_composite(&mut store.context(), key_a0_id)
413410
.unwrap();
414411
}
415412

@@ -449,7 +446,7 @@ mod tests {
449446

450447
let data = DataView("Hello, World!".to_string(), key_2_id);
451448
let encrypted = data
452-
.encrypt(&mut ctx, key_2_id, ContentFormat::OctetStream)
449+
.encrypt_composite(&mut ctx, key_2_id)
453450
.unwrap();
454451

455452
let decrypted1 = encrypted.decrypt(&mut ctx, key_2_id).unwrap();

crates/bitwarden-crypto/src/store/mod.rs

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use std::sync::{Arc, RwLock};
2626

2727
use rayon::prelude::*;
2828

29-
use crate::{cose::ContentFormat, Decryptable, Encryptable, IdentifyKey, KeyId, KeyIds};
29+
use crate::{CompositeEncryptable, Decryptable, IdentifyKey, KeyId, KeyIds};
3030

3131
mod backend;
3232
mod context;
@@ -74,9 +74,9 @@ pub use context::KeyStoreContext;
7474
/// SymmKeyId::User
7575
/// }
7676
/// }
77-
/// impl Encryptable<Ids, SymmKeyId, EncString> for Data {
78-
/// fn encrypt(&self, ctx: &mut KeyStoreContext<Ids>, key: SymmKeyId, content_format: ContentFormat) -> Result<EncString, CryptoError> {
79-
/// self.0.encrypt(ctx, key, content_format)
77+
/// impl CompositeEncryptable<Ids, SymmKeyId, EncString> for Data {
78+
/// fn encrypt_composite(&self, ctx: &mut KeyStoreContext<Ids>, key: SymmKeyId) -> Result<EncString, CryptoError> {
79+
/// self.0.encrypt(ctx, key)
8080
/// }
8181
/// }
8282
///
@@ -209,12 +209,12 @@ impl<Ids: KeyIds> KeyStore<Ids> {
209209
/// already be present in the store, otherwise this will return an error.
210210
/// This method is not parallelized, and is meant for single item encryption.
211211
/// If you need to encrypt multiple items, use `encrypt_list` instead.
212-
pub fn encrypt<Key: KeyId, Data: Encryptable<Ids, Key, Output> + IdentifyKey<Key>, Output>(
212+
pub fn encrypt<Key: KeyId, Data: CompositeEncryptable<Ids, Key, Output> + IdentifyKey<Key>, Output>(
213213
&self,
214214
data: Data,
215215
) -> Result<Output, crate::CryptoError> {
216216
let key = data.key_identifier();
217-
data.encrypt(&mut self.context(), key, ContentFormat::OctetStream)
217+
data.encrypt_composite(&mut self.context(), key)
218218
}
219219

220220
/// Decrypt a list of items using this key store. The keys returned by
@@ -257,7 +257,7 @@ impl<Ids: KeyIds> KeyStore<Ids> {
257257
/// single item encryption.
258258
pub fn encrypt_list<
259259
Key: KeyId,
260-
Data: Encryptable<Ids, Key, Output> + IdentifyKey<Key> + Send + Sync,
260+
Data: CompositeEncryptable<Ids, Key, Output> + IdentifyKey<Key> + Send + Sync,
261261
Output: Send + Sync,
262262
>(
263263
&self,
@@ -272,7 +272,7 @@ impl<Ids: KeyIds> KeyStore<Ids> {
272272

273273
for item in chunk {
274274
let key = item.key_identifier();
275-
result.push(item.encrypt(&mut ctx, key, ContentFormat::DomainObject));
275+
result.push(item.encrypt_composite(&mut ctx, key));
276276
ctx.clear_local();
277277
}
278278

@@ -304,9 +304,7 @@ fn batch_chunk_size(len: usize) -> usize {
304304
#[cfg(test)]
305305
pub(crate) mod tests {
306306
use crate::{
307-
store::{KeyStore, KeyStoreContext},
308-
traits::tests::{TestIds, TestSymmKey},
309-
EncString, SymmetricCryptoKey,
307+
store::{KeyStore, KeyStoreContext}, traits::tests::{TestIds, TestSymmKey}, EncString, Encryptable, SymmetricCryptoKey
310308
};
311309

312310
pub struct DataView(pub String, pub TestSymmKey);
@@ -324,14 +322,13 @@ pub(crate) mod tests {
324322
}
325323
}
326324

327-
impl crate::Encryptable<TestIds, TestSymmKey, Data> for DataView {
328-
fn encrypt(
325+
impl crate::CompositeEncryptable<TestIds, TestSymmKey, Data> for DataView {
326+
fn encrypt_composite(
329327
&self,
330328
ctx: &mut KeyStoreContext<TestIds>,
331329
key: TestSymmKey,
332-
_content_format: crate::cose::ContentFormat,
333330
) -> Result<Data, crate::CryptoError> {
334-
Ok(Data(self.0.encrypt(ctx, key, _content_format)?, key))
331+
Ok(Data(self.0.encrypt(ctx, key, crate::ContentFormat::Utf8)?, key))
335332
}
336333
}
337334

crates/bitwarden-crypto/src/traits/encryptable.rs

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,47 @@
11
use crate::{store::KeyStoreContext, ContentFormat, CryptoError, EncString, KeyId, KeyIds};
22

3-
/// An encryption operation that takes the input value and encrypts it into the output value.
3+
/// An encryption operation that takes the input value and encrypts the fields on it recursively.
44
/// Implementations should generally consist of calling [Encryptable::encrypt] for all the fields of
55
/// the type.
6+
pub trait CompositeEncryptable<Ids: KeyIds, Key: KeyId, Output> {
7+
/// For a struct made up of many small encstrings, such as a cipher, this takes the struct
8+
/// and recursively encrypts all the fields / sub-structs.
9+
fn encrypt_composite(
10+
&self,
11+
ctx: &mut KeyStoreContext<Ids>,
12+
key: Key,
13+
) -> Result<Output, CryptoError>;
14+
}
15+
16+
impl<Ids: KeyIds, Key: KeyId, T: CompositeEncryptable<Ids, Key, Output>, Output>
17+
CompositeEncryptable<Ids, Key, Option<Output>> for Option<T>
18+
{
19+
fn encrypt_composite(
20+
&self,
21+
ctx: &mut KeyStoreContext<Ids>,
22+
key: Key,
23+
) -> Result<Option<Output>, CryptoError> {
24+
self.as_ref()
25+
.map(|value| value.encrypt_composite(ctx, key))
26+
.transpose()
27+
}
28+
}
29+
30+
impl <Ids: KeyIds, Key: KeyId, T: CompositeEncryptable<Ids, Key, Output>, Output>
31+
CompositeEncryptable<Ids, Key, Vec<Output>> for Vec<T>
32+
{
33+
fn encrypt_composite(
34+
&self,
35+
ctx: &mut KeyStoreContext<Ids>,
36+
key: Key,
37+
) -> Result<Vec<Output>, CryptoError> {
38+
self.iter()
39+
.map(|value| value.encrypt_composite(ctx, key))
40+
.collect()
41+
}
42+
}
43+
44+
/// An encryption operation that takes the input value - a primitive such as `Vec<u8>`, `String` - and encrypts it into the output value.
645
pub trait Encryptable<Ids: KeyIds, Key: KeyId, Output> {
746
fn encrypt(
847
&self,
@@ -89,8 +128,7 @@ impl<Ids: KeyIds, Key: KeyId, T: Encryptable<Ids, Key, Output>, Output>
89128
#[cfg(test)]
90129
mod tests {
91130
use crate::{
92-
cose::ContentFormat, traits::tests::*, AsymmetricCryptoKey, Decryptable, Encryptable,
93-
KeyStore, SymmetricCryptoKey,
131+
cose::ContentFormat, traits::tests::*, AsymmetricCryptoKey, Decryptable, Encryptable, KeyStore, SymmetricCryptoKey
94132
};
95133

96134
fn test_store() -> KeyStore<TestIds> {

crates/bitwarden-crypto/src/traits/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
mod encryptable;
2-
pub use encryptable::Encryptable;
2+
pub use encryptable::{CompositeEncryptable, Encryptable};
33
mod decryptable;
44
pub use decryptable::Decryptable;
55

crates/bitwarden-exporters/src/export.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use bitwarden_core::{key_management::KeyIds, Client};
2-
use bitwarden_crypto::{Encryptable, IdentifyKey, KeyStoreContext};
2+
use bitwarden_crypto::{CompositeEncryptable, IdentifyKey, KeyStoreContext};
33
use bitwarden_vault::{Cipher, CipherView, Collection, Folder, FolderView};
44

55
use crate::{
@@ -80,10 +80,9 @@ fn encrypt_import(
8080
view.set_new_fido2_credentials(ctx, passkeys)?;
8181
}
8282

83-
let new_cipher = view.encrypt(
83+
let new_cipher = view.encrypt_composite(
8484
ctx,
8585
view.key_identifier(),
86-
bitwarden_crypto::ContentFormat::DomainObject,
8786
)?;
8887

8988
Ok(new_cipher)

crates/bitwarden-send/src/send.rs

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ use bitwarden_core::{
88
require,
99
};
1010
use bitwarden_crypto::{
11-
generate_random_bytes, ContentFormat, CryptoError, Decryptable, EncString, Encryptable,
12-
IdentifyKey, KeyStoreContext,
11+
generate_random_bytes, CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext
1312
};
1413
use chrono::{DateTime, Utc};
1514
use serde::{Deserialize, Serialize};
@@ -189,12 +188,11 @@ impl Decryptable<KeyIds, SymmetricKeyId, SendTextView> for SendText {
189188
}
190189
}
191190

192-
impl Encryptable<KeyIds, SymmetricKeyId, SendText> for SendTextView {
193-
fn encrypt(
191+
impl CompositeEncryptable<KeyIds, SymmetricKeyId, SendText> for SendTextView {
192+
fn encrypt_composite(
194193
&self,
195194
ctx: &mut KeyStoreContext<KeyIds>,
196195
key: SymmetricKeyId,
197-
_content_format: ContentFormat,
198196
) -> Result<SendText, CryptoError> {
199197
Ok(SendText {
200198
text: self.text.encrypt(ctx, key, ContentFormat::Utf8)?,
@@ -218,12 +216,11 @@ impl Decryptable<KeyIds, SymmetricKeyId, SendFileView> for SendFile {
218216
}
219217
}
220218

221-
impl Encryptable<KeyIds, SymmetricKeyId, SendFile> for SendFileView {
222-
fn encrypt(
219+
impl CompositeEncryptable<KeyIds, SymmetricKeyId, SendFile> for SendFileView {
220+
fn encrypt_composite(
223221
&self,
224222
ctx: &mut KeyStoreContext<KeyIds>,
225223
key: SymmetricKeyId,
226-
_content_format: ContentFormat,
227224
) -> Result<SendFile, CryptoError> {
228225
Ok(SendFile {
229226
id: self.id.clone(),
@@ -299,12 +296,11 @@ impl Decryptable<KeyIds, SymmetricKeyId, SendListView> for Send {
299296
}
300297
}
301298

302-
impl Encryptable<KeyIds, SymmetricKeyId, Send> for SendView {
303-
fn encrypt(
299+
impl CompositeEncryptable<KeyIds, SymmetricKeyId, Send> for SendView {
300+
fn encrypt_composite(
304301
&self,
305302
ctx: &mut KeyStoreContext<KeyIds>,
306303
key: SymmetricKeyId,
307-
_content_format: ContentFormat,
308304
) -> Result<Send, CryptoError> {
309305
// For sends, we first decrypt the send key with the user key, and stretch it to it's full
310306
// size For the rest of the fields, we ignore the provided SymmetricCryptoKey and
@@ -338,8 +334,8 @@ impl Encryptable<KeyIds, SymmetricKeyId, Send> for SendView {
338334
}),
339335

340336
r#type: self.r#type,
341-
file: self.file.encrypt(ctx, send_key, ContentFormat::Utf8)?,
342-
text: self.text.encrypt(ctx, send_key, ContentFormat::Utf8)?,
337+
file: self.file.encrypt_composite(ctx, send_key)?,
338+
text: self.text.encrypt_composite(ctx, send_key)?,
343339

344340
max_access_count: self.max_access_count,
345341
access_count: self.access_count,

crates/bitwarden-vault/src/cipher/attachment.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use bitwarden_core::key_management::{KeyIds, SymmetricKeyId};
22
use bitwarden_crypto::{
3-
ContentFormat, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext,
3+
CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext
44
};
55
use serde::{Deserialize, Serialize};
66
#[cfg(feature = "wasm")]
@@ -73,12 +73,11 @@ impl IdentifyKey<SymmetricKeyId> for AttachmentFile {
7373
}
7474
}
7575

76-
impl Encryptable<KeyIds, SymmetricKeyId, AttachmentEncryptResult> for AttachmentFileView<'_> {
77-
fn encrypt(
76+
impl CompositeEncryptable<KeyIds, SymmetricKeyId, AttachmentEncryptResult> for AttachmentFileView<'_> {
77+
fn encrypt_composite(
7878
&self,
7979
ctx: &mut KeyStoreContext<KeyIds>,
8080
key: SymmetricKeyId,
81-
_content_format: ContentFormat,
8281
) -> Result<AttachmentEncryptResult, CryptoError> {
8382
let ciphers_key = Cipher::decrypt_cipher_key(ctx, key, &self.cipher.key)?;
8483

@@ -99,7 +98,7 @@ impl Encryptable<KeyIds, SymmetricKeyId, AttachmentEncryptResult> for Attachment
9998
attachment.size_name = Some(size_name(contents.len()));
10099

101100
Ok(AttachmentEncryptResult {
102-
attachment: attachment.encrypt(ctx, ciphers_key, ContentFormat::DomainObject)?,
101+
attachment: attachment.encrypt_composite(ctx, ciphers_key)?,
103102
contents,
104103
})
105104
}
@@ -135,12 +134,11 @@ impl Decryptable<KeyIds, SymmetricKeyId, Vec<u8>> for AttachmentFile {
135134
}
136135
}
137136

138-
impl Encryptable<KeyIds, SymmetricKeyId, Attachment> for AttachmentView {
139-
fn encrypt(
137+
impl CompositeEncryptable<KeyIds, SymmetricKeyId, Attachment> for AttachmentView {
138+
fn encrypt_composite(
140139
&self,
141140
ctx: &mut KeyStoreContext<KeyIds>,
142141
key: SymmetricKeyId,
143-
_content_format: ContentFormat,
144142
) -> Result<Attachment, CryptoError> {
145143
Ok(Attachment {
146144
id: self.id.clone(),

crates/bitwarden-vault/src/cipher/card.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use bitwarden_api_api::models::CipherCardModel;
22
use bitwarden_core::key_management::{KeyIds, SymmetricKeyId};
33
use bitwarden_crypto::{
4-
ContentFormat, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext,
4+
CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext
55
};
66
use serde::{Deserialize, Serialize};
77
#[cfg(feature = "wasm")]
@@ -52,12 +52,11 @@ pub enum CardBrand {
5252
Other,
5353
}
5454

55-
impl Encryptable<KeyIds, SymmetricKeyId, Card> for CardView {
56-
fn encrypt(
55+
impl CompositeEncryptable<KeyIds, SymmetricKeyId, Card> for CardView {
56+
fn encrypt_composite(
5757
&self,
5858
ctx: &mut KeyStoreContext<KeyIds>,
5959
key: SymmetricKeyId,
60-
_content_format: ContentFormat,
6160
) -> Result<Card, CryptoError> {
6261
Ok(Card {
6362
cardholder_name: self

0 commit comments

Comments
 (0)