feat(store-encryption): Add the EncryptableValue.

This patch introduces the `EncryptableValue` trait to represent usual
operations on a value to be encoded by `encrypt_value_data`.
This commit is contained in:
Ivan Enderlin
2026-07-07 16:09:38 +02:00
parent 510b7192c9
commit 286ae8a12b
2 changed files with 59 additions and 6 deletions

View File

@@ -23,7 +23,7 @@ use std::{
use async_trait::async_trait;
use deadpool_sync::InteractError;
use itertools::Itertools;
use matrix_sdk_store_encryption::StoreCipher;
use matrix_sdk_store_encryption::{EncryptableValue, StoreCipher};
use ruma::{OwnedEventId, OwnedRoomId, serde::Raw, time::SystemTime};
use rusqlite::{OptionalExtension, Params, Row, Statement, Transaction, limits::Limit};
use serde::{Serialize, de::DeserializeOwned};
@@ -641,12 +641,15 @@ pub(crate) trait EncryptableStore {
}
}
fn encode_value(&self, value: Vec<u8>) -> Result<Vec<u8>> {
fn encode_value<V>(&self, value: V) -> Result<Vec<u8>>
where
V: EncryptableValue + Into<Vec<u8>>,
{
if let Some(key) = self.get_cypher() {
let encrypted = key.encrypt_value_data(value)?;
Ok(rmp_serde::to_vec_named(&encrypted)?)
} else {
Ok(value)
Ok(value.into())
}
}

View File

@@ -448,13 +448,16 @@ impl StoreCipher {
/// assert_eq!(value, decrypted);
/// # anyhow::Ok(()) };
/// ```
pub fn encrypt_value_data(&self, mut data: Vec<u8>) -> Result<EncryptedValue, Error> {
pub fn encrypt_value_data<D>(&self, mut data: D) -> Result<EncryptedValue, Error>
where
D: EncryptableValue,
{
let nonce = Keys::get_nonce();
let cipher = XChaCha20Poly1305::new(self.inner.encryption_key());
let ciphertext = cipher.encrypt(XNonce::from_slice(&nonce), data.as_ref())?;
let ciphertext = cipher.encrypt(XNonce::from_slice(&nonce), data.as_bytes())?;
data.zeroize();
data.zeroiize();
Ok(EncryptedValue { version: VERSION, ciphertext, nonce })
}
@@ -808,6 +811,53 @@ struct EncryptedStoreCipher {
pub ciphertext_info: CipherTextInfo,
}
/// A trait to get a slice of bytes and to zeroize a data, which are the
/// required operations for [`StoreCipher::encrypt_value_data`].
///
/// The goal of this trait was to call [`Zeroize`] efficiently on `Vec<u8>` and
/// `&[u8]`. We could call `vec.iter_mut().zeroize()` but the implementation of
/// `Zeroize` on `Vec<u8>` does a bit more than that as it clears the vector and
/// zeroizes the spare capacity as a best effort.
pub trait EncryptableValue {
/// Get the encodable value as bytes.
fn as_bytes(&self) -> &[u8];
/// Zeroize the encodable value.
///
/// Called `zeroiize` to avoid clashes with [`Zeroize::zeroize`].
fn zeroiize(&mut self);
}
impl EncryptableValue for Vec<u8> {
fn as_bytes(&self) -> &[u8] {
AsRef::as_ref(self)
}
fn zeroiize(&mut self) {
Zeroize::zeroize(self);
}
}
impl EncryptableValue for String {
fn as_bytes(&self) -> &[u8] {
str::as_bytes(self)
}
fn zeroiize(&mut self) {
Zeroize::zeroize(self);
}
}
impl EncryptableValue for &mut [u8] {
fn as_bytes(&self) -> &[u8] {
self
}
fn zeroiize(&mut self) {
self.iter_mut().zeroize();
}
}
#[cfg(test)]
mod tests {
use serde_json::{Value, json};