1use base64::{
4 alphabet,
5 engine::{general_purpose, GeneralPurpose},
6 Engine,
7};
8use matrix_sdk_store_encryption::StoreCipher;
9use ruma::{
10 events::{
11 receipt::ReceiptType, GlobalAccountDataEventType, RoomAccountDataEventType, StateEventType,
12 },
13 DeviceId, EventId, MxcUri, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, TransactionId,
14 UserId,
15};
16use wasm_bindgen::JsValue;
17use web_sys::IdbKeyRange;
18
19pub const KEY_SEPARATOR: &str = "\u{001D}";
21pub const RANGE_END: &str = "\u{001E}";
24pub const ESCAPED: &str = "\u{001E}\u{001D}";
27
28const STANDARD_NO_PAD: GeneralPurpose =
29 GeneralPurpose::new(&alphabet::STANDARD, general_purpose::NO_PAD);
30
31pub trait SafeEncode {
38 fn as_encoded_string(&self) -> String;
44
45 fn as_secure_string(&self, table_name: &str, store_cipher: &StoreCipher) -> String {
49 STANDARD_NO_PAD
50 .encode(store_cipher.hash_key(table_name, self.as_encoded_string().as_bytes()))
51 }
52
53 fn encode_to_range(&self) -> Result<IdbKeyRange, String> {
57 let key = self.as_encoded_string();
58 IdbKeyRange::bound(
59 &JsValue::from([&key, KEY_SEPARATOR].concat()),
60 &JsValue::from([&key, RANGE_END].concat()),
61 )
62 .map_err(|e| e.as_string().unwrap_or_else(|| "Creating key range failed".to_owned()))
63 }
64
65 fn encode_to_range_secure(
66 &self,
67 table_name: &str,
68 store_cipher: &StoreCipher,
69 ) -> Result<IdbKeyRange, String> {
70 let key = self.as_secure_string(table_name, store_cipher);
71 IdbKeyRange::bound(
72 &JsValue::from([&key, KEY_SEPARATOR].concat()),
73 &JsValue::from([&key, RANGE_END].concat()),
74 )
75 .map_err(|e| e.as_string().unwrap_or_else(|| "Creating key range failed".to_owned()))
76 }
77}
78
79impl<A, B> SafeEncode for (A, B)
82where
83 A: SafeEncode,
84 B: SafeEncode,
85{
86 fn as_encoded_string(&self) -> String {
87 [&self.0.as_encoded_string(), KEY_SEPARATOR, &self.1.as_encoded_string()].concat()
88 }
89
90 fn as_secure_string(&self, table_name: &str, store_cipher: &StoreCipher) -> String {
91 [
92 &STANDARD_NO_PAD
93 .encode(store_cipher.hash_key(table_name, self.0.as_encoded_string().as_bytes())),
94 KEY_SEPARATOR,
95 &STANDARD_NO_PAD
96 .encode(store_cipher.hash_key(table_name, self.1.as_encoded_string().as_bytes())),
97 ]
98 .concat()
99 }
100}
101
102impl<A, B, C> SafeEncode for (A, B, C)
105where
106 A: SafeEncode,
107 B: SafeEncode,
108 C: SafeEncode,
109{
110 fn as_encoded_string(&self) -> String {
111 [
112 &self.0.as_encoded_string(),
113 KEY_SEPARATOR,
114 &self.1.as_encoded_string(),
115 KEY_SEPARATOR,
116 &self.2.as_encoded_string(),
117 ]
118 .concat()
119 }
120
121 fn as_secure_string(&self, table_name: &str, store_cipher: &StoreCipher) -> String {
122 [
123 &STANDARD_NO_PAD
124 .encode(store_cipher.hash_key(table_name, self.0.as_encoded_string().as_bytes())),
125 KEY_SEPARATOR,
126 &STANDARD_NO_PAD
127 .encode(store_cipher.hash_key(table_name, self.1.as_encoded_string().as_bytes())),
128 KEY_SEPARATOR,
129 &STANDARD_NO_PAD
130 .encode(store_cipher.hash_key(table_name, self.2.as_encoded_string().as_bytes())),
131 ]
132 .concat()
133 }
134}
135
136impl<A, B, C, D> SafeEncode for (A, B, C, D)
139where
140 A: SafeEncode,
141 B: SafeEncode,
142 C: SafeEncode,
143 D: SafeEncode,
144{
145 fn as_encoded_string(&self) -> String {
146 [
147 &self.0.as_encoded_string(),
148 KEY_SEPARATOR,
149 &self.1.as_encoded_string(),
150 KEY_SEPARATOR,
151 &self.2.as_encoded_string(),
152 KEY_SEPARATOR,
153 &self.3.as_encoded_string(),
154 ]
155 .concat()
156 }
157
158 fn as_secure_string(&self, table_name: &str, store_cipher: &StoreCipher) -> String {
159 [
160 &STANDARD_NO_PAD
161 .encode(store_cipher.hash_key(table_name, self.0.as_encoded_string().as_bytes())),
162 KEY_SEPARATOR,
163 &STANDARD_NO_PAD
164 .encode(store_cipher.hash_key(table_name, self.1.as_encoded_string().as_bytes())),
165 KEY_SEPARATOR,
166 &STANDARD_NO_PAD
167 .encode(store_cipher.hash_key(table_name, self.2.as_encoded_string().as_bytes())),
168 KEY_SEPARATOR,
169 &STANDARD_NO_PAD
170 .encode(store_cipher.hash_key(table_name, self.3.as_encoded_string().as_bytes())),
171 ]
172 .concat()
173 }
174}
175
176impl<A, B, C, D, E> SafeEncode for (A, B, C, D, E)
179where
180 A: SafeEncode,
181 B: SafeEncode,
182 C: SafeEncode,
183 D: SafeEncode,
184 E: SafeEncode,
185{
186 fn as_encoded_string(&self) -> String {
187 [
188 &self.0.as_encoded_string(),
189 KEY_SEPARATOR,
190 &self.1.as_encoded_string(),
191 KEY_SEPARATOR,
192 &self.2.as_encoded_string(),
193 KEY_SEPARATOR,
194 &self.3.as_encoded_string(),
195 KEY_SEPARATOR,
196 &self.4.as_encoded_string(),
197 ]
198 .concat()
199 }
200
201 fn as_secure_string(&self, table_name: &str, store_cipher: &StoreCipher) -> String {
202 [
203 &STANDARD_NO_PAD
204 .encode(store_cipher.hash_key(table_name, self.0.as_encoded_string().as_bytes())),
205 KEY_SEPARATOR,
206 &STANDARD_NO_PAD
207 .encode(store_cipher.hash_key(table_name, self.1.as_encoded_string().as_bytes())),
208 KEY_SEPARATOR,
209 &STANDARD_NO_PAD
210 .encode(store_cipher.hash_key(table_name, self.2.as_encoded_string().as_bytes())),
211 KEY_SEPARATOR,
212 &STANDARD_NO_PAD
213 .encode(store_cipher.hash_key(table_name, self.3.as_encoded_string().as_bytes())),
214 KEY_SEPARATOR,
215 &STANDARD_NO_PAD
216 .encode(store_cipher.hash_key(table_name, self.4.as_encoded_string().as_bytes())),
217 ]
218 .concat()
219 }
220}
221
222impl SafeEncode for String {
223 fn as_encoded_string(&self) -> String {
224 self.replace(KEY_SEPARATOR, ESCAPED)
225 }
226}
227
228impl SafeEncode for str {
229 fn as_encoded_string(&self) -> String {
230 self.replace(KEY_SEPARATOR, ESCAPED)
231 }
232}
233
234impl<T: SafeEncode + ?Sized> SafeEncode for &T {
235 fn as_encoded_string(&self) -> String {
236 (*self).as_encoded_string()
237 }
238}
239
240impl SafeEncode for TransactionId {
241 fn as_encoded_string(&self) -> String {
242 self.to_string().as_encoded_string()
243 }
244}
245
246impl SafeEncode for GlobalAccountDataEventType {
247 fn as_encoded_string(&self) -> String {
248 self.to_string().as_encoded_string()
249 }
250}
251
252impl SafeEncode for RoomAccountDataEventType {
253 fn as_encoded_string(&self) -> String {
254 self.to_string().as_encoded_string()
255 }
256}
257
258impl SafeEncode for StateEventType {
259 fn as_encoded_string(&self) -> String {
260 self.to_string().as_encoded_string()
261 }
262}
263
264impl SafeEncode for ReceiptType {
265 fn as_encoded_string(&self) -> String {
266 self.as_str().as_encoded_string()
267 }
268}
269
270impl SafeEncode for RoomId {
271 fn as_encoded_string(&self) -> String {
272 self.as_str().as_encoded_string()
273 }
274}
275
276impl SafeEncode for OwnedRoomId {
277 fn as_encoded_string(&self) -> String {
278 self.as_str().as_encoded_string()
279 }
280}
281
282impl SafeEncode for UserId {
283 fn as_encoded_string(&self) -> String {
284 self.as_str().as_encoded_string()
285 }
286}
287
288impl SafeEncode for OwnedUserId {
289 fn as_encoded_string(&self) -> String {
290 self.as_str().as_encoded_string()
291 }
292}
293
294impl SafeEncode for DeviceId {
295 fn as_encoded_string(&self) -> String {
296 self.as_str().as_encoded_string()
297 }
298}
299
300impl SafeEncode for EventId {
301 fn as_encoded_string(&self) -> String {
302 self.as_str().as_encoded_string()
303 }
304}
305
306impl SafeEncode for OwnedEventId {
307 fn as_encoded_string(&self) -> String {
308 self.as_str().as_encoded_string()
309 }
310}
311
312impl SafeEncode for MxcUri {
313 fn as_encoded_string(&self) -> String {
314 self.as_str().as_encoded_string()
315 }
316}
317
318impl SafeEncode for usize {
319 fn as_encoded_string(&self) -> String {
320 self.to_string()
321 }
322
323 fn as_secure_string(&self, _table_name: &str, _store_cipher: &StoreCipher) -> String {
324 self.to_string()
325 }
326}