Commit Graph

73 Commits

Author SHA1 Message Date
Ivan Enderlin
1f98159213 chore(sqlite): Simplify Vec capacity calculation.
Co-authored-by: Jonas Platte <jplatte+git@posteo.de>
2023-09-18 16:20:05 +02:00
Ivan Enderlin
4c244c6d83 feat(sqlite) chunk_large_query_over doesn't chunk if not necessary.
This patch updates `chunk_large_query_over`. This function works great
when it's required to chunk some data over a query. However, if the
data don't need to be chunked, it is possible to get a quicker path that
doesn't involve 2 `Vec` allocations, nor a `split_off` of one `Vec`, nor
a `Vec::extend` (which move data in memory). The quicker path, which is
also the most likely to be hit, simply does a single `Vec` allocation,
and that's it.
2023-09-18 15:13:45 +02:00
Benjamin Bouvier
ab7ec1bc38 feat: implement CryptoStore::remove_custom_value
As it's going to be used for the OIDC PR, so as to remove a remembered hash of session tokens.
2023-09-07 11:41:22 +02:00
Jonas Platte
5630851062 Upgrade Ruma 2023-08-30 15:22:00 +02:00
Benjamin Bouvier
dec5c6bc2d feat(sliding sync): save the to-device token in OlmMachine::receive_sync_changes 2023-07-31 12:24:55 +02:00
Benjamin Bouvier
719683d284 feat: allow storing a next_batch_token in the crypto stores 2023-07-31 12:24:55 +02:00
Richard van der Hoff
c6dab4088e Rename "Recovery Key" to "Backup decryption key" (#2305)
... because there are too many "recovery keys"
2023-07-20 09:49:54 +01:00
Damir Jelić
6ea0d886f7 Introduce a secret inbox
Up until now, users had to listen for to-device events to check for
secrets that were received as an `m.secret.send` event.

This has a bunch of shortcomings:
    1. Once the has been given to the consumer, it's gone and can't be
       retrieved anymore. Secrets may get lost if an app restart happens
       before the consumer decides what to do with it.
    2. The consumer can't be sure if the event was received in a
       secure manner.

This commit ads a inbox for our received secrets where we will long-term
store all secrets we receive until the user decides to delete them.

It's deemed fine to store all secrets, since we only accept secrets we
have requested and if they have been received from a verified device of
ours.
2023-07-19 14:27:18 +02:00
Ivan Enderlin
a70e79420c fix(sqlite): Ensure there is free space for static parameters, in chunk_large_query_over. 2023-07-06 16:46:35 +02:00
Ivan Enderlin
4e20dc5047 chore(sqlite): To make a friend smile. 2023-07-06 16:27:32 +02:00
Ivan Enderlin
23dae0612d chore(sqlite): Use itertools in repeat_vars. 2023-07-06 16:22:37 +02:00
Ivan Enderlin
d33e2aff2e doc(sqlite): Fix a typo. 2023-07-06 15:18:41 +02:00
Ivan Enderlin
380bce0604 fix(sqlite): Prevent reaching the maximum number of parameters in an SQL statement.
This patch introduces a new `chunk_large_query_over` function. Imagine
there is a _dynamic_ query that runs potentially large number of
parameters, so much that the maximum of parameters can be hit. Then,
this function is for you. It will execute the query on chunks of
parameters.

`chunk_large_query_over` uses `Vec::split_off` to avoid cloning `Key`s
as much as possible. It's difficult to use references here because of
the async nature of `SqliteObjectExt::prepare`.

This patch updates `get_kv_blobs`, `get_room_infos`,
`get_maybe_stripped_state_events_for_keys`, `get_profiles`,
`get_user_ids`, and `get_display_names` to use this new function.

This patch finally adds the `repeat_vars` function to replace in a
more efficient way the `vec!["?"; n].join(", ")` pattern. There is less
memory allocations.
2023-07-06 15:11:42 +02:00
Benjamin Bouvier
bb34f69662 chore: get rid of previous locking crypto-store methods 2023-07-04 17:19:01 +02:00
Benjamin Bouvier
66e1b94d31 test: add test for the generational counter invalidation 2023-06-30 12:41:04 +02:00
Benjamin Bouvier
73ad51879a feat: implement time lease based locks for the CryptoStore (#2140)
This implements a new time lease based lock for the `CryptoStore`, that doesn't require explicit unlocking, so that's more robust in the context of #1928, where any process may die because the device is running out of battery, or unexpected flows cause a lock to not be released properly in one or the other process.

```
//! This is a per-process lock that may be used only for very specific use
//! cases, where multiple processes might concurrently write to the same
//! database at the same time; this would invalidate crypto store caches, so
//! that should be done mindfully. Such a lock can be acquired multiple times by
//! the same process, and it remains active as long as there's at least one user
//! in a given process.
//!
//! The lock is implemented using time-based leases to values inserted in a
//! crypto store. The store maintains the lock identifier (key), who's the
//! current holder (value), and an expiration timestamp on the side; see also
//! `CryptoStore::try_take_leased_lock` for more details.
//!
//! The lock is initially acquired for a certain period of time (namely, the
//! duration of a lease, aka `LEASE_DURATION_MS`), and then a "heartbeat" task
//! renews the lease to extend its duration, every so often (namely, every
//! `EXTEND_LEASE_EVERY_MS`). Since the tokio scheduler might be busy, the
//! extension request should happen way more frequently than the duration of a
//! lease, in case a deadline is missed. The current values have been chosen to
//! reflect that, with a ratio of 1:10 as of 2023-06-23.
//!
//! Releasing the lock happens naturally, by not renewing a lease. It happens
//! automatically after the duration of the last lease, at most.
```

---

* feat: implement a time lease based lock for the crypto store
* feat: switch the crypto-store lock a time-leased based one
* chore: fix CI, don't use unixepoch in sqlite and do time math in rust
* chore: dummy implementation in indexeddb, don't run lease locks tests there
* feat: in NSE, wait the duration of a lease if first attempt to unlock failed
* feat: immediately release the lock when there are no more holders
* chore: clippy
* chore: add comment about atomic sanity
* chore: increase sleeps in timeline queue tests?
* feat: lower lease and renew durations
* feat: keep track of the extend-lease task
* fix: increment num_holders when acquiring the lock for the first time
* chore: reduce indent + abort prev renew task on non-wasm + add logs
2023-06-29 13:05:44 +00:00
Benjamin Bouvier
76ed3511b5 Add a value-based lock in the CryptoStores (#2049)
This implements a value-based lock in the crypto stores. The intent is to use that for multiple processes to be able to make writes into the store concurrently, while still cooperating on who does them. In particular, we need this for #1928, since we may have up to two different processes trying to write into the crypto store at the same time.

## New methods in the `CryptoStore` trait

The idea is to introduce two new methods touching **custom values** in the crypto store:

- one to atomically insert a value, only if it was missing (so, not following the semantics of `upsert` used in the `set_custom_value`) 
- one to atomically remove a custom value

Those two operations match the semantics we want:

- take the lock only if it ain't taken already == insert an entry only if it was missing
- release the lock = remove the entry

By looking at the number of lines affected by the query, we can infer whether the insert/remove happened or not, that is, if we managed to take the lock or not.

## High-level APIs

I've also added an high-level API, `CryptoStoreLock`, that helps managing such a lock, and adds some niceties on top of that:

- exponential backoff to retry attempts at acquiring the lock, when it was already taken
- attempt to gracefully recover when the lock has been taken by an app that's been killed by the environment
- full configuration of the key / value / backoff parameters

While it'd be nice to have something like a `CryptoStoreLockGuard`, it's hard to implement without being racy, because of the `async` statements that would happen in the `Drop` method (and async drop isn't stable yet).

## Test program

There's also a test program in which I shamelessly show my rudimentary unix skills; I've put it in the `labs/` directory but this could as well be a large integration test. A parent program initially fills a custom crypto store, then creates a `pipe()` for 1-way communication with a child created with `fork()`; then the parent sends commands to the child. These commands consist in reading and writing into the crypto store, using a lock. And while the child attempts to perform these operations, the parent tries hard to get the lock at the same time. This helps figuring out a few issues and making sure that cross-process locking would work as intended.
2023-06-16 13:05:54 +00:00
Kévin Commaille
16a63d352c base: Remove the session field from StateChanges
It is never set nor used.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-06-15 11:12:40 +02:00
Kévin Commaille
e32e9b5a22 base: Add state store method to fetch several display_names at once
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-06-14 13:51:52 +02:00
Kévin Commaille
7cba6d0849 base: Add state store method to fetch several presence events at once
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-06-14 13:51:52 +02:00
Kévin Commaille
b8abd1c022 base: Add state store method to fetch several profiles at once
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-06-14 13:51:52 +02:00
Kévin Commaille
f740195eb6 base: Add state store method to fetch several state events at once
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-06-14 13:51:52 +02:00
Kévin Commaille
ac8ea4786c base: Deprecate StateStore::get_stripped_room_infos
It is now unused.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-06-14 10:45:37 +02:00
Kévin Commaille
ea219d836e base: Do not separate stripped room info
Since stripped and non-stripped room infos use the same types,
the separation is not necessary anymore.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-06-14 10:45:37 +02:00
Kévin Commaille
ae2f834345 sqlite: Return an error when DB version is invalid or missing (#2038)
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-06-09 11:02:33 +00:00
Kévin Commaille
407375ad17 base: Move StateStore::get_member_event to StateStoreExt
It is a wrapper around get_state_event_static_for_key

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-06-06 11:56:11 +02:00
Kévin Commaille
83e7afab5d sdk: Allow to get stripped state events from the store
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-06-01 11:13:24 +02:00
Kévin Commaille
ea826a257d sdk: Replace Sled with SQLite as defaut store
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-05-08 12:11:10 +02:00
Kévin Commaille
09e446b1d5 sqlite: Fix doc error
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-05-08 12:11:10 +02:00
Jonas Platte
88580d95bf Consistently use Ruma's Owned*Id types
… for simplicity; instead of `Arc<*Id>`.
2023-05-05 12:34:15 +02:00
Jonas Platte
37be24ee19 sqlite: Fix compiler warnings 2023-05-03 11:42:50 +02:00
Benjamin Bouvier
be41dcf300 Remove unused dependencies 2023-05-02 15:06:46 +02:00
Kévin Commaille
6ca6a9a84a Implement SQLite state store
Co-authored-by: Jonas Platte <jplatte@matrix.org>
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-04-26 17:51:11 +02:00
Kévin Commaille
91da155b55 sqlite: Change signatures of SqliteObjectExt methods
Allows to pass owned strings as well as static strings

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-04-26 17:51:11 +02:00
Jonas Platte
40272c5989 refactor: Move sqlite crypto store migrations in new subdirectory 2023-04-26 17:51:11 +02:00
Damir Jelić
cd865d21c3 Drop outbound group sessions in the SQLite store
The format of the outbound group session struct has changed. We nowadays correctly rotate the group session if we can't restore it, but it's still good to avoid logging the error in this case.
2023-04-13 14:48:14 +00:00
Damir Jelić
316b29c95f Merge branch 'main' into valere/msc_2399 2023-04-13 10:59:05 +02:00
Damir Jelić
c73aeef2ed Fix the serialization of outbound group sessions in the SQLite store
The SQLite crypto store uses rmp_serde to serialize all the data we're
going to store. This works nicely for most things, one exception to this
is the OutboundGroupSession type.

The OutboundGroupSession type stores to-device requests to ensure that
the session doesn't get used before it is shared with the whole group
and to ensure that the to-device requests get restored if the
session gets restored after an application restart.

The to-device requests type critically contain `Raw<AnyToDeviceEvent>`,
the `Raw` type here being the serde_json::Raw type. rmp_serde seems to
serialize this just fine, but later deserialization fails.

We're avoiding the issue by using serde_json to serialize the
OutboundGroupSession.
2023-04-11 10:08:06 +02:00
Damir Jelić
0f8da0b723 Test that to-device requests in the group session can get deserialized 2023-04-11 10:08:06 +02:00
Damir Jelić
6fd129dcfa Remove some commented out code and fix some formatting 2023-04-05 13:22:41 +02:00
Damir Jelić
50d477b91c More clippy fixes 2023-04-05 11:54:29 +02:00
Damir Jelić
81e2725b6f Remove DirectWithheldCode 2023-04-04 13:30:29 +02:00
Jonas Platte
7dd086fcdc Make sqlite bundling optional 2023-03-30 17:59:51 +02:00
Jonas Platte
d680b331d0 Make tokio a workspace dependency 2023-03-28 21:08:57 +02:00
Jonas Platte
cd33d8ca38 Always use RwLock and Mutex from tokio
… instead of async-lock, which we previously used on wasm.
2023-03-28 21:08:57 +02:00
Damir Jelić
61ea15eb39 Merge branch 'main' into valere/msc_2399 2023-03-15 19:31:04 +01:00
Andy Uhnak
283137f190 Introduce RoomSettings 2023-03-14 10:27:11 +00:00
Andy Uhnak
7e7c91699f Generic key-value API 2023-03-14 10:27:11 +00:00
Andy Uhnak
b4b111f91f Store encryption settings 2023-03-14 10:27:11 +00:00
valere
9733d9842a Merge branch 'main' into valere/msc_2399 2023-03-09 17:04:21 +01:00