#![allow(dead_code)]
use base64::{
alphabet,
engine::{general_purpose, GeneralPurpose},
Engine,
};
use matrix_sdk_store_encryption::StoreCipher;
use ruma::{
events::{
receipt::ReceiptType, GlobalAccountDataEventType, RoomAccountDataEventType, StateEventType,
},
DeviceId, EventId, MxcUri, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, TransactionId,
UserId,
};
use wasm_bindgen::JsValue;
use web_sys::IdbKeyRange;
pub const KEY_SEPARATOR: &str = "\u{001D}";
pub const RANGE_END: &str = "\u{001E}";
pub const ESCAPED: &str = "\u{001E}\u{001D}";
const STANDARD_NO_PAD: GeneralPurpose =
GeneralPurpose::new(&alphabet::STANDARD, general_purpose::NO_PAD);
pub trait SafeEncode {
fn as_encoded_string(&self) -> String;
fn as_secure_string(&self, table_name: &str, store_cipher: &StoreCipher) -> String {
STANDARD_NO_PAD
.encode(store_cipher.hash_key(table_name, self.as_encoded_string().as_bytes()))
}
fn encode_with_counter(&self, i: usize) -> JsValue {
format!("{}{KEY_SEPARATOR}{i:016x}", self.as_encoded_string()).into()
}
fn encode_with_counter_secure(
&self,
table_name: &str,
store_cipher: &StoreCipher,
i: usize,
) -> JsValue {
format!("{}{KEY_SEPARATOR}{i:016x}", self.as_secure_string(table_name, store_cipher)).into()
}
fn encode_to_range(&self) -> Result<IdbKeyRange, String> {
let key = self.as_encoded_string();
IdbKeyRange::bound(
&JsValue::from([&key, KEY_SEPARATOR].concat()),
&JsValue::from([&key, RANGE_END].concat()),
)
.map_err(|e| e.as_string().unwrap_or_else(|| "Creating key range failed".to_owned()))
}
fn encode_to_range_secure(
&self,
table_name: &str,
store_cipher: &StoreCipher,
) -> Result<IdbKeyRange, String> {
let key = self.as_secure_string(table_name, store_cipher);
IdbKeyRange::bound(
&JsValue::from([&key, KEY_SEPARATOR].concat()),
&JsValue::from([&key, RANGE_END].concat()),
)
.map_err(|e| e.as_string().unwrap_or_else(|| "Creating key range failed".to_owned()))
}
}
impl<A, B> SafeEncode for (A, B)
where
A: SafeEncode,
B: SafeEncode,
{
fn as_encoded_string(&self) -> String {
[&self.0.as_encoded_string(), KEY_SEPARATOR, &self.1.as_encoded_string()].concat()
}
fn as_secure_string(&self, table_name: &str, store_cipher: &StoreCipher) -> String {
[
&STANDARD_NO_PAD
.encode(store_cipher.hash_key(table_name, self.0.as_encoded_string().as_bytes())),
KEY_SEPARATOR,
&STANDARD_NO_PAD
.encode(store_cipher.hash_key(table_name, self.1.as_encoded_string().as_bytes())),
]
.concat()
}
}
impl<A, B, C> SafeEncode for (A, B, C)
where
A: SafeEncode,
B: SafeEncode,
C: SafeEncode,
{
fn as_encoded_string(&self) -> String {
[
&self.0.as_encoded_string(),
KEY_SEPARATOR,
&self.1.as_encoded_string(),
KEY_SEPARATOR,
&self.2.as_encoded_string(),
]
.concat()
}
fn as_secure_string(&self, table_name: &str, store_cipher: &StoreCipher) -> String {
[
&STANDARD_NO_PAD
.encode(store_cipher.hash_key(table_name, self.0.as_encoded_string().as_bytes())),
KEY_SEPARATOR,
&STANDARD_NO_PAD
.encode(store_cipher.hash_key(table_name, self.1.as_encoded_string().as_bytes())),
KEY_SEPARATOR,
&STANDARD_NO_PAD
.encode(store_cipher.hash_key(table_name, self.2.as_encoded_string().as_bytes())),
]
.concat()
}
}
impl<A, B, C, D> SafeEncode for (A, B, C, D)
where
A: SafeEncode,
B: SafeEncode,
C: SafeEncode,
D: SafeEncode,
{
fn as_encoded_string(&self) -> String {
[
&self.0.as_encoded_string(),
KEY_SEPARATOR,
&self.1.as_encoded_string(),
KEY_SEPARATOR,
&self.2.as_encoded_string(),
KEY_SEPARATOR,
&self.3.as_encoded_string(),
]
.concat()
}
fn as_secure_string(&self, table_name: &str, store_cipher: &StoreCipher) -> String {
[
&STANDARD_NO_PAD
.encode(store_cipher.hash_key(table_name, self.0.as_encoded_string().as_bytes())),
KEY_SEPARATOR,
&STANDARD_NO_PAD
.encode(store_cipher.hash_key(table_name, self.1.as_encoded_string().as_bytes())),
KEY_SEPARATOR,
&STANDARD_NO_PAD
.encode(store_cipher.hash_key(table_name, self.2.as_encoded_string().as_bytes())),
KEY_SEPARATOR,
&STANDARD_NO_PAD
.encode(store_cipher.hash_key(table_name, self.3.as_encoded_string().as_bytes())),
]
.concat()
}
}
impl<A, B, C, D, E> SafeEncode for (A, B, C, D, E)
where
A: SafeEncode,
B: SafeEncode,
C: SafeEncode,
D: SafeEncode,
E: SafeEncode,
{
fn as_encoded_string(&self) -> String {
[
&self.0.as_encoded_string(),
KEY_SEPARATOR,
&self.1.as_encoded_string(),
KEY_SEPARATOR,
&self.2.as_encoded_string(),
KEY_SEPARATOR,
&self.3.as_encoded_string(),
KEY_SEPARATOR,
&self.4.as_encoded_string(),
]
.concat()
}
fn as_secure_string(&self, table_name: &str, store_cipher: &StoreCipher) -> String {
[
&STANDARD_NO_PAD
.encode(store_cipher.hash_key(table_name, self.0.as_encoded_string().as_bytes())),
KEY_SEPARATOR,
&STANDARD_NO_PAD
.encode(store_cipher.hash_key(table_name, self.1.as_encoded_string().as_bytes())),
KEY_SEPARATOR,
&STANDARD_NO_PAD
.encode(store_cipher.hash_key(table_name, self.2.as_encoded_string().as_bytes())),
KEY_SEPARATOR,
&STANDARD_NO_PAD
.encode(store_cipher.hash_key(table_name, self.3.as_encoded_string().as_bytes())),
KEY_SEPARATOR,
&STANDARD_NO_PAD
.encode(store_cipher.hash_key(table_name, self.4.as_encoded_string().as_bytes())),
]
.concat()
}
}
impl SafeEncode for String {
fn as_encoded_string(&self) -> String {
self.replace(KEY_SEPARATOR, ESCAPED)
}
}
impl SafeEncode for str {
fn as_encoded_string(&self) -> String {
self.replace(KEY_SEPARATOR, ESCAPED)
}
}
impl<T: SafeEncode + ?Sized> SafeEncode for &T {
fn as_encoded_string(&self) -> String {
(*self).as_encoded_string()
}
}
impl SafeEncode for TransactionId {
fn as_encoded_string(&self) -> String {
self.to_string().as_encoded_string()
}
}
impl SafeEncode for GlobalAccountDataEventType {
fn as_encoded_string(&self) -> String {
self.to_string().as_encoded_string()
}
}
impl SafeEncode for RoomAccountDataEventType {
fn as_encoded_string(&self) -> String {
self.to_string().as_encoded_string()
}
}
impl SafeEncode for StateEventType {
fn as_encoded_string(&self) -> String {
self.to_string().as_encoded_string()
}
}
impl SafeEncode for ReceiptType {
fn as_encoded_string(&self) -> String {
self.as_str().as_encoded_string()
}
}
impl SafeEncode for RoomId {
fn as_encoded_string(&self) -> String {
self.as_str().as_encoded_string()
}
}
impl SafeEncode for OwnedRoomId {
fn as_encoded_string(&self) -> String {
self.as_str().as_encoded_string()
}
}
impl SafeEncode for UserId {
fn as_encoded_string(&self) -> String {
self.as_str().as_encoded_string()
}
}
impl SafeEncode for OwnedUserId {
fn as_encoded_string(&self) -> String {
self.as_str().as_encoded_string()
}
}
impl SafeEncode for DeviceId {
fn as_encoded_string(&self) -> String {
self.as_str().as_encoded_string()
}
}
impl SafeEncode for EventId {
fn as_encoded_string(&self) -> String {
self.as_str().as_encoded_string()
}
}
impl SafeEncode for OwnedEventId {
fn as_encoded_string(&self) -> String {
self.as_str().as_encoded_string()
}
}
impl SafeEncode for MxcUri {
fn as_encoded_string(&self) -> String {
self.as_str().as_encoded_string()
}
}
impl SafeEncode for usize {
fn as_encoded_string(&self) -> String {
self.to_string()
}
fn as_secure_string(&self, _table_name: &str, _store_cipher: &StoreCipher) -> String {
self.to_string()
}
}