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