1use crate::{
2 access_control::{Access, AccessSecrets, LocalSecret, SetLocalSecret, WriteSecrets},
3 crypto::{
4 Hash, Password, PasswordSalt,
5 cipher::{self, Nonce},
6 sign,
7 },
8 db::{self, DatabaseId},
9 device_id::DeviceId,
10 protocol::RepositoryId,
11 store::Error as StoreError,
12};
13use rand::{Rng, rngs::OsRng};
14use sqlx::Row;
15use std::{borrow::Cow, fmt, time::Duration};
16use tracing::instrument;
17use zeroize::Zeroize;
18
19const REPOSITORY_ID: &[u8] = b"repository_id";
21const READ_PASSWORD_SALT: &[u8] = b"read_password_salt";
26const WRITE_PASSWORD_SALT: &[u8] = b"write_password_salt";
27const WRITER_ID: &[u8] = b"writer_id";
28const READ_KEY: &[u8] = b"read_key";
29const WRITE_KEY: &[u8] = b"write_key";
30const DATABASE_ID: &[u8] = b"database_id";
31
32const DEVICE_ID: &[u8] = b"device_id";
33const READ_KEY_VALIDATOR: &[u8] = b"read_key_validator";
34
35const QUOTA: &[u8] = b"quota";
36const BLOCK_EXPIRATION: &[u8] = b"block_expiration";
37
38const DATA_VERSION: &[u8] = b"data_version";
40
41const DEPRECATED_ACCESS_KEY: &[u8] = b"access_key"; const DEPRECATED_PASSWORD_SALT: &[u8] = b"password_salt";
57
58pub struct Metadata {
62 db: db::Pool,
63}
64
65impl Metadata {
66 pub(crate) fn new(db: db::Pool) -> Self {
67 Self { db }
68 }
69
70 #[instrument(skip(self), fields(value))]
71 pub async fn get<T>(&self, name: &str) -> Result<Option<T>, StoreError>
72 where
73 T: MetadataGet + fmt::Debug,
74 {
75 let mut conn = self.db.acquire().await?;
76 get_public(&mut conn, name.as_bytes()).await
77 }
78
79 pub async fn set<'a, T>(&self, name: &'a str, value: T) -> Result<(), StoreError>
80 where
81 T: MetadataSet<'a> + fmt::Debug,
82 {
83 let mut tx = self.write().await?;
84 tx.set(name, value).await?;
85 tx.commit().await?;
86
87 Ok(())
88 }
89
90 pub async fn remove(&self, name: &str) -> Result<(), StoreError> {
91 let mut tx = self.write().await?;
92 tx.remove(name).await?;
93 tx.commit().await?;
94
95 Ok(())
96 }
97
98 pub async fn write(&self) -> Result<MetadataWriter, StoreError> {
99 Ok(MetadataWriter {
100 tx: self.db.begin_write().await?,
101 })
102 }
103}
104
105pub struct MetadataWriter {
106 tx: db::WriteTransaction,
107}
108
109impl MetadataWriter {
110 pub async fn get<T>(&mut self, name: &str) -> Result<Option<T>, StoreError>
111 where
112 T: MetadataGet + fmt::Debug,
113 {
114 get_public(&mut self.tx, name.as_bytes()).await
115 }
116
117 pub async fn set<'a, T>(&mut self, name: &'a str, value: T) -> Result<(), StoreError>
118 where
119 T: MetadataSet<'a> + fmt::Debug,
120 {
121 set_public(&mut self.tx, name.as_bytes(), value).await
122 }
123
124 pub async fn remove(&mut self, name: &str) -> Result<(), StoreError> {
125 remove_public(&mut self.tx, name.as_bytes()).await
126 }
127
128 pub async fn commit(self) -> Result<(), StoreError> {
129 self.tx.commit().await?;
130 Ok(())
131 }
132}
133
134pub(crate) enum KeyType {
138 Read,
139 Write,
140}
141
142pub(crate) async fn password_to_key(
143 tx: &mut db::WriteTransaction,
144 key_type: KeyType,
145 password: &Password,
146) -> Result<cipher::SecretKey, StoreError> {
147 let salt = get_password_salt(tx, key_type).await?;
148
149 Ok(cipher::SecretKey::derive_from_password(
150 password.as_ref(),
151 &salt,
152 ))
153}
154
155async fn secret_to_key<'a>(
156 tx: &mut db::WriteTransaction,
157 key_type: KeyType,
158 secret: &'a LocalSecret,
159) -> Result<Cow<'a, cipher::SecretKey>, StoreError> {
160 match secret {
161 LocalSecret::Password(password) => password_to_key(tx, key_type, password)
162 .await
163 .map(Cow::Owned),
164 LocalSecret::SecretKey(key) => Ok(Cow::Borrowed(key)),
165 }
166}
167
168pub(crate) fn secret_to_key_and_salt(
169 secret: &'_ SetLocalSecret,
170) -> (Cow<'_, cipher::SecretKey>, Cow<'_, PasswordSalt>) {
171 match secret {
172 SetLocalSecret::Password(password) => {
173 let salt = PasswordSalt::random();
174 let key = cipher::SecretKey::derive_from_password(password.as_ref(), &salt);
175 (Cow::Owned(key), Cow::Owned(salt))
176 }
177 SetLocalSecret::KeyAndSalt { key, salt } => (Cow::Borrowed(key), Cow::Borrowed(salt)),
178 }
179}
180
181pub(crate) async fn get_or_generate_database_id(db: &db::Pool) -> Result<DatabaseId, StoreError> {
185 let mut tx = db.begin_write().await?;
186 let database_id = match get_public_blob(&mut tx, DATABASE_ID).await {
187 Ok(Some(database_id)) => database_id,
188 Ok(None) => {
189 let database_id: DatabaseId = OsRng.r#gen();
190 set_public_blob(&mut tx, DATABASE_ID, &database_id).await?;
191 tx.commit().await?;
192 database_id
193 }
194 Err(error) => return Err(error),
195 };
196
197 Ok(database_id)
198}
199
200pub(crate) async fn get_writer_id(
204 conn: &mut db::Connection,
205 local_key: Option<&cipher::SecretKey>,
206) -> Result<Option<sign::PublicKey>, StoreError> {
207 get_blob(conn, WRITER_ID, local_key).await
208}
209
210pub(crate) async fn set_writer_id(
211 tx: &mut db::WriteTransaction,
212 writer_id: &sign::PublicKey,
213 local_key: Option<&cipher::SecretKey>,
214) -> Result<(), StoreError> {
215 set_blob(tx, WRITER_ID, writer_id, local_key).await?;
216 Ok(())
217}
218
219pub(crate) fn generate_writer_id() -> sign::PublicKey {
222 sign::Keypair::random().public_key()
223}
224
225pub(crate) async fn get_or_generate_writer_id(
226 tx: &mut db::WriteTransaction,
227 local_key: Option<&cipher::SecretKey>,
228) -> Result<sign::PublicKey, StoreError> {
229 let writer_id = if let Some(writer_id) = get_writer_id(tx, local_key).await? {
230 writer_id
231 } else {
232 let writer_id = generate_writer_id();
233 set_writer_id(tx, &writer_id, local_key).await?;
234 writer_id
235 };
236
237 Ok(writer_id)
238}
239
240pub(crate) async fn check_device_id(
246 conn: &mut db::Connection,
247 device_id: &DeviceId,
248) -> Result<bool, StoreError> {
249 let old_device_id: Option<DeviceId> = get_public_blob(conn, DEVICE_ID).await?;
250 Ok(old_device_id.as_ref() == Some(device_id))
251}
252
253pub(crate) async fn set_device_id(
254 tx: &mut db::WriteTransaction,
255 device_id: &DeviceId,
256) -> Result<(), StoreError> {
257 set_public_blob(tx, DEVICE_ID, device_id).await?;
258 Ok(())
259}
260
261pub(super) async fn get_repository_id(
265 conn: &mut db::Connection,
266) -> Result<RepositoryId, StoreError> {
267 get_public_blob(conn, REPOSITORY_ID)
269 .await?
270 .ok_or(StoreError::MalformedData)
271}
272
273async fn set_public_read_key(
274 tx: &mut db::WriteTransaction,
275 read_key: &cipher::SecretKey,
276) -> Result<(), StoreError> {
277 set_public_blob(tx, READ_KEY, read_key).await
278}
279
280async fn set_secret_read_key(
281 tx: &mut db::WriteTransaction,
282 id: &RepositoryId,
283 read_key: &cipher::SecretKey,
284 local_secret_key: &cipher::SecretKey,
285 local_salt: &PasswordSalt,
286) -> Result<(), StoreError> {
287 set_secret_blob(tx, READ_KEY, read_key, local_secret_key).await?;
288 set_secret_blob(tx, READ_KEY_VALIDATOR, read_key_validator(id), read_key).await?;
289 set_password_salt(tx, KeyType::Read, local_salt).await
290}
291
292pub(crate) async fn set_read_key(
293 tx: &mut db::WriteTransaction,
294 id: &RepositoryId,
295 read_key: &cipher::SecretKey,
296 local: Option<(&cipher::SecretKey, &PasswordSalt)>,
297) -> Result<(), StoreError> {
298 if let Some((local_secret_key, local_salt)) = local {
299 remove_public_read_key(tx).await?;
300 set_secret_read_key(tx, id, read_key, local_secret_key, local_salt).await
301 } else {
302 set_public_read_key(tx, read_key).await?;
303 obfuscate_secret_read_key(tx).await
304 }
305}
306
307async fn remove_public_read_key(tx: &mut db::WriteTransaction) -> Result<(), StoreError> {
308 remove_public(tx, READ_KEY).await
309}
310
311async fn obfuscate_secret_read_key(tx: &mut db::WriteTransaction) -> Result<(), StoreError> {
312 let dummy_id = RepositoryId::from(sign::Keypair::random().public_key());
313 let dummy_local_key = cipher::SecretKey::random();
314 let dummy_read_key = cipher::SecretKey::random();
315
316 set_secret_blob(tx, READ_KEY, &dummy_read_key, &dummy_local_key).await?;
317 set_secret_blob(
318 tx,
319 READ_KEY_VALIDATOR,
320 read_key_validator(&dummy_id),
321 &dummy_read_key,
322 )
323 .await?;
324
325 obfuscate_read_password_salt(tx).await?;
326
327 Ok(())
328}
329
330pub(crate) async fn remove_read_key(tx: &mut db::WriteTransaction) -> Result<(), StoreError> {
331 remove_public_read_key(tx).await?;
332 obfuscate_secret_read_key(tx).await
333}
334
335async fn set_public_write_key(
338 tx: &mut db::WriteTransaction,
339 secrets: &WriteSecrets,
340) -> Result<(), StoreError> {
341 set_public_blob(tx, WRITE_KEY, secrets.write_keys.to_bytes()).await
342}
343
344async fn set_secret_write_key(
345 tx: &mut db::WriteTransaction,
346 secrets: &WriteSecrets,
347 local_secret_key: &cipher::SecretKey,
348 local_salt: &PasswordSalt,
349) -> Result<(), StoreError> {
350 set_secret_blob(
351 tx,
352 WRITE_KEY,
353 secrets.write_keys.to_bytes(),
354 local_secret_key,
355 )
356 .await?;
357 set_password_salt(tx, KeyType::Write, local_salt).await
358}
359
360pub(crate) async fn set_write_key(
361 tx: &mut db::WriteTransaction,
362 secrets: &WriteSecrets,
363 local: Option<(&cipher::SecretKey, &PasswordSalt)>,
364) -> Result<(), StoreError> {
365 if let Some((local_secret_key, local_salt)) = local {
366 remove_public_write_key(tx).await?;
367 set_secret_write_key(tx, secrets, local_secret_key, local_salt).await
368 } else {
369 set_public_write_key(tx, secrets).await?;
370 obfuscate_secret_write_key(tx).await
371 }
372}
373
374async fn remove_public_write_key(tx: &mut db::WriteTransaction) -> Result<(), StoreError> {
375 remove_public(tx, WRITE_KEY).await
376}
377
378async fn obfuscate_secret_write_key(tx: &mut db::WriteTransaction) -> Result<(), StoreError> {
379 let dummy_local_key = cipher::SecretKey::random();
380 let dummy_write_key = sign::Keypair::random().to_bytes();
381 set_secret_blob(tx, WRITE_KEY, &dummy_write_key, &dummy_local_key).await?;
382 obfuscate_write_password_salt(tx).await
383}
384
385pub(crate) async fn remove_write_key(tx: &mut db::WriteTransaction) -> Result<(), StoreError> {
386 remove_public_write_key(tx).await?;
387 obfuscate_secret_write_key(tx).await
388}
389
390pub(crate) async fn get_password_salt(
393 tx: &mut db::WriteTransaction,
394 key_type: KeyType,
395) -> Result<PasswordSalt, StoreError> {
396 migrate_to_separate_password_salts(tx).await?;
397
398 match key_type {
399 KeyType::Read => get_public_blob(tx, READ_PASSWORD_SALT).await?,
400 KeyType::Write => get_public_blob(tx, WRITE_PASSWORD_SALT).await?,
401 }
402 .ok_or(StoreError::MalformedData)
404}
405
406async fn set_password_salt(
407 tx: &mut db::WriteTransaction,
408 key_type: KeyType,
409 salt: &PasswordSalt,
410) -> Result<(), StoreError> {
411 migrate_to_separate_password_salts(tx).await?;
412 match key_type {
413 KeyType::Read => set_public_blob(tx, READ_PASSWORD_SALT, salt).await,
414 KeyType::Write => set_public_blob(tx, WRITE_PASSWORD_SALT, salt).await,
415 }
416}
417
418async fn obfuscate_read_password_salt(tx: &mut db::WriteTransaction) -> Result<(), StoreError> {
419 migrate_to_separate_password_salts(tx).await?;
420 let dummy_salt = PasswordSalt::random();
421 set_public_blob(tx, READ_PASSWORD_SALT, &dummy_salt).await
422}
423
424async fn obfuscate_write_password_salt(tx: &mut db::WriteTransaction) -> Result<(), StoreError> {
425 migrate_to_separate_password_salts(tx).await?;
426 let dummy_salt = PasswordSalt::random();
427 set_public_blob(tx, WRITE_PASSWORD_SALT, &dummy_salt).await
428}
429
430async fn migrate_to_separate_password_salts(
431 tx: &mut db::WriteTransaction,
432) -> Result<(), StoreError> {
433 let single_salt: PasswordSalt = match get_public_blob(tx, DEPRECATED_PASSWORD_SALT).await {
434 Ok(Some(salt)) => salt,
435 Ok(None) => {
436 return Ok(());
438 }
439 Err(error) => return Err(error),
440 };
441
442 set_public_blob(tx, READ_PASSWORD_SALT, &single_salt).await?;
443 set_public_blob(tx, WRITE_PASSWORD_SALT, &single_salt).await?;
444 remove_public(tx, DEPRECATED_PASSWORD_SALT).await
445}
446
447pub(crate) async fn requires_local_secret_for_reading(
450 conn: &mut db::Connection,
451) -> Result<bool, StoreError> {
452 match get_public_blob::<cipher::SecretKey>(conn, READ_KEY).await {
453 Ok(Some(_)) => return Ok(false),
454 Ok(None) => (),
455 Err(err) => return Err(err),
456 }
457
458 match get_public_blob::<sign::Keypair>(conn, WRITE_KEY).await {
459 Ok(Some(_)) => Ok(false),
460 Ok(None) => Ok(true),
461 Err(err) => Err(err),
462 }
463}
464
465pub(crate) async fn requires_local_secret_for_writing(
466 conn: &mut db::Connection,
467) -> Result<bool, StoreError> {
468 match get_public_blob::<sign::Keypair>(conn, WRITE_KEY).await {
469 Ok(Some(_)) => Ok(false),
470 Ok(None) => Ok(true),
471 Err(err) => Err(err),
472 }
473}
474
475pub(crate) async fn initialize_access_secrets<'a>(
476 tx: &mut db::WriteTransaction,
477 access: &'a Access,
478) -> Result<LocalKeys<'a>, StoreError> {
479 set_public_blob(tx, REPOSITORY_ID, access.id()).await?;
480 set_access(tx, access).await
481}
482
483pub(crate) async fn set_access<'a>(
484 tx: &mut db::WriteTransaction,
485 access: &'a Access,
486) -> Result<LocalKeys<'a>, StoreError> {
487 match access {
488 Access::Blind { .. } => {
489 remove_public_read_key(tx).await?;
490 obfuscate_secret_read_key(tx).await?;
491 remove_public_write_key(tx).await?;
492 obfuscate_secret_write_key(tx).await?;
493
494 Ok(LocalKeys {
495 read: None,
496 write: None,
497 })
498 }
499 Access::ReadUnlocked { id: _, read_key } => {
500 set_public_read_key(tx, read_key).await?;
501 obfuscate_secret_read_key(tx).await?;
502 remove_public_write_key(tx).await?;
503 obfuscate_secret_write_key(tx).await?;
504
505 Ok(LocalKeys {
506 read: None,
507 write: None,
508 })
509 }
510 Access::ReadLocked {
511 id,
512 local_secret,
513 read_key,
514 } => {
515 let (local_secret_key, local_salt) = secret_to_key_and_salt(local_secret);
516
517 remove_public_read_key(tx).await?;
518 set_secret_read_key(tx, id, read_key, &local_secret_key, &local_salt).await?;
519 remove_public_write_key(tx).await?;
520 obfuscate_secret_write_key(tx).await?;
521
522 Ok(LocalKeys {
523 read: Some(local_secret_key),
524 write: None,
525 })
526 }
527 Access::WriteUnlocked { secrets } => {
528 set_public_read_key(tx, &secrets.read_key).await?;
529 obfuscate_secret_read_key(tx).await?;
530 set_public_write_key(tx, secrets).await?;
531 obfuscate_secret_write_key(tx).await?;
532
533 Ok(LocalKeys {
534 read: None,
535 write: None,
536 })
537 }
538 Access::WriteLocked {
539 local_read_secret,
540 local_write_secret,
541 secrets,
542 } => {
543 let (local_read_key, local_read_salt) = secret_to_key_and_salt(local_read_secret);
544 let (local_write_key, local_write_salt) = secret_to_key_and_salt(local_write_secret);
545
546 remove_public_read_key(tx).await?;
547 set_secret_read_key(
548 tx,
549 &secrets.id,
550 &secrets.read_key,
551 &local_read_key,
552 &local_read_salt,
553 )
554 .await?;
555 remove_public_write_key(tx).await?;
556 set_secret_write_key(tx, secrets, &local_write_key, &local_write_salt).await?;
557
558 Ok(LocalKeys {
559 read: Some(local_read_key),
560 write: Some(local_write_key),
561 })
562 }
563 Access::WriteLockedReadUnlocked {
564 local_write_secret,
565 secrets,
566 } => {
567 let (local_write_key, local_write_salt) = secret_to_key_and_salt(local_write_secret);
568
569 set_public_read_key(tx, &secrets.read_key).await?;
570 obfuscate_secret_read_key(tx).await?;
571 remove_public_write_key(tx).await?;
572 set_secret_write_key(tx, secrets, &local_write_key, &local_write_salt).await?;
573
574 Ok(LocalKeys {
575 read: None,
576 write: Some(local_write_key),
577 })
578 }
579 }
580}
581
582pub(crate) struct LocalKeys<'a> {
583 #[allow(unused)]
584 pub read: Option<Cow<'a, cipher::SecretKey>>,
585 pub write: Option<Cow<'a, cipher::SecretKey>>,
586}
587
588pub(crate) async fn get_access_secrets<'a>(
589 tx: &mut db::WriteTransaction,
590 local_secret: Option<&'a LocalSecret>,
591) -> Result<(AccessSecrets, Option<Cow<'a, cipher::SecretKey>>), StoreError> {
592 let id = get_repository_id(tx).await?;
593
594 let local_write_key = match &local_secret {
595 Some(local_secret) => Some(secret_to_key(tx, KeyType::Write, local_secret).await?),
596 None => None,
597 };
598
599 match get_write_key(tx, local_write_key.as_deref(), &id).await {
600 Ok(Some(write_keys)) => {
601 let access = AccessSecrets::Write(WriteSecrets::from(write_keys));
602 return Ok((access, local_write_key));
603 }
604 Ok(None) => (),
605 Err(e) => return Err(e),
606 }
607
608 let local_read_key = match &local_secret {
609 Some(local_secret) => Some(secret_to_key(tx, KeyType::Read, local_secret).await?),
610 None => None,
611 };
612
613 match get_read_key(tx, local_read_key.as_deref(), &id).await {
615 Ok(Some(read_key)) => {
616 let access = AccessSecrets::Read { id, read_key };
617 return Ok((access, local_write_key));
618 }
619 Ok(None) => (),
620 Err(e) => return Err(e),
621 }
622
623 Ok((AccessSecrets::Blind { id }, None))
625}
626
627async fn get_write_key(
629 conn: &mut db::Connection,
630 local_key: Option<&cipher::SecretKey>,
631 id: &RepositoryId,
632) -> Result<Option<sign::Keypair>, StoreError> {
633 let write_keys: Option<sign::Keypair> = match get_blob(conn, WRITE_KEY, local_key).await? {
635 Some(write_keys) => Some(write_keys),
636 None => {
637 get_blob(conn, DEPRECATED_ACCESS_KEY, local_key).await?
639 }
640 };
641
642 let Some(write_keys) = write_keys else {
643 return Ok(None);
644 };
645
646 let derived_id = RepositoryId::from(write_keys.public_key());
647
648 if &derived_id == id {
649 Ok(Some(write_keys))
650 } else {
651 Ok(None)
652 }
653}
654
655async fn get_read_key(
656 conn: &mut db::Connection,
657 local_key: Option<&cipher::SecretKey>,
658 id: &RepositoryId,
659) -> Result<Option<cipher::SecretKey>, StoreError> {
660 let read_key: cipher::SecretKey = match get_blob(conn, READ_KEY, local_key).await {
661 Ok(Some(read_key)) => read_key,
662 Ok(None) => {
663 if let Some(key) = get_blob(conn, DEPRECATED_ACCESS_KEY, local_key).await? {
665 key
666 } else {
667 return Ok(None);
668 }
669 }
670 Err(error) => return Err(error),
671 };
672
673 if local_key.is_none() {
674 return Ok(Some(read_key));
675 }
676
677 let key_validator_expected = read_key_validator(id);
678 let key_validator_actual: Option<Hash> =
679 get_secret_blob(conn, READ_KEY_VALIDATOR, &read_key).await?;
680
681 if key_validator_actual == Some(key_validator_expected) {
682 Ok(Some(read_key))
684 } else {
685 Ok(None)
686 }
687}
688
689pub(crate) mod quota {
693 use super::*;
694 use crate::protocol::StorageSize;
695
696 pub(crate) async fn get(conn: &mut db::Connection) -> Result<Option<StorageSize>, StoreError> {
697 Ok(get_public(conn, QUOTA).await?.map(StorageSize::from_bytes))
698 }
699
700 pub(crate) async fn set(tx: &mut db::WriteTransaction, value: u64) -> Result<(), StoreError> {
701 set_public(tx, QUOTA, value).await
702 }
703
704 pub(crate) async fn remove(tx: &mut db::WriteTransaction) -> Result<(), StoreError> {
705 remove_public(tx, QUOTA).await
706 }
707}
708
709pub(crate) mod block_expiration {
713 use super::*;
714
715 pub(crate) async fn get(conn: &mut db::Connection) -> Result<Option<Duration>, StoreError> {
716 Ok(get_public(conn, BLOCK_EXPIRATION)
717 .await?
718 .map(Duration::from_millis))
719 }
720
721 pub(crate) async fn set(
722 tx: &mut db::WriteTransaction,
723 value: Option<Duration>,
724 ) -> Result<(), StoreError> {
725 if let Some(duration) = value {
726 set_public(
727 tx,
728 BLOCK_EXPIRATION,
729 u64::try_from(duration.as_millis()).unwrap_or(u64::MAX),
730 )
731 .await
732 } else {
733 remove_public(tx, BLOCK_EXPIRATION).await
734 }
735 }
736}
737
738pub(crate) mod data_version {
742 use super::*;
743
744 pub(crate) async fn get(conn: &mut db::Connection) -> Result<u64, StoreError> {
745 Ok(get_public(conn, DATA_VERSION).await?.unwrap_or(0))
746 }
747
748 pub(crate) async fn set(tx: &mut db::WriteTransaction, value: u64) -> Result<(), StoreError> {
749 set_public(tx, DATA_VERSION, value).await
750 }
751}
752
753async fn get_public_blob<T>(conn: &mut db::Connection, id: &[u8]) -> Result<Option<T>, StoreError>
757where
758 T: for<'a> TryFrom<&'a [u8]>,
759{
760 let row = sqlx::query("SELECT value FROM metadata_public WHERE name = ?")
761 .bind(id)
762 .fetch_optional(conn)
763 .await?;
764
765 if let Some(row) = row {
766 let bytes: &[u8] = row.get(0);
767 let bytes = bytes.try_into().map_err(|_| StoreError::MalformedData)?;
768 Ok(Some(bytes))
769 } else {
770 Ok(None)
771 }
772}
773
774async fn set_public_blob<T>(
775 tx: &mut db::WriteTransaction,
776 id: &[u8],
777 blob: T,
778) -> Result<(), StoreError>
779where
780 T: AsRef<[u8]>,
781{
782 sqlx::query("INSERT OR REPLACE INTO metadata_public(name, value) VALUES (?, ?)")
783 .bind(id)
784 .bind(blob.as_ref())
785 .execute(tx)
786 .await?;
787
788 Ok(())
789}
790
791async fn get_public<T>(conn: &mut db::Connection, id: &[u8]) -> Result<Option<T>, StoreError>
792where
793 T: MetadataGet,
794{
795 let row = sqlx::query("SELECT value FROM metadata_public WHERE name = ?")
796 .bind(id)
797 .fetch_optional(conn)
798 .await?;
799 if let Some(row) = row {
800 let value = T::get(&row).map_err(|_| StoreError::MalformedData)?;
801 Ok(Some(value))
802 } else {
803 Ok(None)
804 }
805}
806
807async fn set_public<'a, T>(
808 tx: &mut db::WriteTransaction,
809 id: &'a [u8],
810 value: T,
811) -> Result<(), StoreError>
812where
813 T: MetadataSet<'a>,
814{
815 let query = sqlx::query("INSERT OR REPLACE INTO metadata_public(name, value) VALUES (?, ?)");
816 let query = query.bind(id);
817 let query = value.bind(query);
818 query.execute(tx).await?;
819
820 Ok(())
821}
822
823async fn remove_public(tx: &mut db::WriteTransaction, id: &[u8]) -> Result<(), StoreError> {
824 sqlx::query("DELETE FROM metadata_public WHERE name = ?")
825 .bind(id)
826 .execute(tx)
827 .await?;
828 Ok(())
829}
830
831pub trait MetadataGet: detail::Get {}
832pub trait MetadataSet<'a>: detail::Set<'a> {}
833
834impl<T> MetadataGet for T where T: detail::Get {}
835impl<'a, T> MetadataSet<'a> for T where T: detail::Set<'a> {}
836
837mod detail {
839 use crate::db;
840 use sqlx::{
841 Row, Sqlite,
842 sqlite::{SqliteArguments, SqliteRow},
843 };
844
845 type Query<'q> = sqlx::query::Query<'q, Sqlite, SqliteArguments<'q>>;
846
847 pub trait Get: Sized {
848 fn get(row: &SqliteRow) -> Result<Self, sqlx::Error>;
849 }
850
851 pub trait Set<'a>
852 where
853 Self: 'a,
854 {
855 fn bind(self, query: Query<'a>) -> Query<'a>;
856 }
857
858 impl Get for bool {
859 fn get(row: &SqliteRow) -> Result<Self, sqlx::Error> {
860 row.try_get(0)
861 }
862 }
863
864 impl<'a> Set<'a> for bool {
865 fn bind(self, query: Query<'a>) -> Query<'a> {
866 query.bind(self)
867 }
868 }
869
870 impl Get for u64 {
871 fn get(row: &SqliteRow) -> Result<Self, sqlx::Error> {
872 row.try_get(0).map(db::decode_u64)
873 }
874 }
875
876 impl<'a> Set<'a> for u64 {
877 fn bind(self, query: Query<'a>) -> Query<'a> {
878 query.bind(db::encode_u64(self))
879 }
880 }
881
882 impl Get for String {
883 fn get(row: &SqliteRow) -> Result<Self, sqlx::Error> {
884 row.try_get(0)
885 }
886 }
887
888 impl<'a> Set<'a> for String {
889 fn bind(self, query: Query<'a>) -> Query<'a> {
890 query.bind(self)
891 }
892 }
893
894 impl<'a> Set<'a> for &'a str {
895 fn bind(self, query: Query<'a>) -> Query<'a> {
896 query.bind(self)
897 }
898 }
899}
900
901async fn get_secret_blob<T>(
905 conn: &mut db::Connection,
906 id: &[u8],
907 local_key: &cipher::SecretKey,
908) -> Result<Option<T>, StoreError>
909where
910 for<'a> T: TryFrom<&'a [u8]>,
911{
912 let row = sqlx::query("SELECT nonce, value FROM metadata_secret WHERE name = ?")
913 .bind(id)
914 .fetch_optional(conn)
915 .await?;
916
917 let Some(row) = row else {
918 return Ok(None);
919 };
920
921 let nonce: &[u8] = row.get(0);
922 let nonce = Nonce::try_from(nonce).map_err(|_| StoreError::MalformedData)?;
923
924 let mut buffer: Vec<_> = row.get(1);
925
926 local_key.decrypt_no_aead(&nonce, &mut buffer);
927
928 let secret = T::try_from(&buffer).map_err(|_| StoreError::MalformedData)?;
929 buffer.zeroize();
930
931 Ok(Some(secret))
932}
933
934async fn set_secret_blob<T>(
935 tx: &mut db::WriteTransaction,
936 id: &[u8],
937 blob: T,
938 local_key: &cipher::SecretKey,
939) -> Result<(), StoreError>
940where
941 T: AsRef<[u8]>,
942{
943 let nonce = make_nonce();
944
945 let mut cypher = blob.as_ref().to_vec();
946 local_key.encrypt_no_aead(&nonce, &mut cypher);
947
948 sqlx::query(
949 "INSERT OR REPLACE INTO metadata_secret(name, nonce, value)
950 VALUES (?, ?, ?)",
951 )
952 .bind(id)
953 .bind(&nonce[..])
954 .bind(&cypher)
955 .execute(tx)
956 .await?;
957
958 Ok(())
959}
960
961fn make_nonce() -> Nonce {
962 rand::random()
966}
967
968fn read_key_validator(id: &RepositoryId) -> Hash {
970 id.salted_hash(b"ouisync read key validator")
971}
972
973async fn get_blob<T>(
975 conn: &mut db::Connection,
976 id: &[u8],
977 local_key: Option<&cipher::SecretKey>,
978) -> Result<Option<T>, StoreError>
979where
980 for<'a> T: TryFrom<&'a [u8]>,
981{
982 match local_key {
983 Some(local_key) => get_secret_blob(conn, id, local_key).await,
984 None => get_public_blob(conn, id).await,
985 }
986}
987
988async fn set_blob<T>(
989 tx: &mut db::WriteTransaction,
990 id: &[u8],
991 blob: T,
992 local_key: Option<&cipher::SecretKey>,
993) -> Result<(), StoreError>
994where
995 T: AsRef<[u8]>,
996{
997 match local_key {
998 Some(local_key) => set_secret_blob(tx, id, blob, local_key).await,
999 None => set_public_blob(tx, id, blob).await,
1000 }
1001}
1002
1003#[cfg(test)]
1006mod tests {
1007 use super::*;
1008 use crate::db;
1009 use tempfile::TempDir;
1010
1011 async fn setup() -> (TempDir, db::Pool) {
1012 db::create_temp().await.unwrap()
1013 }
1014
1015 #[tokio::test(flavor = "multi_thread")]
1016 async fn store_plaintext() {
1017 let (_base_dir, pool) = setup().await;
1018 let mut tx = pool.begin_write().await.unwrap();
1019
1020 set_public_blob(&mut tx, b"hello", b"world").await.unwrap();
1021
1022 let v: [u8; 5] = get_public_blob(&mut tx, b"hello").await.unwrap().unwrap();
1023
1024 assert_eq!(b"world", &v);
1025 }
1026
1027 #[tokio::test(flavor = "multi_thread")]
1028 async fn store_cyphertext() {
1029 let (_base_dir, pool) = setup().await;
1030 let mut tx = pool.begin_write().await.unwrap();
1031
1032 let key = cipher::SecretKey::random();
1033
1034 set_secret_blob(&mut tx, b"hello", b"world", &key)
1035 .await
1036 .unwrap();
1037
1038 let v: [u8; 5] = get_secret_blob(&mut tx, b"hello", &key)
1039 .await
1040 .unwrap()
1041 .unwrap();
1042
1043 assert_eq!(b"world", &v);
1044 }
1045
1046 #[tokio::test(flavor = "multi_thread")]
1049 async fn bad_key_is_not_error() {
1050 let (_base_dir, pool) = setup().await;
1051 let mut tx = pool.begin_write().await.unwrap();
1052
1053 let good_key = cipher::SecretKey::random();
1054 let bad_key = cipher::SecretKey::random();
1055
1056 set_secret_blob(&mut tx, b"hello", b"world", &good_key)
1057 .await
1058 .unwrap();
1059
1060 let v: [u8; 5] = get_secret_blob(&mut tx, b"hello", &bad_key)
1061 .await
1062 .unwrap()
1063 .unwrap();
1064
1065 assert_ne!(b"world", &v);
1066 }
1067
1068 #[tokio::test(flavor = "multi_thread")]
1069 async fn store_restore() {
1070 let accesses = [
1071 Access::Blind {
1072 id: RepositoryId::random(),
1073 },
1074 Access::ReadUnlocked {
1075 id: RepositoryId::random(),
1076 read_key: cipher::SecretKey::random(),
1077 },
1078 Access::ReadLocked {
1079 id: RepositoryId::random(),
1080 local_secret: SetLocalSecret::random(),
1081 read_key: cipher::SecretKey::random(),
1082 },
1083 Access::WriteUnlocked {
1084 secrets: WriteSecrets::random(),
1085 },
1086 Access::WriteLocked {
1087 local_read_secret: SetLocalSecret::random(),
1088 local_write_secret: SetLocalSecret::random(),
1089 secrets: WriteSecrets::random(),
1090 },
1091 Access::WriteLockedReadUnlocked {
1092 local_write_secret: SetLocalSecret::random(),
1093 secrets: WriteSecrets::random(),
1094 },
1095 ];
1096
1097 for access in accesses {
1098 let (_base_dir, pool) = db::create_temp().await.unwrap();
1099
1100 let mut tx = pool.begin_write().await.unwrap();
1101 let local_keys = initialize_access_secrets(&mut tx, &access).await.unwrap();
1102 tx.commit().await.unwrap();
1103
1104 let local_key = local_keys
1105 .write
1106 .as_deref()
1107 .or(local_keys.read.as_deref())
1108 .cloned();
1109
1110 let mut tx = pool.begin_write().await.unwrap();
1111
1112 let local_secret = local_key.clone().map(LocalSecret::SecretKey);
1113
1114 let access_secrets = get_access_secrets(&mut tx, local_secret.as_ref())
1115 .await
1116 .unwrap();
1117
1118 assert_eq!(
1119 (access.secrets(), local_key.as_ref()),
1120 (access_secrets.0, access_secrets.1.as_deref())
1121 );
1122 }
1123 }
1124}