Skip to content

Commit 5d716f9

Browse files
authored
Stores: Remove StoreCipher::{en,de}crypt_value_[base64_]typed (#3638)
* Use `IndexeddbSerializer` more widely in test code reuse `IndexeddbSerializer::maybe_encrypt_value` instead of re-inventing it. * Rewrite `StoreCipher::decrypt_value_base64_typed` Instead of un-base64-ing and calling `decrypt_value_typed` (which deserializes the result`), call `decrypt_value_base64_data` (which un-base64s before decrypting but does not deserialize the result), then deserialize. This makes it more symmetrical with `encrypt_value_base64_typed`, and helps me get rid of `decrypt_value_typed` (which is barely used.) * Fix docs on `StoreCipher::encrypt_value_base64_typed` looks like they got C&Ped from `encrypt_value_typed`. * Inline `StoreCipher::{encrypt,decrypt}_value_typed` Each of these are quite simple, are only used in two places, and their existence melts my brain. * Rewrite `IndexeddbSerializer::maybe_{encrypt,decrypt}_value` ... to use `en/decrypt_value_base64_data` instead of `en/decrypt_value_base64_typed`. We have to have the de/serialization code for the unencrypted case anyway, so using the higher-level method isn't helping us much. * Remove unused `StoreCipher::{en,de}crypt_value_base64_typed` Outside of tests, these things are totally unused.
1 parent 09dc9b9 commit 5d716f9

File tree

4 files changed

+48
-180
lines changed

4 files changed

+48
-180
lines changed

crates/matrix-sdk-indexeddb/src/crypto_store/indexeddb_serializer.rs

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use matrix_sdk_store_encryption::{EncryptedValueBase64, StoreCipher};
2525
use serde::{de::DeserializeOwned, Deserialize, Serialize};
2626
use wasm_bindgen::JsValue;
2727
use web_sys::IdbKeyRange;
28+
use zeroize::Zeroizing;
2829

2930
use crate::{safe_encode::SafeEncode, IndexeddbCryptoStoreError};
3031

@@ -145,18 +146,29 @@ impl IndexeddbSerializer {
145146
}
146147
}
147148

148-
/// Encode an InboundGroupSession for storage as a value in indexeddb.
149+
/// Encode an object for storage as a value in indexeddb.
150+
///
151+
/// First serializes the object as JSON bytes.
152+
///
153+
/// Then, if a cipher is set, encrypts the JSON with a nonce into binary
154+
/// blobs, and base64-encodes the blobs.
155+
///
156+
/// If no cipher is set, just base64-encodes the JSON bytes.
157+
///
158+
/// Finally, returns an object encapsulating the result.
149159
pub fn maybe_encrypt_value(
150160
&self,
151161
value: PickledInboundGroupSession,
152162
) -> Result<MaybeEncrypted, CryptoStoreError> {
163+
// First serialize the object as JSON.
164+
let serialized = serde_json::to_vec(&value).map_err(CryptoStoreError::backend)?;
165+
166+
// Then either encrypt the JSON, or just base64-encode it.
153167
Ok(match &self.store_cipher {
154168
Some(cipher) => MaybeEncrypted::Encrypted(
155-
cipher.encrypt_value_base64_typed(&value).map_err(CryptoStoreError::backend)?,
156-
),
157-
None => MaybeEncrypted::Unencrypted(
158-
BASE64.encode(serde_json::to_vec(&value).map_err(CryptoStoreError::backend)?),
169+
cipher.encrypt_value_base64_data(serialized).map_err(CryptoStoreError::backend)?,
159170
),
171+
None => MaybeEncrypted::Unencrypted(BASE64.encode(serialized)),
160172
})
161173
}
162174

@@ -198,16 +210,19 @@ impl IndexeddbSerializer {
198210
&self,
199211
value: MaybeEncrypted,
200212
) -> Result<PickledInboundGroupSession, CryptoStoreError> {
201-
match (&self.store_cipher, value) {
213+
// First extract the plaintext JSON, either by decrypting or un-base64-ing.
214+
let plaintext = Zeroizing::new(match (&self.store_cipher, value) {
202215
(Some(cipher), MaybeEncrypted::Encrypted(enc)) => {
203-
cipher.decrypt_value_base64_typed(enc).map_err(CryptoStoreError::backend)
216+
cipher.decrypt_value_base64_data(enc).map_err(CryptoStoreError::backend)?
204217
}
205218
(None, MaybeEncrypted::Unencrypted(unc)) => {
206-
Ok(serde_json::from_slice(&BASE64.decode(unc).map_err(CryptoStoreError::backend)?)
207-
.map_err(CryptoStoreError::backend)?)
219+
BASE64.decode(unc).map_err(CryptoStoreError::backend)?
208220
}
209221

210-
_ => Err(CryptoStoreError::UnpicklingError),
211-
}
222+
_ => return Err(CryptoStoreError::UnpicklingError),
223+
});
224+
225+
// Then deserialize the JSON.
226+
Ok(serde_json::from_slice(&plaintext)?)
212227
}
213228
}

crates/matrix-sdk-indexeddb/src/crypto_store/migrations/mod.rs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -235,18 +235,16 @@ mod tests {
235235
/// Make lots of sessions and see how long it takes to count them in v10
236236
#[async_test]
237237
async fn count_lots_of_sessions_v10() {
238-
let cipher = Arc::new(StoreCipher::new().unwrap());
239-
let serializer = IndexeddbSerializer::new(Some(cipher.clone()));
238+
let serializer = IndexeddbSerializer::new(Some(Arc::new(StoreCipher::new().unwrap())));
239+
240240
// Session keys are slow to create, so make one upfront and use it for every
241241
// session
242242
let session_key = create_session_key();
243243

244244
// Create lots of InboundGroupSessionIndexedDbObject objects
245245
let mut objects = Vec::with_capacity(NUM_RECORDS_FOR_PERF);
246246
for i in 0..NUM_RECORDS_FOR_PERF {
247-
objects.push(
248-
create_inbound_group_sessions3_record(i, &session_key, &cipher, &serializer).await,
249-
);
247+
objects.push(create_inbound_group_sessions3_record(i, &session_key, &serializer).await);
250248
}
251249

252250
// Create a DB with an inbound_group_sessions3 store
@@ -334,15 +332,13 @@ mod tests {
334332
async fn create_inbound_group_sessions3_record(
335333
i: usize,
336334
session_key: &SessionKey,
337-
cipher: &Arc<StoreCipher>,
338335
serializer: &IndexeddbSerializer,
339336
) -> (JsValue, JsValue) {
340337
let session = create_inbound_group_session(i, session_key);
341338
let pickled_session = session.pickle().await;
339+
342340
let session_dbo = InboundGroupSessionIndexedDbObject {
343-
pickled_session: MaybeEncrypted::Encrypted(
344-
cipher.encrypt_value_base64_typed(&pickled_session).unwrap(),
345-
),
341+
pickled_session: serializer.maybe_encrypt_value(pickled_session).unwrap(),
346342
needs_backup: false,
347343
backed_up_to: -1,
348344
};

crates/matrix-sdk-indexeddb/src/state_store/mod.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,10 @@ pub use keys::ALL_STORES;
150150
/// Encrypt (if needs be) then JSON-serialize a value.
151151
fn serialize_value(store_cipher: Option<&StoreCipher>, event: &impl Serialize) -> Result<JsValue> {
152152
Ok(match store_cipher {
153-
Some(cipher) => JsValue::from_serde(&cipher.encrypt_value_typed(event)?)?,
153+
Some(cipher) => {
154+
let data = serde_json::to_vec(event)?;
155+
JsValue::from_serde(&cipher.encrypt_value_data(data)?)?
156+
}
154157
None => JsValue::from_serde(event)?,
155158
})
156159
}
@@ -161,7 +164,13 @@ fn deserialize_value<T: DeserializeOwned>(
161164
event: &JsValue,
162165
) -> Result<T> {
163166
match store_cipher {
164-
Some(cipher) => Ok(cipher.decrypt_value_typed(event.into_serde()?)?),
167+
Some(cipher) => {
168+
use zeroize::Zeroize;
169+
let mut plaintext = cipher.decrypt_value_data(event.into_serde()?)?;
170+
let ret = serde_json::from_slice(&plaintext);
171+
plaintext.zeroize();
172+
Ok(ret?)
173+
}
165174
None => Ok(event.into_serde()?),
166175
}
167176
}

crates/matrix-sdk-store-encryption/src/lib.rs

Lines changed: 6 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -418,45 +418,8 @@ impl StoreCipher {
418418
/// # anyhow::Ok(()) };
419419
/// ```
420420
pub fn encrypt_value(&self, value: &impl Serialize) -> Result<Vec<u8>, Error> {
421-
Ok(serde_json::to_vec(&self.encrypt_value_typed(value)?)?)
422-
}
423-
424-
/// Encrypt a value before it is inserted into the key/value store.
425-
///
426-
/// A value can be decrypted using the
427-
/// [`StoreCipher::decrypt_value_typed()`] method. This is the lower
428-
/// level function to `encrypt_value`, but returns the
429-
/// full `EncryptdValue`-type
430-
///
431-
/// # Arguments
432-
///
433-
/// * `value` - A value that should be encrypted, any value that implements
434-
/// `Serialize` can be given to this method. The value will be serialized
435-
/// as json before it is encrypted.
436-
///
437-
///
438-
/// # Examples
439-
///
440-
/// ```no_run
441-
/// # let example = || {
442-
/// use matrix_sdk_store_encryption::StoreCipher;
443-
/// use serde_json::{json, value::Value};
444-
///
445-
/// let store_cipher = StoreCipher::new()?;
446-
///
447-
/// let value = json!({
448-
/// "some": "data",
449-
/// });
450-
///
451-
/// let encrypted = store_cipher.encrypt_value_typed(&value)?;
452-
/// let decrypted: Value = store_cipher.decrypt_value_typed(encrypted)?;
453-
///
454-
/// assert_eq!(value, decrypted);
455-
/// # anyhow::Ok(()) };
456-
/// ```
457-
pub fn encrypt_value_typed(&self, value: &impl Serialize) -> Result<EncryptedValue, Error> {
458421
let data = serde_json::to_vec(value)?;
459-
self.encrypt_value_data(data)
422+
Ok(serde_json::to_vec(&self.encrypt_value_data(data)?)?)
460423
}
461424

462425
/// Encrypt some data before it is inserted into the key/value store.
@@ -497,47 +460,6 @@ impl StoreCipher {
497460
Ok(EncryptedValue { version: VERSION, ciphertext, nonce })
498461
}
499462

500-
/// Encrypt a value before it is inserted into the key/value store.
501-
///
502-
/// A value can be decrypted using the
503-
/// [`StoreCipher::decrypt_value_typed()`] method. This is the lower
504-
/// level function to `encrypt_value`, but returns the
505-
/// full `EncryptdValue`-type
506-
///
507-
/// # Arguments
508-
///
509-
/// * `value` - A value that should be encrypted, any value that implements
510-
/// `Serialize` can be given to this method. The value will be serialized
511-
/// as json before it is encrypted.
512-
///
513-
///
514-
/// # Examples
515-
///
516-
/// ```no_run
517-
/// # let example = || {
518-
/// use matrix_sdk_store_encryption::StoreCipher;
519-
/// use serde_json::{json, value::Value};
520-
///
521-
/// let store_cipher = StoreCipher::new()?;
522-
///
523-
/// let value = json!({
524-
/// "some": "data",
525-
/// });
526-
///
527-
/// let encrypted = store_cipher.encrypt_value_typed(&value)?;
528-
/// let decrypted: Value = store_cipher.decrypt_value_typed(encrypted)?;
529-
///
530-
/// assert_eq!(value, decrypted);
531-
/// # anyhow::Ok(()) };
532-
/// ```
533-
pub fn encrypt_value_base64_typed(
534-
&self,
535-
value: &impl Serialize,
536-
) -> Result<EncryptedValueBase64, Error> {
537-
let data = serde_json::to_vec(value)?;
538-
self.encrypt_value_base64_data(data)
539-
}
540-
541463
/// Encrypt some data before it is inserted into the key/value store,
542464
/// using base64 for arrays of integers.
543465
///
@@ -603,89 +525,12 @@ impl StoreCipher {
603525
/// ```
604526
pub fn decrypt_value<T: DeserializeOwned>(&self, value: &[u8]) -> Result<T, Error> {
605527
let value: EncryptedValue = serde_json::from_slice(value)?;
606-
self.decrypt_value_typed(value)
607-
}
608-
609-
/// Decrypt a value after it was fetched from the key/value store.
610-
///
611-
/// A value can be encrypted using the
612-
/// [`StoreCipher::encrypt_value_typed()`] method. Lower level method to
613-
/// [`StoreCipher::decrypt_value_typed()`]
614-
///
615-
/// # Arguments
616-
///
617-
/// * `value` - The EncryptedValue of a value that should be decrypted.
618-
///
619-
/// The method will deserialize the decrypted value into the expected type.
620-
///
621-
/// # Examples
622-
///
623-
/// ```
624-
/// # let example = || {
625-
/// use matrix_sdk_store_encryption::StoreCipher;
626-
/// use serde_json::{json, value::Value};
627-
///
628-
/// let store_cipher = StoreCipher::new()?;
629-
///
630-
/// let value = json!({
631-
/// "some": "data",
632-
/// });
633-
///
634-
/// let encrypted = store_cipher.encrypt_value_typed(&value)?;
635-
/// let decrypted: Value = store_cipher.decrypt_value_typed(encrypted)?;
636-
///
637-
/// assert_eq!(value, decrypted);
638-
/// # anyhow::Ok(()) };
639-
/// ```
640-
pub fn decrypt_value_typed<T: DeserializeOwned>(
641-
&self,
642-
value: EncryptedValue,
643-
) -> Result<T, Error> {
644528
let mut plaintext = self.decrypt_value_data(value)?;
645529
let ret = serde_json::from_slice(&plaintext);
646530
plaintext.zeroize();
647531
Ok(ret?)
648532
}
649533

650-
/// Decrypt a base64-encoded value after it was fetched from the key/value
651-
/// store.
652-
///
653-
/// A value can be encrypted using the
654-
/// [`StoreCipher::encrypt_value_base64_typed()`] method.
655-
///
656-
/// # Arguments
657-
///
658-
/// * `value` - The EncryptedValueBase64 of a value that should be
659-
/// decrypted.
660-
///
661-
/// The method will deserialize the decrypted value into the expected type.
662-
///
663-
/// # Examples
664-
///
665-
/// ```
666-
/// # let example = || {
667-
/// use matrix_sdk_store_encryption::StoreCipher;
668-
/// use serde_json::{json, value::Value};
669-
///
670-
/// let store_cipher = StoreCipher::new()?;
671-
///
672-
/// let value = json!({
673-
/// "some": "data",
674-
/// });
675-
///
676-
/// let encrypted = store_cipher.encrypt_value_base64_typed(&value)?;
677-
/// let decrypted: Value = store_cipher.decrypt_value_base64_typed(encrypted)?;
678-
///
679-
/// assert_eq!(value, decrypted);
680-
/// # anyhow::Ok(()) };
681-
/// ```
682-
pub fn decrypt_value_base64_typed<T: DeserializeOwned>(
683-
&self,
684-
value: EncryptedValueBase64,
685-
) -> Result<T, Error> {
686-
self.decrypt_value_typed(value.try_into()?)
687-
}
688-
689534
/// Decrypt a base64-encoded value after it was fetched from the key/value
690535
/// store.
691536
///
@@ -1100,8 +945,11 @@ mod tests {
1100945

1101946
let store_cipher = StoreCipher::new()?;
1102947

1103-
let encrypted = store_cipher.encrypt_value_base64_typed(&event)?;
1104-
let decrypted: Value = store_cipher.decrypt_value_base64_typed(encrypted)?;
948+
let data = serde_json::to_vec(&event)?;
949+
let encrypted = store_cipher.encrypt_value_base64_data(data)?;
950+
951+
let plaintext = store_cipher.decrypt_value_base64_data(encrypted)?;
952+
let decrypted: Value = serde_json::from_slice(&plaintext)?;
1105953

1106954
assert_eq!(event, decrypted);
1107955

0 commit comments

Comments
 (0)